summaryrefslogtreecommitdiffstats
path: root/pw
diff options
context:
space:
mode:
authorBaptiste Daroussin <bapt@FreeBSD.org>2015-07-12 20:29:51 +0000
committerBaptiste Daroussin <bapt@FreeBSD.org>2015-07-12 20:29:51 +0000
commitf6975773c5ebf939f9e3f22b2b600d2c821604bc (patch)
tree8ae8db36682640068427bbf00fdaed91decb822d /pw
parent5c8793faf0c3ceee6ca534d0f69b417a3759f7af (diff)
downloadpw-darwin-f6975773c5ebf939f9e3f22b2b600d2c821604bc.tar.gz
pw-darwin-f6975773c5ebf939f9e3f22b2b600d2c821604bc.tar.zst
pw-darwin-f6975773c5ebf939f9e3f22b2b600d2c821604bc.zip
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
Diffstat (limited to 'pw')
-rw-r--r--pw/cpdir.c152
-rw-r--r--pw/pw.c4
-rw-r--r--pw/pw_user.c17
-rw-r--r--pw/pwupd.h4
4 files changed, 90 insertions, 87 deletions
diff --git a/pw/cpdir.c b/pw/cpdir.c
index 0fd671b..e84d0f3 100644
--- a/pw/cpdir.c
+++ b/pw/cpdir.c
@@ -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 (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 (--counter == 0 && copybuf != NULL) {
- free(copybuf);
- copybuf = NULL;
+
+ 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);
}
-
diff --git a/pw/pw.c b/pw/pw.c
index 532d77b..d81cfb0 100644
--- a/pw/pw.c
+++ b/pw/pw.c
@@ -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
diff --git a/pw/pw_user.c b/pw/pw_user.c
index 16b2ac8..afc163f 100644
--- a/pw/pw_user.c
+++ b/pw/pw_user.c
@@ -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);
}
diff --git a/pw/pwupd.h b/pw/pwupd.h
index 92e5548..f08d2c7 100644
--- a/pw/pwupd.h
+++ b/pw/pwupd.h
@@ -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 * nam);
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