1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
217257d1b4Sraf 
22da6c28aaSamw /*
23575bd8a2Smarks  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24da6c28aaSamw  * Use is subject to license terms.
2533f5ff17SMilan Jurik  * Copyright 2012 Milan Jurik. All rights reserved.
26*b63d0986SJohn Levon  * Copyright (c) 2019, Joyent, Inc.
27da6c28aaSamw  */
28da6c28aaSamw 
29da6c28aaSamw #include "libcmdutils.h"
30da6c28aaSamw 
31da6c28aaSamw 
32da6c28aaSamw /*
33da6c28aaSamw  * Gets file descriptors of attribute directories for source and target
34da6c28aaSamw  * attribute files
35da6c28aaSamw  */
36da6c28aaSamw int
get_attrdirs(int indfd,int outdfd,char * attrfile,int * sfd,int * tfd)37da6c28aaSamw get_attrdirs(int indfd, int outdfd, char *attrfile, int *sfd, int *tfd)
38da6c28aaSamw {
39da6c28aaSamw 	int	pwdfd;
40da6c28aaSamw 	int	fd1;
41da6c28aaSamw 	int	fd2;
42da6c28aaSamw 
43da6c28aaSamw 	pwdfd = open(".", O_RDONLY);
44da6c28aaSamw 	if ((pwdfd != -1) && (fchdir(indfd) == 0)) {
45da6c28aaSamw 		if ((fd1 = attropen(attrfile, ".", O_RDONLY)) == -1) {
46da6c28aaSamw 			(void) fchdir(pwdfd);
47da6c28aaSamw 			(void) close(pwdfd);
48da6c28aaSamw 			return (1);
49da6c28aaSamw 		}
50da6c28aaSamw 		*sfd = fd1;
51da6c28aaSamw 	} else {
52da6c28aaSamw 		(void) fchdir(pwdfd);
53da6c28aaSamw 		(void) close(pwdfd);
54da6c28aaSamw 		return (1);
55da6c28aaSamw 	}
56da6c28aaSamw 	if (fchdir(outdfd) == 0) {
57da6c28aaSamw 		if ((fd2 = attropen(attrfile, ".", O_RDONLY)) == -1) {
58da6c28aaSamw 			(void) fchdir(pwdfd);
59da6c28aaSamw 			(void) close(pwdfd);
60da6c28aaSamw 			return (1);
61da6c28aaSamw 		}
62da6c28aaSamw 		*tfd = fd2;
63da6c28aaSamw 	} else {
64da6c28aaSamw 		(void) fchdir(pwdfd);
65da6c28aaSamw 		(void) close(pwdfd);
66da6c28aaSamw 		return (1);
67da6c28aaSamw 	}
68da6c28aaSamw 	(void) fchdir(pwdfd);
69da6c28aaSamw 	return (0);
70da6c28aaSamw }
71da6c28aaSamw 
72da6c28aaSamw /*
73da6c28aaSamw  * mv_xattrs - Copies the content of the extended attribute files. Then
74*b63d0986SJohn Levon  *	moves the extended system attributes from the input attribute files
75da6c28aaSamw  *      to the target attribute files. Moves the extended system attributes
76da6c28aaSamw  *	from source to the target file. This function returns 0 on success
77da6c28aaSamw  *	and nonzero on error.
78da6c28aaSamw  */
79da6c28aaSamw int
mv_xattrs(char * cmd,char * infile,char * outfile,int sattr,int silent)80da6c28aaSamw mv_xattrs(char *cmd, char *infile, char *outfile, int sattr, int silent)
81da6c28aaSamw {
82da6c28aaSamw 	int srcfd = -1;
83da6c28aaSamw 	int indfd = -1;
84da6c28aaSamw 	int outdfd = -1;
85da6c28aaSamw 	int tmpfd = -1;
86da6c28aaSamw 	int sattrfd = -1;
87da6c28aaSamw 	int tattrfd = -1;
88da6c28aaSamw 	int asfd = -1;
89da6c28aaSamw 	int atfd = -1;
90da6c28aaSamw 	DIR *dirp = NULL;
91da6c28aaSamw 	struct dirent *dp = NULL;
92da6c28aaSamw 	char *etext = NULL;
93da6c28aaSamw 	struct stat st1;
94da6c28aaSamw 	struct stat st2;
95da6c28aaSamw 	nvlist_t *response = NULL;
96da6c28aaSamw 	nvlist_t *res = NULL;
97da6c28aaSamw 
98da6c28aaSamw 	if ((srcfd = open(infile, O_RDONLY)) == -1) {
99da6c28aaSamw 		etext = dgettext(TEXT_DOMAIN, "cannot open source");
100da6c28aaSamw 		goto error;
101da6c28aaSamw 	}
102da6c28aaSamw 	if (sattr)
103da6c28aaSamw 		response = sysattr_list(cmd, srcfd, infile);
104da6c28aaSamw 
105da6c28aaSamw 	if ((indfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) {
106da6c28aaSamw 		etext = dgettext(TEXT_DOMAIN, "cannot openat source");
107da6c28aaSamw 		goto error;
108da6c28aaSamw 	}
109da6c28aaSamw 	if ((outdfd = attropen(outfile, ".", O_RDONLY)) == -1) {
110da6c28aaSamw 		etext = dgettext(TEXT_DOMAIN, "cannot attropen target");
111da6c28aaSamw 		goto error;
112da6c28aaSamw 	}
113da6c28aaSamw 	if ((tmpfd = dup(indfd)) == -1) {
114da6c28aaSamw 		etext = dgettext(TEXT_DOMAIN, "cannot dup descriptor");
115da6c28aaSamw 		goto error;
116da6c28aaSamw 
117da6c28aaSamw 	}
118da6c28aaSamw 	if ((dirp = fdopendir(tmpfd)) == NULL) {
119da6c28aaSamw 		etext = dgettext(TEXT_DOMAIN, "cannot access source");
120da6c28aaSamw 		goto error;
121da6c28aaSamw 	}
122da6c28aaSamw 	while ((dp = readdir(dirp)) != NULL) {
123da6c28aaSamw 		if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
124da6c28aaSamw 		    (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
125da6c28aaSamw 		    dp->d_name[2] == '\0') ||
126da6c28aaSamw 		    (sysattr_type(dp->d_name) == _RO_SATTR) ||
127da6c28aaSamw 		    (sysattr_type(dp->d_name) == _RW_SATTR))
128da6c28aaSamw 			continue;
129da6c28aaSamw 
130da6c28aaSamw 		if ((sattrfd = openat(indfd, dp->d_name,
131da6c28aaSamw 		    O_RDONLY)) == -1) {
132da6c28aaSamw 			etext = dgettext(TEXT_DOMAIN,
133da6c28aaSamw 			    "cannot open src attribute file");
134da6c28aaSamw 			goto error;
135da6c28aaSamw 		}
136da6c28aaSamw 		if (fstat(sattrfd, &st1) < 0) {
137da6c28aaSamw 			etext = dgettext(TEXT_DOMAIN,
138da6c28aaSamw 			    "could not stat attribute file");
139da6c28aaSamw 			goto error;
140da6c28aaSamw 		}
141da6c28aaSamw 		if ((tattrfd = openat(outdfd, dp->d_name,
142da6c28aaSamw 		    O_RDWR|O_CREAT|O_TRUNC, st1.st_mode)) == -1) {
143da6c28aaSamw 			etext = dgettext(TEXT_DOMAIN,
144da6c28aaSamw 			    "cannot open target attribute file");
145da6c28aaSamw 			goto error;
146da6c28aaSamw 		}
147da6c28aaSamw 		if (fstat(tattrfd, &st2) < 0) {
148da6c28aaSamw 			etext = dgettext(TEXT_DOMAIN,
149da6c28aaSamw 			    "could not stat attribute file");
150da6c28aaSamw 			goto error;
151da6c28aaSamw 		}
152da6c28aaSamw 		if (writefile(sattrfd, tattrfd, infile, outfile, dp->d_name,
153da6c28aaSamw 		    dp->d_name, &st1, &st2) != 0) {
154da6c28aaSamw 			etext = dgettext(TEXT_DOMAIN,
155da6c28aaSamw 			    "failed to copy extended attribute "
156da6c28aaSamw 			    "from source to target");
157da6c28aaSamw 			goto error;
158da6c28aaSamw 		}
159da6c28aaSamw 
160da6c28aaSamw 		errno = 0;
161da6c28aaSamw 		if (sattr) {
162da6c28aaSamw 			/*
163da6c28aaSamw 			 * Gets non default extended system attributes from
164da6c28aaSamw 			 * source to copy to target.
165da6c28aaSamw 			 */
166*b63d0986SJohn Levon 			res = sysattr_list(cmd, sattrfd, dp->d_name);
167da6c28aaSamw 
168da6c28aaSamw 			if (res != NULL &&
169da6c28aaSamw 			    get_attrdirs(indfd, outdfd, dp->d_name, &asfd,
170da6c28aaSamw 			    &atfd) != 0) {
171da6c28aaSamw 				etext = dgettext(TEXT_DOMAIN,
172da6c28aaSamw 				    "Failed to open attribute files");
173da6c28aaSamw 				goto error;
174da6c28aaSamw 			}
175da6c28aaSamw 			/*
176da6c28aaSamw 			 * Copy extended system attribute from source
177da6c28aaSamw 			 * attribute file to target attribute file
178da6c28aaSamw 			 */
179da6c28aaSamw 			if (res != NULL &&
180da6c28aaSamw 			    (renameat(asfd, VIEW_READWRITE, atfd,
181da6c28aaSamw 			    VIEW_READWRITE) != 0)) {
182da6c28aaSamw 				if (errno == EPERM)
183da6c28aaSamw 					etext = dgettext(TEXT_DOMAIN,
184da6c28aaSamw 					    "Permission denied -"
185da6c28aaSamw 					    "failed to move system attribute");
186da6c28aaSamw 				else
187da6c28aaSamw 					etext = dgettext(TEXT_DOMAIN,
188da6c28aaSamw 					    "failed to move extended "
189da6c28aaSamw 					    "system attribute");
190da6c28aaSamw 				goto error;
191da6c28aaSamw 			}
192da6c28aaSamw 		}
193da6c28aaSamw 		if (sattrfd != -1)
194da6c28aaSamw 			(void) close(sattrfd);
195da6c28aaSamw 		if (tattrfd != -1)
196da6c28aaSamw 			(void) close(tattrfd);
197da6c28aaSamw 		if (asfd != -1)
198da6c28aaSamw 			(void) close(asfd);
199da6c28aaSamw 		if (atfd != -1)
200da6c28aaSamw 			(void) close(atfd);
201da6c28aaSamw 		if (res != NULL) {
202da6c28aaSamw 			nvlist_free(res);
203da6c28aaSamw 			res = NULL;
204da6c28aaSamw 		}
205da6c28aaSamw 	}
206da6c28aaSamw 	errno = 0;
207da6c28aaSamw 	/* Copy extended system attribute from source to target */
208da6c28aaSamw 
209da6c28aaSamw 	if (response != NULL) {
210da6c28aaSamw 		if (renameat(indfd, VIEW_READWRITE, outdfd,
211da6c28aaSamw 		    VIEW_READWRITE) == 0)
212da6c28aaSamw 			goto done;
213da6c28aaSamw 
214da6c28aaSamw 		if (errno == EPERM)
215da6c28aaSamw 			etext = dgettext(TEXT_DOMAIN, "Permission denied");
216da6c28aaSamw 		else
217da6c28aaSamw 			etext = dgettext(TEXT_DOMAIN,
218da6c28aaSamw 			    "failed to move system attribute");
219da6c28aaSamw 	}
220da6c28aaSamw error:
221aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(res);
222da6c28aaSamw 	if (silent == 0 && etext != NULL) {
223da6c28aaSamw 		if (!sattr)
224da6c28aaSamw 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
225da6c28aaSamw 			    "%s: %s: cannot move extended attributes, "),
226da6c28aaSamw 			    cmd, infile);
227da6c28aaSamw 		else
228da6c28aaSamw 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
229da6c28aaSamw 			    "%s: %s: cannot move extended system "
230da6c28aaSamw 			    "attributes, "), cmd, infile);
231da6c28aaSamw 		perror(etext);
232da6c28aaSamw 	}
233da6c28aaSamw done:
234da6c28aaSamw 	if (dirp)
235da6c28aaSamw 		(void) closedir(dirp);
236da6c28aaSamw 	if (sattrfd != -1)
237da6c28aaSamw 		(void) close(sattrfd);
238da6c28aaSamw 	if (tattrfd != -1)
239da6c28aaSamw 		(void) close(tattrfd);
240da6c28aaSamw 	if (asfd != -1)
241da6c28aaSamw 		(void) close(asfd);
242da6c28aaSamw 	if (atfd != -1)
243da6c28aaSamw 		(void) close(atfd);
244da6c28aaSamw 	if (indfd != -1)
245da6c28aaSamw 		(void) close(indfd);
246da6c28aaSamw 	if (outdfd != -1)
247da6c28aaSamw 		(void) close(outdfd);
248aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(response);
249da6c28aaSamw 	if (etext != NULL)
250da6c28aaSamw 		return (1);
251da6c28aaSamw 	else
252da6c28aaSamw 		return (0);
253da6c28aaSamw }
254da6c28aaSamw 
255da6c28aaSamw /*
256da6c28aaSamw  * The function returns non default extended system attribute list
257da6c28aaSamw  * associated with 'fname' and returns NULL when an error has occured
258da6c28aaSamw  * or when only extended system attributes other than archive,
259da6c28aaSamw  * av_modified or crtime are set.
260da6c28aaSamw  *
261da6c28aaSamw  * The function returns system attribute list for the following cases:
262da6c28aaSamw  *
263da6c28aaSamw  *	- any extended system attribute other than the default attributes
264da6c28aaSamw  *	  ('archive', 'av_modified' and 'crtime') is set
265da6c28aaSamw  *	- nvlist has NULL name string
266da6c28aaSamw  *	- nvpair has data type of 'nvlist'
267da6c28aaSamw  *	- default data type.
268da6c28aaSamw  */
269da6c28aaSamw 
270da6c28aaSamw nvlist_t *
sysattr_list(char * cmd,int fd,char * fname)271da6c28aaSamw sysattr_list(char *cmd, int fd, char *fname)
272da6c28aaSamw {
273da6c28aaSamw 	boolean_t	value;
274da6c28aaSamw 	data_type_t	type;
275da6c28aaSamw 	nvlist_t	*response;
276da6c28aaSamw 	nvpair_t	*pair;
277da6c28aaSamw 	f_attr_t	fattr;
278da6c28aaSamw 	char		*name;
279da6c28aaSamw 
280da6c28aaSamw 	if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
281da6c28aaSamw 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
282da6c28aaSamw 		    "%s: %s: fgetattr failed\n"),
283da6c28aaSamw 		    cmd, fname);
284da6c28aaSamw 		return (NULL);
285da6c28aaSamw 	}
286da6c28aaSamw 	pair = NULL;
287da6c28aaSamw 	while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
288da6c28aaSamw 
289da6c28aaSamw 		name = nvpair_name(pair);
290da6c28aaSamw 
291da6c28aaSamw 		if (name != NULL)
292da6c28aaSamw 			fattr = name_to_attr(name);
293da6c28aaSamw 		else
294da6c28aaSamw 			return (response);
295da6c28aaSamw 
296da6c28aaSamw 		type = nvpair_type(pair);
297da6c28aaSamw 		switch (type) {
298da6c28aaSamw 			case DATA_TYPE_BOOLEAN_VALUE:
299da6c28aaSamw 				if (nvpair_value_boolean_value(pair,
300da6c28aaSamw 				    &value) != 0) {
301da6c28aaSamw 					(void) fprintf(stderr,
302da6c28aaSamw 					    dgettext(TEXT_DOMAIN, "%s "
303da6c28aaSamw 					    "nvpair_value_boolean_value "
304da6c28aaSamw 					    "failed\n"), cmd);
305da6c28aaSamw 					continue;
306da6c28aaSamw 				}
307da6c28aaSamw 				if (value && fattr != F_ARCHIVE &&
308da6c28aaSamw 				    fattr != F_AV_MODIFIED)
309da6c28aaSamw 					return (response);
310da6c28aaSamw 				break;
311da6c28aaSamw 			case DATA_TYPE_UINT64_ARRAY:
312da6c28aaSamw 				if (fattr != F_CRTIME)
313da6c28aaSamw 					return (response);
314da6c28aaSamw 				break;
315da6c28aaSamw 			case DATA_TYPE_NVLIST:
316da6c28aaSamw 			default:
317da6c28aaSamw 				return (response);
318da6c28aaSamw 		}
319da6c28aaSamw 	}
320aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(response);
321da6c28aaSamw 	return (NULL);
322da6c28aaSamw }
323