]> arthur.barton.de Git - netatalk.git/blobdiff - bin/ad/ad_mv.c
Posix/SUS portability cleanup
[netatalk.git] / bin / ad / ad_mv.c
index f9bc64d3986a58293188f7c4715b2a0634c1ff04..4d23183ae13865c3c59a8298cde36e5cedbfeb91 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <libgen.h>
 
 #include <atalk/ftw.h>
 #include <atalk/adouble.h>
@@ -50,7 +51,6 @@ static int fflg, iflg, nflg, vflg;
 static afpvol_t svolume, dvolume;
 static cnid_t did, pdid;
 static volatile sig_atomic_t alarmed;
-static char *adexecp;
 static char           *netatalk_dirs[] = {
     ".AppleDouble",
     ".AppleDB",
@@ -60,7 +60,6 @@ static char           *netatalk_dirs[] = {
 
 static int copy(const char *, const char *);
 static int do_move(const char *, const char *);
-static int fastcopy(const char *, const char *, struct stat *);
 static void preserve_fd_acls(int source_fd, int dest_fd, const char *source_path,
                              const char *dest_path);
 /*
@@ -120,7 +119,23 @@ static void usage_mv(void)
 {
     printf(
         "Usage: ad mv [-f | -i | -n] [-v] source target\n"
-        "       ad mv [-f | -i | -n] [-v] source ... directory\n"
+        "       ad mv [-f | -i | -n] [-v] source ... directory\n\n"
+        "Move files around within an AFP volume, updating the CNID\n"
+        "database as needed. If either:\n"
+        " - source or destination is not an AFP volume\n"
+        " - source volume != destinatio volume\n"
+        "the files are copied and removed from the source.\n\n"
+        "The following options are available:\n\n"
+        "   -f   Do not prompt for confirmation before overwriting the destination\n"
+        "        path.  (The -f option overrides any previous -i or -n options.)\n"
+        "   -i   Cause mv to write a prompt to standard error before moving a file\n"
+        "        that would overwrite an existing file.  If the response from the\n"
+        "        standard input begins with the character `y' or `Y', the move is\n"
+        "        attempted.  (The -i option overrides any previous -f or -n\n"
+        "        options.)\n"
+        "   -n   Do not overwrite an existing file.  (The -n option overrides any\n"
+        "        previous -f or -i options.)\n"
+        "   -v   Cause mv to be verbose, showing files after they are moved.\n"
         );
     exit(EXIT_FAILURE);
 }
@@ -138,7 +153,6 @@ int ad_mv(int argc, char *argv[])
     pdid = htonl(1);
     did = htonl(2);
 
-    adexecp = argv[0];
     argc--;
     argv++;
 
@@ -172,7 +186,7 @@ int ad_mv(int argc, char *argv[])
     set_signal();
     cnid_init();
     if (openvol(argv[argc - 1], &dvolume) != 0) {
-        SLOG("Error opening CNID database for %s: ", argv[argc - 1]);
+        SLOG("Error opening CNID database for source \"%s\": ", argv[argc - 1]);
         return 1;
     }
 
@@ -184,12 +198,13 @@ int ad_mv(int argc, char *argv[])
         if (argc > 2)
             usage_mv();
         if (openvol(argv[0], &svolume) != 0) {
-            SLOG("Error opening CNID database for %s: ", argv[0]);
+            SLOG("Error opening CNID database for destination \"%s\": ", argv[0]);
             return 1;
         }
         rval = do_move(argv[0], argv[1]);
         closevol(&svolume);
         closevol(&dvolume);
+        return 1;
     }
 
     /* It's a directory, move each file into it. */
@@ -220,11 +235,8 @@ int ad_mv(int argc, char *argv[])
             rval = 1;
         } else {
             memmove(endp, p, (size_t)len + 1);
-            if (openvol(*argv, &svolume) != 0) {
-                SLOG("Error opening CNID database for %s: ", argv[0]);
-                rval = 1;
-                goto exit;
-            }
+            openvol(*argv, &svolume);
+
             if (do_move(*argv, path))
                 rval = 1;
             closevol(&svolume);
@@ -277,130 +289,109 @@ static int do_move(const char *from, const char *to)
         }
     }
 
-    if (!rename(from, to)) {
-        if (vflg)
-            printf("%s -> %s\n", from, to);
-        return (0);
-    }
-
-    if (errno == EXDEV) {
-        char path[MAXPATHLEN];
+    int mustcopy = 0;
+    /* 
+     * Besides the usual EXDEV we copy instead of moving if
+     * 1) source AFP volume != dest AFP volume
+     * 2) either source or dest isn't even an AFP volume
+     */
+    if (!svolume.volinfo.v_path
+        || !dvolume.volinfo.v_path
+        || strcmp(svolume.volinfo.v_path, dvolume.volinfo.v_path) != 0)
+        mustcopy = 1;
+    
+    cnid_t cnid = 0;
+    if (!mustcopy) {
+        if ((cnid = cnid_for_path(&svolume, from, &did)) == CNID_INVALID) {
+            SLOG("Couldn't resolve CNID for %s", from);
+            return -1;
+        }
 
-        /*
-         * If the source is a symbolic link and is on another
-         * filesystem, it can be recreated at the destination.
-         */
-        if (lstat(from, &sb) == -1) {
-            SLOG("%s: %s", from, strerror(errno));
-            return (1);
+        if (stat(from, &sb) != 0) {
+            SLOG("Cant stat %s: %s", to, strerror(errno));
+            return -1;
         }
-        if (!S_ISLNK(sb.st_mode)) {
-            /* Can't mv(1) a mount point. */
-            if (realpath(from, path) == NULL) {
-                SLOG("cannot resolve %s: %s: %s", from, path, strerror(errno));
+
+        if (rename(from, to) != 0) {
+            if (errno == EXDEV) {
+                mustcopy = 1;
+                char path[MAXPATHLEN];
+
+                /*
+                 * If the source is a symbolic link and is on another
+                 * filesystem, it can be recreated at the destination.
+                 */
+                if (lstat(from, &sb) == -1) {
+                    SLOG("%s: %s", from, strerror(errno));
+                    return (-1);
+                }
+                if (!S_ISLNK(sb.st_mode)) {
+                    /* Can't mv(1) a mount point. */
+                    if (realpath(from, path) == NULL) {
+                        SLOG("cannot resolve %s: %s: %s", from, path, strerror(errno));
+                        return (1);
+                    }
+                }
+            } else { /* != EXDEV */
+                SLOG("rename %s to %s: %s", from, to, strerror(errno));
                 return (1);
             }
+        } /* rename != 0*/
+        
+        switch (sb.st_mode & S_IFMT) {
+        case S_IFREG:
+            if (dvolume.volume.vfs->vfs_renamefile(&dvolume.volume, -1, from, to) != 0) {
+                SLOG("Error moving adouble file for %s", from);
+                return -1;
+            }
+            break;
+        case S_IFDIR:
+            break;
+        default:
+            SLOG("Not a file or dir: %s", from);
+            return -1;
         }
-    } else {
-        SLOG("rename %s to %s: %s", from, to, strerror(errno));
-        return (1);
-    }
-
-    /*
-     * If rename fails because we're trying to cross devices, and
-     * it's a regular file, do the copy internally; otherwise, use
-     * cp and rm.
-     */
-    if (lstat(from, &sb)) {
-        SLOG("%s: %s", from, strerror(errno));
-        return (1);
-    }
-    return (S_ISREG(sb.st_mode) ?
-            fastcopy(from, to, &sb) : copy(from, to));
-}
-
-static int fastcopy(const char *from, const char *to, struct stat *sbp)
-{
-    struct timeval tval[2];
-    static u_int blen;
-    static char *bp;
-    mode_t oldmode;
-    int nread, from_fd, to_fd;
-
-    if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
-        SLOG("%s: %s", from, strerror(errno));
-        return (1);
-    }
-    if (blen < sbp->st_blksize) {
-        if (bp != NULL)
-            free(bp);
-        if ((bp = malloc((size_t)sbp->st_blksize)) == NULL) {
-            blen = 0;
-            SLOG("malloc failed");
-            return (1);
+    
+        /* get CNID of new parent dir */
+        cnid_t newpdid, newdid;
+        if ((newdid = cnid_for_paths_parent(&dvolume, to, &newpdid)) == CNID_INVALID) {
+            SLOG("Couldn't resolve CNID for parent of %s", to);
+            return -1;
         }
-        blen = sbp->st_blksize;
-    }
-    while ((to_fd = open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) {
-        if (errno == EEXIST && unlink(to) == 0)
-            continue;
-        SLOG("%s: %s", to, strerror(errno));
-        (void)close(from_fd);
-        return (1);
-    }
-    while ((nread = read(from_fd, bp, (size_t)blen)) > 0)
-        if (write(to_fd, bp, (size_t)nread) != nread) {
-            SLOG("%s: %s", to, strerror(errno));
-            goto err;
+
+        if (stat(to, &sb) != 0) {
+            SLOG("Cant stat %s: %s", to, strerror(errno));
+            return 1;
         }
-    if (nread < 0) {
-        SLOG("%s: %s", from, strerror(errno));
-    err:
-        if (unlink(to))
-            SLOG("%s: remove", to, strerror(errno));
-        (void)close(from_fd);
-        (void)close(to_fd);
-        return (1);
-    }
 
-    oldmode = sbp->st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID);
-    if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) {
-        SLOG("%s: set owner/group (was: %lu/%lu)", to,
-             (u_long)sbp->st_uid, (u_long)sbp->st_gid, strerror(errno));
-        if (oldmode & S_ISUID) {
-            SLOG("%s: owner/group changed; clearing suid (mode was 0%03o)",
-                 to, oldmode);
-            sbp->st_mode &= ~S_ISUID;
+        char *p = strdup(to);
+        char *name = basename(p);
+        if (cnid_update(dvolume.volume.v_cdb, cnid, &sb, newdid, name, strlen(name)) != 0) {
+            SLOG("Cant update CNID for: %s", to);
+            return 1;
         }
-    }
-    if (fchmod(to_fd, sbp->st_mode))
-        SLOG("%s: set mode (was: 0%03o): %s", to, oldmode, strerror(errno));
-    /*
-     * POSIX 1003.2c states that if _POSIX_ACL_EXTENDED is in effect
-     * for dest_file, then its ACLs shall reflect the ACLs of the
-     * source_file.
-     */
-    preserve_fd_acls(from_fd, to_fd, from, to);
-    (void)close(from_fd);
+        free(p);
 
-    tval[0].tv_sec = sbp->st_atime;
-    tval[1].tv_sec = sbp->st_mtime;
-    tval[0].tv_usec = tval[1].tv_usec = 0;
-    if (utimes(to, tval))
-        SLOG("%s: set times: %s", to, strerror(errno));
+        struct adouble ad;
+        ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
+        if (ad_open_metadata(to, S_ISDIR(sb.st_mode) ? ADFLAGS_DIR : 0, O_RDWR, &ad) != 0) {
+            SLOG("Error opening adouble for: %s", to);
+            return 1;
+        }
+        ad_setid(&ad, sb.st_dev, sb.st_ino, cnid, newdid, dvolume.db_stamp);
+        ad_flush(&ad);
+        ad_close_metadata(&ad);
 
-    if (close(to_fd)) {
-        SLOG("%s: %s", to, strerror(errno));
-        return (1);
+        if (vflg)
+            printf("%s -> %s\n", from, to);
+        return (0);
     }
+    
+    if (mustcopy)
+        return copy(from, to);
 
-    if (unlink(from)) {
-        SLOG("%s: remove: %s", from, strerror(errno));
-        return (1);
-    }
-    if (vflg)
-        printf("%s -> %s\n", from, to);
-    return 0;
+    /* If we get here it's an error */
+    return -1;
 }
 
 static int copy(const char *from, const char *to)
@@ -428,45 +419,49 @@ static int copy(const char *from, const char *to)
 
     /* Copy source to destination. */
     if (!(pid = fork())) {
-        execl(adexecp, "cp", vflg ? "-Rpv" : "-Rp", "--", from, to, (char *)NULL);
+        execl(_PATH_AD, "ad", "cp", vflg ? "-Rpv" : "-Rp", from, to, (char *)NULL);
         _exit(1);
     }
-    if (waitpid(pid, &status, 0) == -1) {
-        SLOG("%s %s %s: waitpid: %s", adexecp, from, to, strerror(errno));
+    while ((waitpid(pid, &status, 0)) == -1) {
+        if (errno == EINTR)
+            continue;
+        SLOG("%s cp -R %s %s: waitpid: %s", _PATH_AD, from, to, strerror(errno));
         return (1);
     }
     if (!WIFEXITED(status)) {
-        SLOG("%s %s %s: did not terminate normally", adexecp, from, to);
+        SLOG("%s cp -R %s %s: did not terminate normally", _PATH_AD, from, to);
         return (1);
     }
     switch (WEXITSTATUS(status)) {
     case 0:
         break;
     default:
-        SLOG("%s %s %s: terminated with %d (non-zero) status",
-              adexecp, from, to, WEXITSTATUS(status));
+        SLOG("%s cp -R %s %s: terminated with %d (non-zero) status",
+             _PATH_AD, from, to, WEXITSTATUS(status));
         return (1);
     }
 
     /* Delete the source. */
     if (!(pid = fork())) {
-        execl(adexecp, "rm", "-Rf", "--", from, (char *)NULL);
+        execl(_PATH_AD, "ad", "rm", "-R", from, (char *)NULL);
         _exit(1);
     }
-    if (waitpid(pid, &status, 0) == -1) {
-        SLOG("%s %s: waitpid: %s", adexecp, from, strerror(errno));
+    while ((waitpid(pid, &status, 0)) == -1) {
+        if (errno == EINTR)
+            continue;
+        SLOG("%s rm -R %s: waitpid: %s", _PATH_AD, from, strerror(errno));
         return (1);
     }
     if (!WIFEXITED(status)) {
-        SLOG("%s %s: did not terminate normally", adexecp, from);
+        SLOG("%s rm -R %s: did not terminate normally", _PATH_AD, from);
         return (1);
     }
     switch (WEXITSTATUS(status)) {
     case 0:
         break;
     default:
-        SLOG("%s %s: terminated with %d (non-zero) status",
-              adexecp, from, WEXITSTATUS(status));
+        SLOG("%s rm -R %s: terminated with %d (non-zero) status",
+              _PATH_AD, from, WEXITSTATUS(status));
         return (1);
     }
     return 0;