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:
parent
3f3def246a
commit
65730d9349
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user