Author: bapt
Date: Sun Jul 12 20:29:51 2015
New Revision: 285430
URL: https://svnweb.freebsd.org/changeset/base/285430

Log:
  Rework the home directory creation and copy or the skel content to use *at
  functions
  
  This allows to simplify the code a bit for -R by not having to keep modifying
  path and also prepare the code to improve support -R in userdel
  
  While here, add regression tests for the functionality

Modified:
  head/usr.sbin/pw/cpdir.c
  head/usr.sbin/pw/pw.c
  head/usr.sbin/pw/pw_user.c
  head/usr.sbin/pw/pwupd.h
  head/usr.sbin/pw/tests/pw_useradd.sh

Modified: head/usr.sbin/pw/cpdir.c
==============================================================================
--- head/usr.sbin/pw/cpdir.c    Sun Jul 12 19:58:12 2015        (r285429)
+++ head/usr.sbin/pw/cpdir.c    Sun Jul 12 20:29:51 2015        (r285430)
@@ -45,87 +45,85 @@ static const char rcsid[] =
 #include "pwupd.h"
 
 void
-copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t 
gid)
+copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid,
+    gid_t gid, int flags)
 {
-       char            src[MAXPATHLEN];
-       char            dst[MAXPATHLEN];
-       char            lnk[MAXPATHLEN];
-       int             len;
+       char            *p, lnk[MAXPATHLEN], copybuf[4096];
+       int             len, homefd, srcfd, destfd;
+       ssize_t         sz;
+       struct stat     st;
+       struct dirent  *e;
+       DIR             *d;
 
-       if (mkdir(dir, mode) != 0 && errno != EEXIST) {
+       if (*dir == '/')
+               dir++;
+
+       if (mkdirat(rootfd, dir, mode) != 0 && errno != EEXIST) {
                warn("mkdir(%s)", dir);
-       } else {
-               int             infd, outfd;
-               struct stat     st;
-
-               static char     counter = 0;
-               static char    *copybuf = NULL;
-
-               ++counter;
-               chown(dir, uid, gid);
-               if (skel != NULL && *skel != '\0') {
-                       DIR            *d = opendir(skel);
-
-                       if (d != NULL) {
-                               struct dirent  *e;
-
-                               while ((e = readdir(d)) != NULL) {
-                                       char           *p = e->d_name;
-
-                                       if (snprintf(src, sizeof(src), "%s/%s", 
skel, p) >= (int)sizeof(src))
-                                               warn("warning: pathname too 
long '%s/%s' (skel not copied)", skel, p);
-                                       else if (lstat(src, &st) == 0) {
-                                               if (strncmp(p, "dot.", 4) == 0) 
/* Conversion */
-                                                       p += 3;
-                                               if (snprintf(dst, sizeof(dst), 
"%s/%s", dir, p) >= (int)sizeof(dst))
-                                                       warn("warning: path too 
long '%s/%s' (skel file skipped)", dir, p);
-                                               else {
-                                                   if (S_ISDIR(st.st_mode)) {  
/* Recurse for this */
-                                                       if (strcmp(e->d_name, 
".") != 0 && strcmp(e->d_name, "..") != 0)
-                                                               copymkdir(dst, 
src, st.st_mode & _DEF_DIRMODE, uid, gid);
-                                                               chflags(dst, 
st.st_flags);      /* propagate flags */
-                                                   } else if 
(S_ISLNK(st.st_mode) && (len = readlink(src, lnk, sizeof(lnk) - 1)) != -1) {
-                                                       lnk[len] = '\0';
-                                                       symlink(lnk, dst);
-                                                       lchown(dst, uid, gid);
-                                                       /*
-                                                        * Note: don't 
propagate special attributes
-                                                        * but do propagate 
file flags
-                                                        */
-                                                   } else if 
(S_ISREG(st.st_mode) && (outfd = open(dst, O_RDWR | O_CREAT | O_EXCL, 
st.st_mode)) != -1) {
-                                                       if ((infd = open(src, 
O_RDONLY)) == -1) {
-                                                               close(outfd);
-                                                               remove(dst);
-                                                       } else {
-                                                               int             
b;
-
-                                                               /*
-                                                                * Allocate our 
copy buffer if we need to
-                                                                */
-                                                               if (copybuf == 
NULL)
-                                                                       copybuf 
= malloc(4096);
-                                                               while ((b = 
read(infd, copybuf, 4096)) > 0)
-                                                                       
write(outfd, copybuf, b);
-                                                               close(infd);
-                                                               /*
-                                                                * Propagate 
special filesystem flags
-                                                                */
-                                                               fchown(outfd, 
uid, gid);
-                                                               fchflags(outfd, 
st.st_flags);
-                                                               close(outfd);
-                                                               chown(dst, uid, 
gid);
-                                                       }
-                                                   }
-                                               }
-                                       }
-                               }
-                               closedir(d);
-                       }
+               return;
+       }
+       fchownat(rootfd, dir, uid, gid, AT_SYMLINK_NOFOLLOW);
+       if (flags > 0)
+               chflagsat(rootfd, dir, flags, AT_SYMLINK_NOFOLLOW);
+
+       if (skelfd == -1)
+               return;
+
+       homefd = openat(rootfd, dir, O_DIRECTORY);
+       if ((d = fdopendir(skelfd)) == NULL) {
+               close(skelfd);
+               close(homefd);
+               return;
+       }
+
+       while ((e = readdir(d)) != NULL) {
+               if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0)
+                       continue;
+
+               p = e->d_name;
+               if (fstatat(skelfd, p, &st, AT_SYMLINK_NOFOLLOW) == -1)
+                       continue;
+
+               if (strncmp(p, "dot.", 4) == 0) /* Conversion */
+                       p += 3;
+
+               if (S_ISDIR(st.st_mode)) {
+                       copymkdir(homefd, p, openat(skelfd, e->d_name, 
O_DIRECTORY),
+                           st.st_mode & _DEF_DIRMODE, uid, gid, st.st_flags);
+                       continue;
                }
-               if (--counter == 0 && copybuf != NULL) {
-                       free(copybuf);
-                       copybuf = NULL;
+
+               if (S_ISLNK(st.st_mode) &&
+                   (len = readlinkat(skelfd, e->d_name, lnk, sizeof(lnk) -1))
+                   != -1) {
+                       lnk[len] = '\0';
+                       symlinkat(lnk, homefd, p);
+                       fchownat(homefd, p, uid, gid, AT_SYMLINK_NOFOLLOW);
+                       continue;
                }
+
+               if (!S_ISREG(st.st_mode))
+                       continue;
+
+               if ((srcfd = openat(skelfd, e->d_name, O_RDONLY)) == -1)
+                       continue;
+               destfd = openat(homefd, p, O_RDWR | O_CREAT | O_EXCL,
+                   st.st_mode);
+               if (destfd == -1) {
+                       close(srcfd);
+                       continue;
+               }
+
+               while ((sz = read(srcfd, copybuf, sizeof(copybuf))) > 0)
+                       write(destfd, copybuf, sz);
+
+               close(srcfd);
+               /*
+                * Propagate special filesystem flags
+                */
+               fchown(destfd, uid, gid);
+               fchflags(destfd, st.st_flags);
+               close(destfd);
        }
+       closedir(d);
 }
-

Modified: head/usr.sbin/pw/pw.c
==============================================================================
--- head/usr.sbin/pw/pw.c       Sun Jul 12 19:58:12 2015        (r285429)
+++ head/usr.sbin/pw/pw.c       Sun Jul 12 20:29:51 2015        (r285430)
@@ -136,6 +136,7 @@ main(int argc, char *argv[])
        name = NULL;
        relocated = nis = false;
        memset(&conf, 0, sizeof(conf));
+       strlcpy(conf.rootdir, "/", sizeof(conf.rootdir));
        strlcpy(conf.etcpath, _PATH_PWD, sizeof(conf.etcpath));
        conf.fd = -1;
 
@@ -215,6 +216,9 @@ main(int argc, char *argv[])
        if (mode == -1 || which == -1)
                cmdhelp(mode, which);
 
+       conf.rootfd = open(conf.rootdir, O_DIRECTORY|O_CLOEXEC);
+       if (conf.rootfd == -1)
+               errx(EXIT_FAILURE, "Unable to open '%s'", conf.rootdir);
        conf.which = which;
        /*
         * We know which mode we're in and what we're about to do, so now

Modified: head/usr.sbin/pw/pw_user.c
==============================================================================
--- head/usr.sbin/pw/pw_user.c  Sun Jul 12 19:58:12 2015        (r285429)
+++ head/usr.sbin/pw/pw_user.c  Sun Jul 12 20:29:51 2015        (r285430)
@@ -67,20 +67,19 @@ static void     rmopie(char const * name
 static void
 create_and_populate_homedir(struct passwd *pwd)
 {
-       char *homedir, *dotdir;
        struct userconf *cnf = conf.userconf;
+       const char *skeldir;
+       int skelfd = -1;
 
-       homedir = dotdir = NULL;
+       skeldir = cnf->dotdir;
 
-       if (conf.rootdir[0] != '\0') {
-               asprintf(&homedir, "%s/%s", conf.rootdir, pwd->pw_dir);
-               if (homedir == NULL)
-                       errx(EX_OSERR, "out of memory");
-               asprintf(&dotdir, "%s/%s", conf.rootdir, cnf->dotdir);
+       if (skeldir != NULL && *skeldir != '\0') {
+               skelfd = openat(conf.rootfd, cnf->dotdir,
+                   O_DIRECTORY|O_CLOEXEC);
        }
 
-       copymkdir(homedir ? homedir : pwd->pw_dir, dotdir ? dotdir: cnf->dotdir,
-           cnf->homemode, pwd->pw_uid, pwd->pw_gid);
+       copymkdir(conf.rootfd, pwd->pw_dir, skelfd, cnf->homemode, pwd->pw_uid,
+           pwd->pw_gid, 0);
        pw_log(cnf, M_ADD, W_USER, "%s(%u) home %s made", pwd->pw_name,
            pwd->pw_uid, pwd->pw_dir);
 }

Modified: head/usr.sbin/pw/pwupd.h
==============================================================================
--- head/usr.sbin/pw/pwupd.h    Sun Jul 12 19:58:12 2015        (r285429)
+++ head/usr.sbin/pw/pwupd.h    Sun Jul 12 20:29:51 2015        (r285430)
@@ -87,6 +87,7 @@ struct pwconf {
        char            *config;
        char            *gecos;
        int              fd;
+       int              rootfd;
        int              which;
        bool             quiet;
        bool             force;
@@ -156,7 +157,8 @@ struct group * vgetgrnam(const char * na
 RET_SETGRENT   vsetgrent(void);
 void           vendgrent(void);
 
-void copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, 
gid_t gid);
+void copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t 
uid,
+    gid_t gid, int flags);
 void rm_r(char const * dir, uid_t uid);
 __END_DECLS
 

Modified: head/usr.sbin/pw/tests/pw_useradd.sh
==============================================================================
--- head/usr.sbin/pw/tests/pw_useradd.sh        Sun Jul 12 19:58:12 2015        
(r285429)
+++ head/usr.sbin/pw/tests/pw_useradd.sh        Sun Jul 12 20:29:51 2015        
(r285430)
@@ -255,6 +255,30 @@ user_add_R_body() {
 #      test -d ${HOME}/home/bar && atf_fail "Directory not removed"
 }
 
+atf_test_case user_add_skel
+user_add_skel_body() {
+       populate_root_etc_skel
+
+       mkdir ${HOME}/skel
+       echo "a" > ${HOME}/skel/.a
+       echo "b" > ${HOME}/skel/b
+       mkdir ${HOME}/skel/c
+       mkdir ${HOME}/skel/c/d
+       mkdir ${HOME}/skel/dot.plop
+       echo "c" > ${HOME}/skel/c/d/dot.c
+       mkdir ${HOME}/home
+       ln -sf /nonexistent ${HOME}/skel/c/foo
+       atf_check -s exit:0 ${RPW} useradd foo -k skel -m
+       test -d ${HOME}/home/foo || atf_fail "Directory not created"
+       test -f ${HOME}/home/foo/.a || atf_fail "File not created"
+       atf_check -o file:${HOME}/skel/.a -s exit:0 cat ${HOME}/home/foo/.a
+       atf_check -o file:${HOME}/skel/b -s exit:0 cat ${HOME}/home/foo/b
+       test -d ${HOME}/home/foo/c || atf_fail "Dotted directory in skel not 
copied"
+       test -d ${HOME}/home/foo/.plop || atf_fail "Directory in skell not 
created"
+       atf_check -o inline:"/nonexistent\n" -s ignore readlink -f 
${HOME}/home/foo/c/foo
+       atf_check -o file:${HOME}/skel/c/d/dot.c -s exit:0 cat 
${HOME}/home/foo/c/d/.c
+}
+
 atf_init_test_cases() {
        atf_add_test_case user_add
        atf_add_test_case user_add_noupdate
@@ -277,4 +301,5 @@ atf_init_test_cases() {
        atf_add_test_case user_add_invalid_group_entry
        atf_add_test_case user_add_password_from_h
        atf_add_test_case user_add_R
+       atf_add_test_case user_add_skel
 }
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to