summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/os.nim28
1 files changed, 25 insertions, 3 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index bd5e83432..688f9a88f 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -836,9 +836,11 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
   ##
   ## If this fails, `EOS` is raised. On the Windows platform this proc will
   ## copy the source file's attributes into dest. On other platforms you need
-  ## to use getFilePermissions and setFilePermissions to copy them by hand,
-  ## otherwise `dest` will inherit the default permissions of a newly created
-  ## file for the user.
+  ## to use getFilePermissions and setFilePermissions to copy them by hand (or
+  ## use the convenience copyFileWithPermissions() proc), otherwise `dest` will
+  ## inherit the default permissions of a newly created file for the user. If
+  ## `dest` already exists, the file attributes will be preserved and the
+  ## content overwritten.
   when defined(Windows):
     when useWinUnicode:
       let s = newWideCString(source)
@@ -1383,6 +1385,26 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
       var res2 = SetFileAttributesA(filename, res)
     if res2 == - 1'i32: OSError(OSLastError())
   
+proc copyFileWithPermissions*(source, dest: string,
+                              ignorePermissionErrors = true) =
+  ## Copies a file from `source` to `dest` preserving file permissions.
+  ##
+  ## This is a wrapper proc around copyFile, getFilePermissions and
+  ## setFilePermissions on non Windows platform. On windows this proc is just a
+  ## wrapper for copyFile since that proc already copies attributes.
+  ##
+  ## On non windows systems permissions are copied after the file itself has
+  ## been copied, which won't happen atomically and could lead to a race
+  ## condition. If ignorePermissionErrors is true, errors while reading/setting
+  ## file attributes will be ignored, otherwise will raise `OSError`.
+  copyFile(source, dest)
+  when not defined(Windows):
+    try:
+      setFilePermissions(dest, getFilePermissions(source))
+    except:
+      if not ignorePermissionErrors:
+        raise
+
 proc inclFilePermissions*(filename: string, 
                           permissions: set[TFilePermission]) {.
   rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =