cpdir.c revision 297290
1107002Sschweikh/*-
2174422Sdougb * Copyright (C) 1996
3174425Sdougb *	David L. Nugent.  All rights reserved.
4174425Sdougb *
5174425Sdougb * Redistribution and use in source and binary forms, with or without
6174425Sdougb * modification, are permitted provided that the following conditions
7174425Sdougb * are met:
8174425Sdougb * 1. Redistributions of source code must retain the above copyright
9174425Sdougb *    notice, this list of conditions and the following disclaimer.
10174425Sdougb * 2. Redistributions in binary form must reproduce the above copyright
11174425Sdougb *    notice, this list of conditions and the following disclaimer in the
12174425Sdougb *    documentation and/or other materials provided with the distribution.
13174425Sdougb *
14174425Sdougb * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15174425Sdougb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16174425Sdougb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17174425Sdougb * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18174425Sdougb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19174425Sdougb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20174425Sdougb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21174425Sdougb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22174425Sdougb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23174425Sdougb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24174425Sdougb * SUCH DAMAGE.
25174425Sdougb */
26174425Sdougb
27174425Sdougb#ifndef lint
28174425Sdougbstatic const char rcsid[] =
29174425Sdougb  "$FreeBSD: head/usr.sbin/pw/cpdir.c 297290 2016-03-26 11:41:35Z bapt $";
30174425Sdougb#endif /* not lint */
31174425Sdougb
32174425Sdougb#include <dirent.h>
33174425Sdougb#include <err.h>
34174425Sdougb#include <errno.h>
35174425Sdougb#include <fcntl.h>
36174425Sdougb#include <string.h>
37174425Sdougb#include <unistd.h>
38174425Sdougb
39174425Sdougb#include "pw.h"
40174425Sdougb
41174425Sdougbvoid
42174425Sdougbcopymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid,
43174425Sdougb    gid_t gid, int flags)
44174425Sdougb{
45174425Sdougb	char		*p, lnk[MAXPATHLEN], copybuf[4096];
46174425Sdougb	int		len, homefd, srcfd, destfd;
47174425Sdougb	ssize_t		sz;
48174425Sdougb	struct stat     st;
49174425Sdougb	struct dirent  *e;
50174425Sdougb	DIR		*d;
51174425Sdougb
52174425Sdougb	if (*dir == '/')
53174425Sdougb		dir++;
54174425Sdougb
55174425Sdougb	if (mkdirat(rootfd, dir, mode) != 0 && errno != EEXIST) {
56174425Sdougb		warn("mkdir(%s)", dir);
57174425Sdougb		return;
58174425Sdougb	}
59174425Sdougb	fchownat(rootfd, dir, uid, gid, AT_SYMLINK_NOFOLLOW);
60174425Sdougb	if (flags > 0)
61174425Sdougb		chflagsat(rootfd, dir, flags, AT_SYMLINK_NOFOLLOW);
62174425Sdougb
63174425Sdougb	if (skelfd == -1)
64174425Sdougb		return;
65174425Sdougb
66174425Sdougb	homefd = openat(rootfd, dir, O_DIRECTORY);
67174425Sdougb	if ((d = fdopendir(skelfd)) == NULL) {
68174425Sdougb		close(skelfd);
69174425Sdougb		close(homefd);
70174425Sdougb		return;
71174425Sdougb	}
72174425Sdougb
73174425Sdougb	while ((e = readdir(d)) != NULL) {
74174425Sdougb		if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0)
75174425Sdougb			continue;
76174425Sdougb
77174425Sdougb		p = e->d_name;
78174425Sdougb		if (fstatat(skelfd, p, &st, AT_SYMLINK_NOFOLLOW) == -1)
79174425Sdougb			continue;
80174425Sdougb
81174425Sdougb		if (strncmp(p, "dot.", 4) == 0)	/* Conversion */
82174425Sdougb			p += 3;
83174425Sdougb
84174425Sdougb		if (S_ISDIR(st.st_mode)) {
85174425Sdougb			copymkdir(homefd, p, openat(skelfd, e->d_name, O_DIRECTORY),
86174425Sdougb			    st.st_mode & _DEF_DIRMODE, uid, gid, st.st_flags);
87174425Sdougb			continue;
88174425Sdougb		}
89174425Sdougb
90174425Sdougb		if (S_ISLNK(st.st_mode) &&
91174425Sdougb		    (len = readlinkat(skelfd, e->d_name, lnk, sizeof(lnk) -1))
92174425Sdougb		    != -1) {
93174425Sdougb			lnk[len] = '\0';
94174425Sdougb			symlinkat(lnk, homefd, p);
95174425Sdougb			fchownat(homefd, p, uid, gid, AT_SYMLINK_NOFOLLOW);
96174425Sdougb			continue;
97174425Sdougb		}
98174425Sdougb
99174425Sdougb		if (!S_ISREG(st.st_mode))
100174425Sdougb			continue;
101174425Sdougb
102174425Sdougb		if ((srcfd = openat(skelfd, e->d_name, O_RDONLY)) == -1)
103174425Sdougb			continue;
104174425Sdougb		destfd = openat(homefd, p, O_RDWR | O_CREAT | O_EXCL,
105174425Sdougb		    st.st_mode);
1062490Sjkh		if (destfd == -1) {
1072490Sjkh			close(srcfd);
1082490Sjkh			continue;
1092490Sjkh		}
1102490Sjkh
1112490Sjkh		while ((sz = read(srcfd, copybuf, sizeof(copybuf))) > 0)
1122490Sjkh			write(destfd, copybuf, sz);
1132490Sjkh
1142490Sjkh		close(srcfd);
1152490Sjkh		/*
1162490Sjkh		 * Propagate special filesystem flags
1172490Sjkh		 */
1182490Sjkh		fchown(destfd, uid, gid);
1192490Sjkh		fchflags(destfd, st.st_flags);
1202490Sjkh		close(destfd);
1212490Sjkh	}
1222490Sjkh	closedir(d);
1232490Sjkh}
1242490Sjkh