]> git.saurik.com Git - apple/copyfile.git/blob - xattr_flags.c
copyfile-173.40.2.tar.gz
[apple/copyfile.git] / xattr_flags.c
1 /*
2 * Copyright (c) 2013 Apple, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <err.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/xattr.h>
32 #include <dispatch/dispatch.h>
33 #include <xpc/private.h>
34
35 #include <xattr_flags.h>
36
37 #define FLAG_DELIM_CHAR '#'
38 #define FLAG_DELIM_STR "#"
39
40 /*
41 * Some default propeteries for EAs we know about internally.
42 */
43 struct defaultList {
44 const char *eaName;
45 const char *propList;
46 int flags; // See below
47 };
48
49 #define propFlagsPrefix 0x0001 // The name is a prefix, so only look at that part
50
51 static const struct defaultList *defaultPropertyTable = NULL;
52
53 static const struct defaultList
54 defaultUnboxedPropertyTable[] = {
55 { "com.apple.quarantine", "PCS", 0 }, // not public
56 { "com.apple.TextEncoding", "CS", 0 }, // Content-dependent, public
57 { "com.apple.metadata:", "PS", propFlagsPrefix }, // Don't export, keep for copy & safe save
58 { "com.apple.security.", "S", propFlagsPrefix },
59 { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save
60 { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork
61 { "com.apple.root.installed", "PC", 0}, // Don't share or sync. Copyable by entitled callers
62 { 0, 0, 0 },
63 };
64
65 static const struct defaultList
66 defaultSandboxedPropertyTable[] = {
67 { "com.apple.quarantine", "PCS", 0 }, // not public
68 { "com.apple.TextEncoding", "CS", 0 }, // Content-dependent, public
69 { "com.apple.metadata:", "PS", propFlagsPrefix }, // Don't export, keep for copy & safe save
70 { "com.apple.security.", "N", propFlagsPrefix },
71 { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save
72 { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork
73 { "com.apple.root.installed", "PC", 0}, // Don't share or sync. Copyable by entitled callers
74 { 0, 0, 0 },
75 };
76
77 /*
78 * The property lists on an EA are set by having a suffix character,
79 * and then a list of characters. In general, we're choosing upper-case
80 * to indicate the property is set, and lower-case to indicate it's to be
81 * cleared.
82 */
83 struct propertyListMapping {
84 char enable; // Character to enable
85 char disable; // Character to disable -- usually lower-case of enable
86 xattr_operation_intent_t value;
87 };
88 static const struct propertyListMapping
89 PropertyListMapTable[] = {
90 { 'C', 'c', XATTR_FLAG_CONTENT_DEPENDENT },
91 { 'P', 'p', XATTR_FLAG_NO_EXPORT },
92 { 'N', 'n', XATTR_FLAG_NEVER_PRESERVE },
93 { 'S', 's', XATTR_FLAG_SYNCABLE },
94 { 0, 0, 0 },
95 };
96
97 /*
98 * Given a converted property list (that is, converted to the
99 * xattr_operation_intent_t type), and an intent, determine if
100 * it should be preserved or not.
101 *
102 * I've chosen to use a block instead of a simple mask on the belief
103 * that the question may be moderately complex. If it ends up not being
104 * so, then this can simply be turned into a mask of which bits to check
105 * as being exclusionary.
106 */
107 static const struct divineIntent {
108 xattr_operation_intent_t intent;
109 int (^checker)(xattr_flags_t);
110 } intentTable[] = {
111 { XATTR_OPERATION_INTENT_COPY, ^(xattr_flags_t flags) {
112 if (flags & XATTR_FLAG_NEVER_PRESERVE)
113 return 0;
114 return 1;
115 } },
116 { XATTR_OPERATION_INTENT_SAVE, ^(xattr_flags_t flags) {
117 if (flags & (XATTR_FLAG_CONTENT_DEPENDENT | XATTR_FLAG_NEVER_PRESERVE))
118 return 0;
119 return 1;
120 } },
121 { XATTR_OPERATION_INTENT_SHARE, ^(xattr_flags_t flags) {
122 if ((flags & (XATTR_FLAG_NO_EXPORT | XATTR_FLAG_NEVER_PRESERVE)) != 0)
123 return 0;
124 return 1;
125 } },
126 { XATTR_OPERATION_INTENT_SYNC, ^(xattr_flags_t flags) {
127 return (flags & (XATTR_FLAG_SYNCABLE | XATTR_FLAG_NEVER_PRESERVE)) == XATTR_FLAG_SYNCABLE;
128 } },
129 { 0, 0 },
130 };
131
132
133 /*
134 * If an EA name is in the default list, find it, and return the property
135 * list string for it.
136 */
137 static const char *
138 nameInDefaultList(const char *eaname)
139 {
140 const struct defaultList *retval;
141 static dispatch_once_t onceToken;
142
143 dispatch_once(&onceToken, ^{
144 if (_xpc_runtime_is_app_sandboxed()) {
145 defaultPropertyTable = defaultSandboxedPropertyTable;
146 } else {
147 defaultPropertyTable = defaultUnboxedPropertyTable;
148 }
149 });
150
151 for (retval = defaultPropertyTable; retval->eaName; retval++) {
152 if ((retval->flags & propFlagsPrefix) != 0 &&
153 strncmp(retval->eaName, eaname, strlen(retval->eaName)) == 0)
154 return retval->propList;
155 if (strcmp(retval->eaName, eaname) == 0)
156 return retval->propList;
157 }
158 return NULL;
159 }
160
161 /*
162 * Given an EA name, see if it has a property list in it, and
163 * return a pointer to it. All this is doing is looking for
164 * the delimiter, and returning the string after that. Returns
165 * NULL if the delimiter isn't found. Note that an empty string
166 * is a valid property list, as far as we're concerned.
167 */
168 static const char *
169 findPropertyList(const char *eaname)
170 {
171 const char *ptr = strrchr(eaname, '#');
172 if (ptr)
173 return ptr+1;
174 return NULL;
175 }
176
177 /*
178 * Convert a property list string (e.g., "pCd") into a
179 * xattr_operation_intent_t type.
180 */
181 static xattr_operation_intent_t
182 stringToProperties(const char *proplist)
183 {
184 xattr_operation_intent_t retval = 0;
185 const char *ptr;
186
187 // A switch would be more efficient, but less generic.
188 for (ptr = proplist; *ptr; ptr++) {
189 const struct propertyListMapping *mapPtr;
190 for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) {
191 if (*ptr == mapPtr->enable) {
192 retval |= mapPtr->value;
193 } else if (*ptr == mapPtr->disable) {
194 retval &= ~mapPtr->value;
195 }
196 }
197 }
198 return retval;
199 }
200
201 /*
202 * Given an EA name (e.g., "com.apple.lfs.hfs.test"), and a
203 * xattr_operation_intent_t value (it's currently an integral value, so
204 * just a bitmask), cycle through the list of known properties, and return
205 * a string with the EA name, and the property list appended. E.g., we
206 * might return "com.apple.lfs.hfs.test#pD".
207 *
208 * The tricky part of this funciton is that it will not append any letters
209 * if the value is only the default properites. In that case, it will copy
210 * the EA name, and return that.
211 *
212 * It returns NULL if there was an error. The two errors right now are
213 * no memory (strdup failed), in which case it will set errno to ENOMEM; and
214 * the resulting EA name is longer than XATTR_MAXNAMELEN, in which case it
215 * sets errno to ENAMETOOLONG.
216 *
217 * (Note that it also uses ENAMETOOLONG if the buffer it's trying to set
218 * gets too large. I honestly can't see how that would happen, but it's there
219 * for sanity checking. That would require having more than 64 bits to use.)
220 */
221 char *
222 xattr_name_with_flags(const char *orig, xattr_flags_t propList)
223 {
224 char *retval = NULL;
225 char suffix[66] = { 0 }; // 66: uint64_t for property types, plus '#', plus NUL
226 char *cur = suffix;
227 const struct propertyListMapping *mapPtr;
228
229 *cur++ = '#';
230 for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) {
231 if ((propList & mapPtr->value) != 0) {
232 *cur++ = mapPtr->enable;
233 }
234 if (cur >= (suffix + sizeof(suffix))) {
235 errno = ENAMETOOLONG;
236 return NULL;
237 }
238
239 }
240
241
242 if (cur == suffix + 1) {
243 // No changes made
244 retval = strdup(orig);
245 if (retval == NULL)
246 errno = ENOMEM;
247 } else {
248 const char *defaultEntry = NULL;
249 if ((defaultEntry = nameInDefaultList(orig)) != NULL &&
250 strcmp(defaultEntry, suffix + 1) == 0) {
251 // Just use the name passed in
252 retval = strdup(orig);
253 } else {
254 asprintf(&retval, "%s%s", orig, suffix);
255 }
256 if (retval == NULL) {
257 errno = ENOMEM;
258 } else {
259 if (strlen(retval) > XATTR_MAXNAMELEN) {
260 free(retval);
261 retval = NULL;
262 errno = ENAMETOOLONG;
263 }
264 }
265 }
266 return retval;
267 }
268
269 char *
270 xattr_name_without_flags(const char *eaname)
271 {
272 char *retval = NULL;
273 char *tmp;
274
275 if ((tmp = strrchr(eaname, FLAG_DELIM_CHAR)) == NULL) {
276 retval = strdup(eaname);
277 } else {
278 retval = calloc(tmp - eaname + 1, 1);
279 if (retval) {
280 strlcpy(retval, eaname, tmp - eaname + 1);
281 }
282 }
283 if (retval == NULL) {
284 errno = ENOMEM;
285 }
286 return retval;
287 }
288
289 int
290 xattr_intent_with_flags(xattr_operation_intent_t intent, xattr_flags_t flags)
291 {
292 const struct divineIntent *ip;
293
294 for (ip = intentTable; ip->intent; ip++) {
295 if (ip->intent == intent) {
296 return ip->checker(flags);
297 }
298 }
299 if ((flags & XATTR_FLAG_NEVER_PRESERVE) != 0)
300 return 0; // Special case, don't try to copy this one
301
302 return 1; // Default
303 }
304
305 xattr_flags_t
306 xattr_flags_from_name(const char *eaname)
307 {
308 xattr_flags_t retval = 0;
309 const char *propList;
310
311 propList = findPropertyList(eaname);
312 if (propList == NULL) {
313 propList = nameInDefaultList(eaname);
314 }
315 if (propList != NULL) {
316 retval = stringToProperties(propList);
317 }
318
319 return retval;
320 }
321
322 /*
323 * Indicate whether an EA should be preserved, when using the
324 * given intent.
325 *
326 * This returns 0 if it should not be preserved, and 1 if it should.
327 *
328 * It simply looks through the tables we have above, and compares the
329 * xattr_operation_intent_t for the EA with the intent. If the
330 * EA doesn't have any properties, and it's not on the default list, the
331 * default is to preserve it.
332 */
333
334 int
335 xattr_preserve_for_intent(const char *eaname, xattr_operation_intent_t intent)
336 {
337 xattr_flags_t flags = xattr_flags_from_name(eaname);
338
339 return xattr_intent_with_flags(intent, flags);
340 }
341
342 #include "xattr_properties.h"