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