]> granicus.if.org Git - shadow/commitdiff
2010-04-04 Nicolas François <nicolas.francois@centraliens.net>
authornekral-guest <nekral-guest@5a98b0ae-9ef6-0310-add3-de5d479b70d7>
Sun, 4 Apr 2010 20:55:46 +0000 (20:55 +0000)
committernekral-guest <nekral-guest@5a98b0ae-9ef6-0310-add3-de5d479b70d7>
Sun, 4 Apr 2010 20:55:46 +0000 (20:55 +0000)
* src/useradd.c: spool is a constant string.
* src/useradd.c: Set the new copy_tree's paramater 'copy_root' to false

2010-04-04  Nicolas François  <nicolas.francois@centraliens.net>

* src/usermod.c: move_home() is only called if mflg is set.
* src/usermod.c: Fail is -m is provided but the old home directory
is not a directory.
* src/usermod.c: Use the previous improvement of copy_tree to
provide better error diagnosis.
* src/usermod.c: When rename() is used, also change the ownership.
* src/usermod.c: Do not change the ownership of the root directory
twice.
* src/usermod.c: When -u is provided, only change the ownership of
the home directory if it is a directory.
* src/usermod.c: Also change ownerships when -g is used.

2010-04-04  Nicolas François  <nicolas.francois@centraliens.net>

* lib/prototypes.h, libmisc/copydir.c: Add the old UID and GID to
copy_tree to detect when ownership shall be changed.
* libmisc/copydir.c: Document the behavior when the IDs are set to
-1.
* lib/prototypes.h, libmisc/copydir.c (copy_tree): Add parameter
copy_root.
* libmisc/copydir.c: error() and ctx can be static.
* libmisc/copydir.c (copy_hardlink): Remove parameter src.

2010-04-04  Nicolas François  <nicolas.francois@centraliens.net>

* libmisc/chowndir.c: Dynamically allocate memory to support
path longer than 1024 characters.
* libmisc/chowndir.c: Fix typos in documentation.
* libmisc/chowndir.c: Support and document the behavior when a old
or new ID is set to -1.
* libmisc/chowndir.c: Improved error detection when chown fails.
* libmisc/chowndir.c: Harmonize error handling strategy when an
error occurs: stop changing ownership as soon as an error was
detected.

ChangeLog
lib/prototypes.h
libmisc/chowndir.c
libmisc/copydir.c
src/useradd.c
src/usermod.c

index 3809c7ea7656f338d51b060b537203c3b2ab9ee6..08f132292b22519b74f0c27d2350a02a73597f1b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+2010-04-04  Nicolas François  <nicolas.francois@centraliens.net>
+
+       * src/useradd.c: spool is a constant string.
+       * src/useradd.c: Set the new copy_tree's paramater 'copy_root' to false 
+
+2010-04-04  Nicolas François  <nicolas.francois@centraliens.net>
+
+       * src/usermod.c: move_home() is only called if mflg is set.
+       * src/usermod.c: Fail is -m is provided but the old home directory
+       is not a directory.
+       * src/usermod.c: Use the previous improvement of copy_tree to
+       provide better error diagnosis.
+       * src/usermod.c: When rename() is used, also change the ownership.
+       * src/usermod.c: Do not change the ownership of the root directory
+       twice.
+       * src/usermod.c: When -u is provided, only change the ownership of
+       the home directory if it is a directory.
+       * src/usermod.c: Also change ownerships when -g is used.
+
+2010-04-04  Nicolas François  <nicolas.francois@centraliens.net>
+
+       * lib/prototypes.h, libmisc/copydir.c: Add the old UID and GID to
+       copy_tree to detect when ownership shall be changed.
+       * libmisc/copydir.c: Document the behavior when the IDs are set to
+       -1.
+       * lib/prototypes.h, libmisc/copydir.c (copy_tree): Add parameter
+       copy_root.
+       * libmisc/copydir.c: error() and ctx can be static.
+       * libmisc/copydir.c (copy_hardlink): Remove parameter src.
+
+2010-04-04  Nicolas François  <nicolas.francois@centraliens.net>
+
+       * libmisc/chowndir.c: Dynamically allocate memory to support
+       path longer than 1024 characters.
+       * libmisc/chowndir.c: Fix typos in documentation.
+       * libmisc/chowndir.c: Support and document the behavior when a old
+       or new ID is set to -1.
+       * libmisc/chowndir.c: Improved error detection when chown fails.
+       * libmisc/chowndir.c: Harmonize error handling strategy when an
+       error occurs: stop changing ownership as soon as an error was
+       detected.
+
 2010-04-03  Nicolas François  <nicolas.francois@centraliens.net>
 
        * man/hu/passwd.5: Fix formatting typo.
index b9bbd0b2d2993919c0df13684fee9706b55316ed..b8e19ec319bf475e3e7c9b90ec0439e61126086c 100644 (file)
@@ -75,7 +75,9 @@ extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *);
 extern char *Basename (char *str);
 
 /* chowndir.c */
-extern int chown_tree (const char *, uid_t, uid_t, gid_t, gid_t);
+extern int chown_tree (const char *root,
+                       uid_t old_uid, uid_t new_uid,
+                       gid_t old_gid, gid_t new_gid);
 
 /* chowntty.c */
 extern void chown_tty (const struct passwd *);
@@ -116,8 +118,9 @@ extern bool console (const char *);
 
 /* copydir.c */
 extern int copy_tree (const char *src_root, const char *dst_root,
-                      long int uid, long int gid);
-
+                      bool copy_root,
+                      uid_t old_uid, uid_t new_uid,
+                      gid_t old_gid, gid_t new_gid);
 #ifdef WITH_SELINUX
 extern int selinux_file_context (const char *dst_name);
 #endif
index b1239613871782d38ef6e10d5c7c46f304e8a479..5179c6bffce85a1daad80466d8bdc19401a60dca 100644 (file)
  *
  *     chown_dir() walks a directory tree and changes the ownership
  *     of all files owned by the provided user ID.
+ *
+ *     Only files owned (resp. group-owned) by old_uid (resp. by old_gid)
+ *     will have their ownership (resp. group-ownership) modified, unless
+ *     old_uid (resp. old_gid) is set to -1.
+ *
+ *     new_uid and new_gid can be set to -1 to indicate that no owner or
+ *     group-owner shall be changed.
  */
-int
-chown_tree (const char *root,
-            uid_t old_uid,
-            uid_t new_uid,
-            gid_t old_gid,
-            gid_t new_gid)
+int chown_tree (const char *root,
+                uid_t old_uid,
+                uid_t new_uid,
+                gid_t old_gid,
+                gid_t new_gid)
 {
-       char new_name[1024];
+       char *new_name;
+       size_t new_name_len;
        int rc = 0;
        struct DIRECT *ent;
        struct stat sb;
        DIR *dir;
 
+       new_name = malloc (1024);
+       if (NULL == new_name) {
+               return -1;
+       }
+       new_name_len = 1024;
+
        /*
         * Make certain the directory exists.  This routine is called
-        * directory by the invoker, or recursively.
+        * directly by the invoker, or recursively.
         */
 
        if (access (root, F_OK) != 0) {
@@ -71,8 +84,8 @@ chown_tree (const char *root,
        /*
         * Open the directory and read each entry.  Every entry is tested
         * to see if it is a directory, and if so this routine is called
-        * recursively.  If not, it is checked to see if it is owned by
-        * old user ID.
+        * recursively.  If not, it is checked to see if an ownership
+        * shall be changed.
         */
 
        dir = opendir (root);
@@ -81,6 +94,8 @@ chown_tree (const char *root,
        }
 
        while ((ent = readdir (dir))) {
+               uid_t tmpuid = (uid_t) -1;
+               gid_t tmpgid = (gid_t) -1;
 
                /*
                 * Skip the "." and ".." entries
@@ -96,12 +111,16 @@ chown_tree (const char *root,
                 * destination files.
                 */
 
-               if (strlen (root) + strlen (ent->d_name) + 2 > sizeof new_name) {
-                       break;
+               if (strlen (root) + strlen (ent->d_name) + 2 > new_name_len) {
+                       new_name = realloc (new_name, new_name_len + 1024);
+                       if (NULL == new_name) {
+                               rc = -1;
+                               break;
+                       }
+                       new_name_len += 1024;
                }
 
-               snprintf (new_name, sizeof new_name, "%s/%s", root,
-                         ent->d_name);
+               snprintf (new_name, new_name_len, "%s/%s", root, ent->d_name);
 
                /* Don't follow symbolic links! */
                if (LSTAT (new_name, &sb) == -1) {
@@ -126,23 +145,52 @@ chown_tree (const char *root,
                        continue;
                }
 #endif
-               if (sb.st_uid == old_uid) {
-                       LCHOWN (new_name, new_uid,
-                               (sb.st_gid == old_gid) ? new_gid : sb.st_gid);
+               /*
+                * By default, the IDs are not changed (-1).
+                *
+                * If the file is not owned by the user, the owner is not
+                * changed.
+                *
+                * If the file is not group-owned by the group, the
+                * group-owner is not changed.
+                */
+               if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) {
+                       tmpuid = new_uid;
+               }
+               if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) {
+                       tmpgid = new_gid;
+               }
+               if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) {
+                       rc = LCHOWN (new_name, tmpuid, tmpgid);
+                       if (0 != rc) {
+                               break;
+                       }
                }
        }
+
+       free (new_name);
        (void) closedir (dir);
 
        /*
         * Now do the root of the tree
         */
 
-       if (stat (root, &sb) == 0) {
-               if (sb.st_uid == old_uid) {
-                       LCHOWN (root, new_uid,
-                               sb.st_gid == old_gid ? new_gid : sb.st_gid);
+       if ((0 == rc) && (stat (root, &sb) == 0)) {
+               uid_t tmpuid = (uid_t) -1;
+               gid_t tmpgid = (gid_t) -1;
+               if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) {
+                       tmpuid = new_uid;
+               }
+               if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) {
+                       tmpgid = new_gid;
                }
+               if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) {
+                       rc = LCHOWN (root, tmpuid, tmpgid);
+               }
+       } else {
+               rc = -1;
        }
+
        return rc;
 }
 
index 6d4f83977780f67f3c785fb53982e880a7b03592..ba6b91c7966e6bf5edd100cbf1b0081adb56c14a 100644 (file)
@@ -68,24 +68,38 @@ struct link_name {
 static /*@exposed@*/struct link_name *links;
 
 static int copy_entry (const char *src, const char *dst,
-                       long int uid, long int gid);
+                       uid_t old_uid, uid_t new_uid,
+                       gid_t old_gid, gid_t new_gid);
 static int copy_dir (const char *src, const char *dst,
                      const struct stat *statp, const struct timeval mt[],
-                     long int uid, long int gid);
+                     uid_t old_uid, uid_t new_uid,
+                     gid_t old_gid, gid_t new_gid);
 #ifdef S_IFLNK
 static char *readlink_malloc (const char *filename);
 static int copy_symlink (const char *src, const char *dst,
                          const struct stat *statp, const struct timeval mt[],
-                         long int uid, long int gid);
+                         uid_t old_uid, uid_t new_uid,
+                         gid_t old_gid, gid_t new_gid);
 #endif                         /* S_IFLNK */
-static int copy_hardlink (const char *src, const char *dst,
+static int copy_hardlink (const char *dst,
                           struct link_name *lp);
 static int copy_special (const char *src, const char *dst,
                          const struct stat *statp, const struct timeval mt[],
-                         long int uid, long int gid);
+                         uid_t old_uid, uid_t new_uid,
+                         gid_t old_gid, gid_t new_gid);
 static int copy_file (const char *src, const char *dst,
                       const struct stat *statp, const struct timeval mt[],
-                      long int uid, long int gid);
+                      uid_t old_uid, uid_t new_uid,
+                      gid_t old_gid, gid_t new_gid);
+static int chown_if_needed (const char *dst, const struct stat *statp,
+                            uid_t old_uid, uid_t new_uid,
+                            gid_t old_gid, gid_t new_gid);
+static int lchown_if_needed (const char *dst, const struct stat *statp,
+                             uid_t old_uid, uid_t new_uid,
+                             gid_t old_gid, gid_t new_gid);
+static int fchown_if_needed (int fdst, const struct stat *statp,
+                             uid_t old_uid, uid_t new_uid,
+                             gid_t old_gid, gid_t new_gid);
 
 #ifdef WITH_SELINUX
 /*
@@ -130,7 +144,10 @@ int selinux_file_context (const char *dst_name)
 #endif                         /* WITH_SELINUX */
 
 #if defined(WITH_ACL) || defined(WITH_ATTR)
-void error (struct error_context *ctx, const char *fmt, ...)
+/*
+ * error - format the error messages for the ACL and EQ libraries.
+ */
+static void error (struct error_context *ctx, const char *fmt, ...)
 {
        va_list ap;
 
@@ -143,7 +160,7 @@ void error (struct error_context *ctx, const char *fmt, ...)
        va_end (ap);
 }
 
-struct error_context ctx = {
+static struct error_context ctx = {
        error
 };
 #endif                         /* WITH_ACL || WITH_ATTR */
@@ -225,15 +242,46 @@ static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, c
  *
  *     copy_tree() walks a directory tree and copies ordinary files
  *     as it goes.
+ *
+ *     old_uid and new_uid are used to set the ownership of the copied
+ *     files. Unless old_uid is set to -1, only the files owned by
+ *     old_uid have their ownership changed to new_uid. In addition, if
+ *     new_uid is set to -1, no ownership will be changed.
+ *
+ *     The same logic applies for the group-ownership and
+ *     old_gid/new_gid.
  */
 int copy_tree (const char *src_root, const char *dst_root,
-               long int uid, long int gid)
+               bool copy_root,
+               uid_t old_uid, uid_t new_uid,
+               gid_t old_gid, gid_t new_gid)
 {
        int err = 0;
        bool set_orig = false;
        struct DIRECT *ent;
        DIR *dir;
 
+       if (copy_root) {
+               struct stat sb;
+               if (access (dst_root, F_OK) == 0) {
+                       return -1;
+               }
+
+               if (LSTAT (src_root, &sb) == -1) {
+                       return -1;
+               }
+
+               if (!S_ISDIR (sb.st_mode)) {
+                       fprintf (stderr,
+                                "%s: %s is not a directory",
+                                Prog, src_root);
+                       return -1;
+               }
+
+               return copy_entry (src_root, dst_root,
+                                  old_uid, new_uid, old_gid, new_gid);
+       }
+
        /*
         * Make certain both directories exist.  This routine is called
         * after the home directory is created, or recursively after the
@@ -290,7 +338,9 @@ int copy_tree (const char *src_root, const char *dst_root,
                                snprintf (dst_name, dst_len, "%s/%s",
                                          dst_root, ent->d_name);
 
-                               err = copy_entry (src_name, dst_name, uid, gid);
+                               err = copy_entry (src_name, dst_name,
+                                                 old_uid, new_uid,
+                                                 old_gid, new_gid);
                        }
                        if (NULL != src_name) {
                                free (src_name);
@@ -330,13 +380,18 @@ int copy_tree (const char *src_root, const char *dst_root,
  *
  *     The access and modification time will not be modified.
  *
- *     The permissions will be set to uid/gid.
+ *     The permissions will be set to new_uid/new_gid.
  *
- *     If uid (resp. gid) is equal to -1, the user (resp. group) will
+ *     If new_uid (resp. new_gid) is equal to -1, the user (resp. group) will
  *     not be modified.
+ *
+ *     Only the files owned (resp. group-owned) by old_uid (resp.
+ *     old_gid) will be modified, unless old_uid (resp. old_gid) is set
+ *     to -1.
  */
 static int copy_entry (const char *src, const char *dst,
-                       long int uid, long int gid)
+                       uid_t old_uid, uid_t new_uid,
+                       gid_t old_gid, gid_t new_gid)
 {
        int err = 0;
        struct stat sb;
@@ -371,7 +426,8 @@ static int copy_entry (const char *src, const char *dst,
 #endif                         /* !HAVE_STRUCT_STAT_ST_MTIM */
 
                if (S_ISDIR (sb.st_mode)) {
-                       err = copy_dir (src, dst, &sb, mt, uid, gid);
+                       err = copy_dir (src, dst, &sb, mt,
+                                       old_uid, new_uid, old_gid, new_gid);
                }
 
 #ifdef S_IFLNK
@@ -380,7 +436,8 @@ static int copy_entry (const char *src, const char *dst,
                 */
 
                else if (S_ISLNK (sb.st_mode)) {
-                       err = copy_symlink (src, dst, &sb, mt, uid, gid);
+                       err = copy_symlink (src, dst, &sb, mt,
+                                           old_uid, new_uid, old_gid, new_gid);
                }
 #endif                         /* S_IFLNK */
 
@@ -389,7 +446,7 @@ static int copy_entry (const char *src, const char *dst,
                 */
 
                else if ((lp = check_link (src, &sb)) != NULL) {
-                       err = copy_hardlink (src, dst, lp);
+                       err = copy_hardlink (dst, lp);
                }
 
                /*
@@ -399,7 +456,8 @@ static int copy_entry (const char *src, const char *dst,
                 */
 
                else if (!S_ISREG (sb.st_mode)) {
-                       err = copy_special (src, dst, &sb, mt, uid, gid);
+                       err = copy_special (src, dst, &sb, mt,
+                                           old_uid, new_uid, old_gid, new_gid);
                }
 
                /*
@@ -408,7 +466,8 @@ static int copy_entry (const char *src, const char *dst,
                 */
 
                else {
-                       err = copy_file (src, dst, &sb, mt, uid, gid);
+                       err = copy_file (src, dst, &sb, mt,
+                                        old_uid, new_uid, old_gid, new_gid);
                }
        }
 
@@ -420,14 +479,15 @@ static int copy_entry (const char *src, const char *dst,
  *
  *     Copy a directory (recursively) from src to dst.
  *
- *     statp, mt, uid, gid are used to set the access and modification and the
- *     access rights.
+ *     statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set
+ *     the access and modification and the access rights.
  *
  *     Return 0 on success, -1 on error.
  */
 static int copy_dir (const char *src, const char *dst,
                      const struct stat *statp, const struct timeval mt[],
-                     long int uid, long int gid)
+                     uid_t old_uid, uid_t new_uid,
+                     gid_t old_gid, gid_t new_gid)
 {
        int err = 0;
 
@@ -440,9 +500,8 @@ static int copy_dir (const char *src, const char *dst,
        selinux_file_context (dst);
 #endif                         /* WITH_SELINUX */
        if (   (mkdir (dst, statp->st_mode) != 0)
-           || (chown (dst,
-                      (uid == - 1) ? statp->st_uid : (uid_t) uid,
-                      (gid == - 1) ? statp->st_gid : (gid_t) gid) != 0)
+           || (chown_if_needed (dst, statp,
+                                old_uid, new_uid, old_gid, new_gid) != 0)
 #ifdef WITH_ACL
            || (perm_copy_file (src, dst, &ctx) != 0)
 #else                          /* !WITH_ACL */
@@ -458,7 +517,8 @@ static int copy_dir (const char *src, const char *dst,
         */
            || (attr_copy_file (src, dst, NULL, &ctx) != 0)
 #endif                         /* WITH_ATTR */
-           || (copy_tree (src, dst, uid, gid) != 0)
+           || (copy_tree (src, dst, false,
+                          old_uid, new_uid, old_gid, new_gid) != 0)
            || (utimes (dst, mt) != 0)) {
                err = -1;
        }
@@ -508,14 +568,15 @@ static char *readlink_malloc (const char *filename)
  *
  *     Copy a symlink from src to dst.
  *
- *     statp, mt, uid, gid are used to set the access and modification and the
- *     access rights.
+ *     statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set
+ *     the access and modification and the access rights.
  *
  *     Return 0 on success, -1 on error.
  */
 static int copy_symlink (const char *src, const char *dst,
                          const struct stat *statp, const struct timeval mt[],
-                         long int uid, long int gid)
+                         uid_t old_uid, uid_t new_uid,
+                         gid_t old_gid, gid_t new_gid)
 {
        char *oldlink;
 
@@ -554,9 +615,8 @@ static int copy_symlink (const char *src, const char *dst,
        selinux_file_context (dst);
 #endif                         /* WITH_SELINUX */
        if (   (symlink (oldlink, dst) != 0)
-           || (lchown (dst,
-                       (uid == -1) ? statp->st_uid : (uid_t) uid,
-                       (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)) {
+           || (lchown_if_needed (dst, statp,
+                                 old_uid, new_uid, old_gid, new_gid) != 0)) {
                /* FIXME: there are no modes on symlinks, right?
                 *        ACL could be copied, but this would be much more
                 *        complex than calling perm_copy_file.
@@ -589,7 +649,7 @@ static int copy_symlink (const char *src, const char *dst,
  *
  *     Return 0 on success, -1 on error.
  */
-static int copy_hardlink (const char *src, const char *dst,
+static int copy_hardlink (const char *dst,
                           struct link_name *lp)
 {
        /* FIXME: selinux, ACL, Extended Attributes needed? */
@@ -613,14 +673,15 @@ static int copy_hardlink (const char *src, const char *dst,
  *
  *     Copy a special file from src to dst.
  *
- *     statp, mt, uid, gid are used to set the access and modification and the
- *     access rights.
+ *     statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set
+ *     the access and modification and the access rights.
  *
  *     Return 0 on success, -1 on error.
  */
 static int copy_special (const char *src, const char *dst,
                          const struct stat *statp, const struct timeval mt[],
-                         long int uid, long int gid)
+                         uid_t old_uid, uid_t new_uid,
+                         gid_t old_gid, gid_t new_gid)
 {
        int err = 0;
 
@@ -629,9 +690,8 @@ static int copy_special (const char *src, const char *dst,
 #endif                         /* WITH_SELINUX */
 
        if (   (mknod (dst, statp->st_mode & ~07777, statp->st_rdev) != 0)
-           || (chown (dst,
-                      (uid == -1) ? statp->st_uid : (uid_t) uid,
-                      (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)
+           || (chown_if_needed (dst, statp,
+                                old_uid, new_uid, old_gid, new_gid) != 0)
 #ifdef WITH_ACL
            || (perm_copy_file (src, dst, &ctx) != 0)
 #else                          /* !WITH_ACL */
@@ -659,14 +719,15 @@ static int copy_special (const char *src, const char *dst,
  *
  *     Copy a file from src to dst.
  *
- *     statp, mt, uid, gid are used to set the access and modification and the
- *     access rights.
+ *     statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set
+ *     the access and modification and the access rights.
  *
  *     Return 0 on success, -1 on error.
  */
 static int copy_file (const char *src, const char *dst,
                       const struct stat *statp, const struct timeval mt[],
-                      long int uid, long int gid)
+                      uid_t old_uid, uid_t new_uid,
+                      gid_t old_gid, gid_t new_gid)
 {
        int err = 0;
        int ifd;
@@ -683,9 +744,8 @@ static int copy_file (const char *src, const char *dst,
 #endif                         /* WITH_SELINUX */
        ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777);
        if (   (ofd < 0)
-           || (fchown (ofd,
-                       (uid == -1) ? statp->st_uid : (uid_t) uid,
-                       (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)
+           || (fchown_if_needed (ofd, statp,
+                                 old_uid, new_uid, old_gid, new_gid) != 0)
 #ifdef WITH_ACL
            || (perm_copy_fd (src, ifd, dst, ofd, &ctx) != 0)
 #else                          /* !WITH_ACL */
@@ -734,3 +794,37 @@ static int copy_file (const char *src, const char *dst,
        return err;
 }
 
+#define def_chown_if_needed(chown_function, type_dst)                  \
+static int chown_function ## _if_needed (type_dst dst,                 \
+                                         const struct stat *statp,     \
+                                         uid_t old_uid, uid_t new_uid, \
+                                         gid_t old_gid, gid_t new_gid) \
+{                                                                      \
+       uid_t tmpuid = (uid_t) -1;                                     \
+       gid_t tmpgid = (gid_t) -1;                                     \
+                                                                       \
+       /* Use new_uid if old_uid is set to -1 or if the file was      \
+        * owned by the user. */                                       \
+       if (((uid_t) -1 == old_uid) || (statp->st_uid == old_uid)) {   \
+               tmpuid = new_uid;                                      \
+       }                                                              \
+       /* Otherwise, or if new_uid was set to -1, we keep the same    \
+        * owner. */                                                   \
+       if ((uid_t) -1 == tmpuid) {                                    \
+               tmpuid = statp->st_uid;                                \
+       }                                                              \
+                                                                       \
+       if (((gid_t) -1 == old_gid) || (statp->st_gid == old_gid)) {   \
+               tmpgid = new_gid;                                      \
+       }                                                              \
+       if ((gid_t) -1 == tmpgid) {                                    \
+               tmpgid = statp->st_gid;                                \
+       }                                                              \
+                                                                       \
+       return chown_function (dst, tmpuid, tmpgid);                   \
+}
+
+def_chown_if_needed (chown, const char *)
+def_chown_if_needed (lchown, const char *)
+def_chown_if_needed (fchown, int)
+
index a1deac57f89ba18ca5afcc2a7ac962cd7783a606..a3b3dc7a0141b552a883b17322ce25a686457d75 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1991 - 1994, Julianne Frances Haugh
  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
  * Copyright (c) 2000 - 2006, Tomasz Kłoczko
- * Copyright (c) 2007 - 2009, Nicolas François
+ * Copyright (c) 2007 - 2010, Nicolas François
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1806,7 +1806,8 @@ static void create_home (void)
 static void create_mail (void)
 {
        if (strcasecmp (create_mail_spool, "yes") == 0) {
-               char *spool, *file;
+               const char *spool;
+               char *file;
                int fd;
                struct group *gr;
                gid_t gid;
@@ -2036,7 +2037,8 @@ int main (int argc, char **argv)
        if (mflg) {
                create_home ();
                if (home_added) {
-                       copy_tree (def_template, user_home, user_id, user_gid);
+                       copy_tree (def_template, user_home, false,
+                                  (uid_t)-1, user_id, (gid_t)-1, user_gid);
                } else {
                        fprintf (stderr,
                                 _("%s: warning: the home directory already exists.\n"
index b41271773b3cbe351e7b9098fe04d210f9f61838..2d5036fc26d3caad751c43010ce7df4f54d9058a 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1991 - 1994, Julianne Frances Haugh
  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
  * Copyright (c) 2000 - 2006, Tomasz Kłoczko
- * Copyright (c) 2007 - 2009, Nicolas François
+ * Copyright (c) 2007 - 2010, Nicolas François
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1403,44 +1403,51 @@ static void move_home (void)
 {
        struct stat sb;
 
-       if (mflg && (stat (user_home, &sb) == 0)) {
+       if (stat (user_home, &sb) == 0) {
                /*
-                * Don't try to move it if it is not a directory
-                * (but /dev/null for example).  --marekm
+                * If the new home directory already exist, the user
+                * should not use -m.
                 */
-               if (!S_ISDIR (sb.st_mode)) {
-                       return;
-               }
-
                if (access (user_newhome, F_OK) == 0) {
                        fprintf (stderr,
                                 _("%s: directory %s exists\n"),
                                 Prog, user_newhome);
                        fail_exit (E_HOMEDIR);
-               } else if (rename (user_home, user_newhome) != 0) {
-                       // FIXME: rename above may have broken symlinks
-                       //        pointing to the user's home directory
-                       //        with an absolute path.
-                       if (errno == EXDEV) {
-                               if (mkdir (user_newhome, sb.st_mode & 0777) != 0) {
-                                       fprintf (stderr,
-                                                _("%s: can't create %s\n"),
-                                                Prog, user_newhome);
-                               }
-                               if (chown (user_newhome, sb.st_uid, sb.st_gid) != 0) {
-                                       fprintf (stderr,
-                                                _("%s: can't chown %s\n"),
-                                                Prog, user_newhome);
-                                       rmdir (user_newhome);
-                                       fail_exit (E_HOMEDIR);
-                               }
-                               // FIXME: the current uid & gid should
-                               // also be provided so that only the files
-                               // owned by the user/group have their
-                               // ownership changed.
-                               if (copy_tree (user_home, user_newhome,
-                                              uflg ? (long int)user_newid : -1,
-                                              gflg ? (long int)user_newgid : -1) == 0) {
+               }
+
+               /*
+                * Don't try to move it if it is not a directory
+                * (but /dev/null for example).  --marekm
+                */
+               if (!S_ISDIR (sb.st_mode)) {
+                       fprintf (stderr,
+                                _("%s: The previous home directory (%s) was "
+                                  "not a directory. It is not removed and no "
+                                  "home directories are created.\n"),
+                                Prog, user_home);
+                       fail_exit (E_HOMEDIR);
+               }
+
+               if (rename (user_home, user_newhome) == 0) {
+                       /* FIXME: rename above may have broken symlinks
+                        *        pointing to the user's home directory
+                        *        with an absolute path. */
+                       if (chown_tree (user_newhome,
+                                       user_id,  uflg ? user_newid  : (uid_t)-1,
+                                       user_gid, gflg ? user_newgid : (gid_t)-1) != 0) {
+                               fprintf (stderr,
+                                        _("%s: Failed to change ownership of the home directory"),
+                                        Prog);
+                               fail_exit (E_HOMEDIR);
+                       }
+                       return;
+               } else {
+                       if (EXDEV == errno) {
+                               if (copy_tree (user_home, user_newhome, true,
+                                              user_id,
+                                              uflg ? user_newid : (uid_t)-1,
+                                              user_gid,
+                                              gflg ? user_newgid : (gid_t)-1) == 0) {
                                        if (remove_tree (user_home, true) != 0) {
                                                fprintf (stderr,
                                                         _("%s: warning: failed to completely remove old home directory %s"),
@@ -1457,8 +1464,6 @@ static void move_home (void)
                                        return;
                                }
 
-                               /* TODO: do some cleanup if the copy
-                                *       was started */
                                (void) remove_tree (user_newhome, true);
                        }
                        fprintf (stderr,
@@ -1472,16 +1477,6 @@ static void move_home (void)
                              user_newname, (unsigned int) user_newid, 1);
 #endif
        }
-       if (uflg || gflg) {
-#ifdef WITH_AUDIT
-               audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
-                             "changing home directory owner",
-                             user_newname, (unsigned int) user_newid, 1);
-#endif
-               chown (dflg ? user_newhome : user_home,
-                      uflg ? user_newid : user_id,
-                      gflg ? user_newgid : user_gid);
-       }
 }
 
 /*
@@ -1819,17 +1814,28 @@ int main (int argc, char **argv)
        }
 #endif
 
-       if (uflg) { // FIXME: gflg also, except for faillog/lastlog
+       if (uflg) {
                update_lastlog ();
                update_faillog ();
+       }
 
+       if (!mflg && (uflg || gflg)) {
+               if (access (dflg ? user_newhome : user_home, F_OK) == 0) {
                /*
                 * Change the UID on all of the files owned by `user_id' to
                 * `user_newid' in the user's home directory.
+                *
+                * move_home() already takes care of changing the ownership.
                 */
-               chown_tree (dflg ? user_newhome : user_home,
-                           user_id, user_newid,
-                           user_gid, gflg ? user_newgid : user_gid);
+               if (chown_tree (dflg ? user_newhome : user_home,
+                               user_id,  uflg ? user_newid  : (uid_t)-1,
+                               user_gid, gflg ? user_newgid : (gid_t)-1) != 0) {
+                       fprintf (stderr,
+                                _("%s: Failed to change ownership of the home directory"),
+                                Prog);
+                       fail_exit (E_HOMEDIR);
+               }
+               }
        }
 
        return E_SUCCESS;