]> ruderich.org/simon Gitweb - safcm/safcm.git/blobdiff - remote/sync/files.go
Update copyright years
[safcm/safcm.git] / remote / sync / files.go
index edf81bd2cc795a2630f4b775eca25786ad838c61..20f8505c0fff92a27f638575bda6a0c611ce2ad5 100644 (file)
@@ -1,6 +1,6 @@
 // MsgSyncReq: copy files to the remote host
 
-// Copyright (C) 2021  Simon Ruderich
+// Copyright (C) 2021-2023  Simon Ruderich
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -15,6 +15,7 @@
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+//go:build !windows
 // +build !windows
 
 package sync
@@ -249,7 +250,7 @@ reopen:
                }
 
                // Compare file content (if possible)
-               switch change.Old.Mode.Type() {
+               switch change.Old.Mode.Type() { //nolint:exhaustive
                case 0: // regular file
                        x, err := io.ReadAll(oldFh)
                        if err != nil {
@@ -324,7 +325,7 @@ reopen:
                err := unix.Unlinkat(parentFd, baseName, 0)
                if err != nil && !os.IsNotExist(err) {
                        err2 := unix.Unlinkat(parentFd, baseName,
-                               AT_REMOVEDIR)
+                               unix.AT_REMOVEDIR)
                        if err2 != nil && !os.IsNotExist(err2) {
                                // See src/os/file_unix.go in Go's sources
                                if err2 == unix.ENOTDIR {
@@ -383,6 +384,10 @@ reopen:
                        // the error when the user tries to access this
                        // directory (access for the group will fail though).
                        mode := change.Old.Mode & fs.ModePerm & 0700
+                       // Retain setgid/sticky so that the behavior does not
+                       // change when creating and removing files.
+                       mode |= change.Old.Mode & fs.ModeSetgid
+                       mode |= change.Old.Mode & fs.ModeSticky
                        debugf("chmodding %#o (temporary)", mode)
                        err := oldFh.Chmod(mode)
                        if err != nil {
@@ -441,7 +446,7 @@ reopen:
                err = unix.Fchownat(parentFd, tmpBase, file.Uid, file.Gid,
                        unix.AT_SYMLINK_NOFOLLOW)
                if err != nil {
-                       unix.Unlinkat(parentFd, tmpBase, 0)
+                       unix.Unlinkat(parentFd, tmpBase, 0) //nolint:errcheck
                        return err
                }
                // Permissions are irrelevant for symlinks (on most systems)
@@ -453,9 +458,17 @@ reopen:
        debugf("renaming %q", slashpath.Join(dir, tmpBase))
        err = unix.Renameat(parentFd, tmpBase, parentFd, baseName)
        if err != nil {
-               unix.Unlinkat(parentFd, tmpBase, 0)
+               unix.Unlinkat(parentFd, tmpBase, 0) //nolint:errcheck
                return err
        }
+       // To guarantee durability fsync must be called on a parent directory
+       // after adding, renaming or removing files therein.
+       //
+       // Calling sync on the files itself is not enough according to POSIX;
+       // man 2 fsync: "Calling fsync() does not necessarily ensure that the
+       // entry in the directory containing the file has also reached disk.
+       // For that an explicit fsync() on a file descriptor for the directory
+       // is also needed."
        err = unix.Fsync(parentFd)
        if err != nil {
                return err
@@ -540,9 +553,12 @@ func OpenParentDirectoryNoSymlinks(path string) (int, string, error) {
                }
                dir = ".."
                parts = []string{filepath.Base(wd)}
-       } else if parts[0] != "." {
+       } else {
                // Relative path: start at the current directory
                dir = "."
+               if parts[0] == "." {
+                       parts = parts[1:]
+               }
        }
 
        dirFd, err := unix.Openat(unix.AT_FDCWD, dir, openReadonlyFlags, 0)
@@ -643,31 +659,31 @@ func WriteTempAt(dirFd int, base string, data []byte, uid, gid int,
        _, err = fh.Write(data)
        if err != nil {
                fh.Close()
-               unix.Unlinkat(dirFd, tmpBase, 0)
+               unix.Unlinkat(dirFd, tmpBase, 0) //nolint:errcheck
                return "", err
        }
        // createTempAt() creates the file with 0600
        err = fh.Chown(uid, gid)
        if err != nil {
                fh.Close()
-               unix.Unlinkat(dirFd, tmpBase, 0)
+               unix.Unlinkat(dirFd, tmpBase, 0) //nolint:errcheck
                return "", err
        }
        err = fh.Chmod(mode)
        if err != nil {
                fh.Close()
-               unix.Unlinkat(dirFd, tmpBase, 0)
+               unix.Unlinkat(dirFd, tmpBase, 0) //nolint:errcheck
                return "", err
        }
        err = fh.Sync()
        if err != nil {
                fh.Close()
-               unix.Unlinkat(dirFd, tmpBase, 0)
+               unix.Unlinkat(dirFd, tmpBase, 0) //nolint:errcheck
                return "", err
        }
        err = fh.Close()
        if err != nil {
-               unix.Unlinkat(dirFd, tmpBase, 0)
+               unix.Unlinkat(dirFd, tmpBase, 0) //nolint:errcheck
                return "", err
        }
 
@@ -695,24 +711,3 @@ retry:
 
        return os.NewFile(uintptr(fd), ""), tmpBase, nil
 }
-
-// SyncPath syncs path, which should be a directory. To guarantee durability
-// it must be called on a parent directory after adding, renaming or removing
-// files therein.
-//
-// Calling sync on the files itself is not enough according to POSIX; man 2
-// fsync: "Calling fsync() does not necessarily ensure that the entry in the
-// directory containing the file has also reached disk. For that an explicit
-// fsync() on a file descriptor for the directory is also needed."
-func SyncPath(path string) error {
-       x, err := os.Open(path)
-       if err != nil {
-               return err
-       }
-       err = x.Sync()
-       closeErr := x.Close()
-       if err != nil {
-               return err
-       }
-       return closeErr
-}