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
This commit is contained in:
Baptiste Daroussin 2015-07-12 20:29:51 +00:00
parent 3f3def246a
commit 65730d9349
5 changed files with 117 additions and 89 deletions

View File

@ -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);
}
}
if (--counter == 0 && copybuf != NULL) {
free(copybuf);
copybuf = NULL;
}
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 (!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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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
}