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  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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
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
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 			if (dp->d_name != NULL)
167 				res = sysattr_list(cmd, sattrfd, dp->d_name);
168 
169 			if (res != NULL &&
170 			    get_attrdirs(indfd, outdfd, dp->d_name, &asfd,
171 			    &atfd) != 0) {
172 				etext = dgettext(TEXT_DOMAIN,
173 				    "Failed to open attribute files");
174 				goto error;
175 			}
176 			/*
177 			 * Copy extended system attribute from source
178 			 * attribute file to target attribute file
179 			 */
180 			if (res != NULL &&
181 			    (renameat(asfd, VIEW_READWRITE, atfd,
182 			    VIEW_READWRITE) != 0)) {
183 				if (errno == EPERM)
184 					etext = dgettext(TEXT_DOMAIN,
185 					    "Permission denied -"
186 					    "failed to move system attribute");
187 				else
188 					etext = dgettext(TEXT_DOMAIN,
189 					    "failed to move extended "
190 					    "system attribute");
191 				goto error;
192 			}
193 		}
194 		if (sattrfd != -1)
195 			(void) close(sattrfd);
196 		if (tattrfd != -1)
197 			(void) close(tattrfd);
198 		if (asfd != -1)
199 			(void) close(asfd);
200 		if (atfd != -1)
201 			(void) close(atfd);
202 		if (res != NULL) {
203 			nvlist_free(res);
204 			res = NULL;
205 		}
206 	}
207 	errno = 0;
208 	/* Copy extended system attribute from source to target */
209 
210 	if (response != NULL) {
211 		if (renameat(indfd, VIEW_READWRITE, outdfd,
212 		    VIEW_READWRITE) == 0)
213 			goto done;
214 
215 		if (errno == EPERM)
216 			etext = dgettext(TEXT_DOMAIN, "Permission denied");
217 		else
218 			etext = dgettext(TEXT_DOMAIN,
219 			    "failed to move system attribute");
220 	}
221 error:
222 	if (res != NULL)
223 		nvlist_free(res);
224 	if (silent == 0 && etext != NULL) {
225 		if (!sattr)
226 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
227 			    "%s: %s: cannot move extended attributes, "),
228 			    cmd, infile);
229 		else
230 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
231 			    "%s: %s: cannot move extended system "
232 			    "attributes, "), cmd, infile);
233 		perror(etext);
234 	}
235 done:
236 	if (dirp)
237 		(void) closedir(dirp);
238 	if (sattrfd != -1)
239 		(void) close(sattrfd);
240 	if (tattrfd != -1)
241 		(void) close(tattrfd);
242 	if (asfd != -1)
243 		(void) close(asfd);
244 	if (atfd != -1)
245 		(void) close(atfd);
246 	if (indfd != -1)
247 		(void) close(indfd);
248 	if (outdfd != -1)
249 		(void) close(outdfd);
250 	if (response != NULL)
251 		nvlist_free(response);
252 	if (etext != NULL)
253 		return (1);
254 	else
255 		return (0);
256 }
257 
258 /*
259  * The function returns non default extended system attribute list
260  * associated with 'fname' and returns NULL when an error has occured
261  * or when only extended system attributes other than archive,
262  * av_modified or crtime are set.
263  *
264  * The function returns system attribute list for the following cases:
265  *
266  *	- any extended system attribute other than the default attributes
267  *	  ('archive', 'av_modified' and 'crtime') is set
268  *	- nvlist has NULL name string
269  *	- nvpair has data type of 'nvlist'
270  *	- default data type.
271  */
272 
273 nvlist_t *
274 sysattr_list(char *cmd, int fd, char *fname)
275 {
276 	boolean_t	value;
277 	data_type_t	type;
278 	nvlist_t	*response;
279 	nvpair_t	*pair;
280 	f_attr_t	fattr;
281 	char		*name;
282 
283 	if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
284 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
285 		    "%s: %s: fgetattr failed\n"),
286 		    cmd, fname);
287 		return (NULL);
288 	}
289 	pair = NULL;
290 	while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
291 
292 		name = nvpair_name(pair);
293 
294 		if (name != NULL)
295 			fattr = name_to_attr(name);
296 		else
297 			return (response);
298 
299 		type = nvpair_type(pair);
300 		switch (type) {
301 			case DATA_TYPE_BOOLEAN_VALUE:
302 				if (nvpair_value_boolean_value(pair,
303 				    &value) != 0) {
304 					(void) fprintf(stderr,
305 					    dgettext(TEXT_DOMAIN, "%s "
306 					    "nvpair_value_boolean_value "
307 					    "failed\n"), cmd);
308 					continue;
309 				}
310 				if (value && fattr != F_ARCHIVE &&
311 				    fattr != F_AV_MODIFIED)
312 					return (response);
313 				break;
314 			case DATA_TYPE_UINT64_ARRAY:
315 				if (fattr != F_CRTIME)
316 					return (response);
317 				break;
318 			case DATA_TYPE_NVLIST:
319 			default:
320 				return (response);
321 				break;
322 		}
323 	}
324 	if (response != NULL)
325 		nvlist_free(response);
326 	return (NULL);
327 }
328