svn commit: r285430 - in head/usr.sbin/pw: . tests

Baptiste Daroussin bapt at FreeBSD.org
Sun Jul 12 20:29:54 UTC 2015


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
 }


More information about the svn-src-head mailing list