From: nekral-guest Date: Sun, 4 Apr 2010 20:55:46 +0000 (+0000) Subject: 2010-04-04 Nicolas François X-Git-Tag: 4.1.5~269 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=16362e289b79f82e6e49938c7d4bd9a89e51c8e3;p=shadow 2010-04-04 Nicolas François * 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 * 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 * 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 * 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. --- diff --git a/ChangeLog b/ChangeLog index 3809c7ea..08f13229 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,45 @@ +2010-04-04 Nicolas François + + * 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 + + * 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 + + * 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 + + * 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 * man/hu/passwd.5: Fix formatting typo. diff --git a/lib/prototypes.h b/lib/prototypes.h index b9bbd0b2..b8e19ec3 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -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 diff --git a/libmisc/chowndir.c b/libmisc/chowndir.c index b1239613..5179c6bf 100644 --- a/libmisc/chowndir.c +++ b/libmisc/chowndir.c @@ -45,23 +45,36 @@ * * 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; } diff --git a/libmisc/copydir.c b/libmisc/copydir.c index 6d4f8397..ba6b91c7 100644 --- a/libmisc/copydir.c +++ b/libmisc/copydir.c @@ -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) + diff --git a/src/useradd.c b/src/useradd.c index a1deac57..a3b3dc7a 100644 --- a/src/useradd.c +++ b/src/useradd.c @@ -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" diff --git a/src/usermod.c b/src/usermod.c index b4127177..2d5036fc 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -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;