]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libarchive/archive_read_disk_entry_from_file.c
Merge from libarchive.googlecode.com:
[FreeBSD/FreeBSD.git] / lib / libarchive / archive_read_disk_entry_from_file.c
1 /*-
2  * Copyright (c) 2003-2009 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD$");
28
29 #ifdef HAVE_SYS_TYPES_H
30 /* Mac OSX requires sys/types.h before sys/acl.h. */
31 #include <sys/types.h>
32 #endif
33 #ifdef HAVE_SYS_ACL_H
34 #include <sys/acl.h>
35 #endif
36 #ifdef HAVE_SYS_EXTATTR_H
37 #include <sys/extattr.h>
38 #endif
39 #ifdef HAVE_SYS_PARAM_H
40 #include <sys/param.h>
41 #endif
42 #ifdef HAVE_SYS_STAT_H
43 #include <sys/stat.h>
44 #endif
45 #ifdef HAVE_ACL_LIBACL_H
46 #include <acl/libacl.h>
47 #endif
48 #ifdef HAVE_ATTR_XATTR_H
49 #include <attr/xattr.h>
50 #endif
51 #ifdef HAVE_ERRNO_H
52 #include <errno.h>
53 #endif
54 #ifdef HAVE_LIMITS_H
55 #include <limits.h>
56 #endif
57 #ifdef HAVE_WINDOWS_H
58 #include <windows.h>
59 #endif
60
61 #include "archive.h"
62 #include "archive_entry.h"
63 #include "archive_private.h"
64 #include "archive_read_disk_private.h"
65
66 /*
67  * Linux and FreeBSD plug this obvious hole in POSIX.1e in
68  * different ways.
69  */
70 #if HAVE_ACL_GET_PERM
71 #define ACL_GET_PERM acl_get_perm
72 #elif HAVE_ACL_GET_PERM_NP
73 #define ACL_GET_PERM acl_get_perm_np
74 #endif
75
76 static int setup_acls_posix1e(struct archive_read_disk *,
77     struct archive_entry *, int fd);
78 static int setup_xattrs(struct archive_read_disk *,
79     struct archive_entry *, int fd);
80
81 int
82 archive_read_disk_entry_from_file(struct archive *_a,
83     struct archive_entry *entry,
84     int fd, const struct stat *st)
85 {
86         struct archive_read_disk *a = (struct archive_read_disk *)_a;
87         const char *path;
88         struct stat s;
89         int initial_fd = fd;
90         int r, r1;
91
92         path = archive_entry_sourcepath(entry);
93         if (path == NULL)
94                 path = archive_entry_pathname(entry);
95
96 #ifdef EXT2_IOC_GETFLAGS
97         /* Linux requires an extra ioctl to pull the flags.  Although
98          * this is an extra step, it has a nice side-effect: We get an
99          * open file descriptor which we can use in the subsequent lookups. */
100         if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
101                 if (fd < 0)
102                         fd = open(pathname, O_RDONLY | O_NONBLOCK);
103                 if (fd >= 0) {
104                         unsigned long stflags;
105                         int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
106                         if (r == 0 && stflags != 0)
107                                 archive_entry_set_fflags(entry, stflags, 0);
108                 }
109         }
110 #endif
111
112         if (st == NULL) {
113 #if HAVE_FSTAT
114                 if (fd >= 0) {
115                         if (fstat(fd, &s) != 0)
116                                 return (ARCHIVE_FATAL);
117                 } else
118 #endif
119 #if HAVE_LSTAT
120                 if (!a->follow_symlinks) {
121                         if (lstat(path, &s) != 0)
122                                 return (ARCHIVE_FATAL);
123                 } else
124 #endif
125                 if (stat(path, &s) != 0)
126                         return (ARCHIVE_FATAL);
127                 st = &s;
128         }
129         archive_entry_copy_stat(entry, st);
130
131 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
132         /* On FreeBSD, we get flags for free with the stat. */
133         /* TODO: Does this belong in copy_stat()? */
134         if (st->st_flags != 0)
135                 archive_entry_set_fflags(entry, st->st_flags, 0);
136 #endif
137
138 #ifdef HAVE_READLINK
139         if (S_ISLNK(st->st_mode)) {
140                 char linkbuffer[PATH_MAX + 1];
141                 int lnklen = readlink(path, linkbuffer, PATH_MAX);
142                 if (lnklen < 0) {
143                         archive_set_error(&a->archive, errno,
144                             "Couldn't read link data");
145                         return (ARCHIVE_WARN);
146                 }
147                 linkbuffer[lnklen] = 0;
148                 archive_entry_set_symlink(entry, linkbuffer);
149         }
150 #endif
151
152         r = setup_acls_posix1e(a, entry, fd);
153         r1 = setup_xattrs(a, entry, fd);
154         if (r1 < r)
155                 r = r1;
156         /* If we opened the file earlier in this function, close it. */
157         if (initial_fd != fd)
158                 close(fd);
159         return (r);
160 }
161
162 #ifdef HAVE_POSIX_ACL
163 static void setup_acl_posix1e(struct archive_read_disk *a,
164     struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);
165
166 static int
167 setup_acls_posix1e(struct archive_read_disk *a,
168     struct archive_entry *entry, int fd)
169 {
170         const char      *accpath;
171         acl_t            acl;
172
173         accpath = archive_entry_sourcepath(entry);
174         if (accpath == NULL)
175                 accpath = archive_entry_pathname(entry);
176
177         archive_entry_acl_clear(entry);
178
179         /* Retrieve access ACL from file. */
180         if (fd >= 0)
181                 acl = acl_get_fd(fd);
182 #if HAVE_ACL_GET_LINK_NP
183         else if (!a->follow_symlinks)
184                 acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
185 #endif
186         else
187                 acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
188         if (acl != NULL) {
189                 setup_acl_posix1e(a, entry, acl,
190                     ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
191                 acl_free(acl);
192         }
193
194         /* Only directories can have default ACLs. */
195         if (S_ISDIR(archive_entry_mode(entry))) {
196                 acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
197                 if (acl != NULL) {
198                         setup_acl_posix1e(a, entry, acl,
199                             ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
200                         acl_free(acl);
201                 }
202         }
203         return (ARCHIVE_OK);
204 }
205
206 /*
207  * Translate POSIX.1e ACL into libarchive internal structure.
208  */
209 static void
210 setup_acl_posix1e(struct archive_read_disk *a,
211     struct archive_entry *entry, acl_t acl, int archive_entry_acl_type)
212 {
213         acl_tag_t        acl_tag;
214         acl_entry_t      acl_entry;
215         acl_permset_t    acl_permset;
216         int              s, ae_id, ae_tag, ae_perm;
217         const char      *ae_name;
218
219         s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
220         while (s == 1) {
221                 ae_id = -1;
222                 ae_name = NULL;
223
224                 acl_get_tag_type(acl_entry, &acl_tag);
225                 if (acl_tag == ACL_USER) {
226                         ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry);
227                         ae_name = archive_read_disk_uname(&a->archive, ae_id);
228                         ae_tag = ARCHIVE_ENTRY_ACL_USER;
229                 } else if (acl_tag == ACL_GROUP) {
230                         ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry);
231                         ae_name = archive_read_disk_gname(&a->archive, ae_id);
232                         ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
233                 } else if (acl_tag == ACL_MASK) {
234                         ae_tag = ARCHIVE_ENTRY_ACL_MASK;
235                 } else if (acl_tag == ACL_USER_OBJ) {
236                         ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
237                 } else if (acl_tag == ACL_GROUP_OBJ) {
238                         ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
239                 } else if (acl_tag == ACL_OTHER) {
240                         ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
241                 } else {
242                         /* Skip types that libarchive can't support. */
243                         continue;
244                 }
245
246                 acl_get_permset(acl_entry, &acl_permset);
247                 ae_perm = 0;
248                 /*
249                  * acl_get_perm() is spelled differently on different
250                  * platforms; see above.
251                  */
252                 if (ACL_GET_PERM(acl_permset, ACL_EXECUTE))
253                         ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
254                 if (ACL_GET_PERM(acl_permset, ACL_READ))
255                         ae_perm |= ARCHIVE_ENTRY_ACL_READ;
256                 if (ACL_GET_PERM(acl_permset, ACL_WRITE))
257                         ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;
258
259                 archive_entry_acl_add_entry(entry,
260                     archive_entry_acl_type, ae_perm, ae_tag,
261                     ae_id, ae_name);
262
263                 s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
264         }
265 }
266 #else
267 static int
268 setup_acls_posix1e(struct archive_read_disk *a,
269     struct archive_entry *entry, int fd)
270 {
271         (void)a;      /* UNUSED */
272         (void)entry;  /* UNUSED */
273         (void)fd;     /* UNUSED */
274         return (ARCHIVE_OK);
275 }
276 #endif
277
278 #if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR
279
280 /*
281  * Linux extended attribute support.
282  *
283  * TODO:  By using a stack-allocated buffer for the first
284  * call to getxattr(), we might be able to avoid the second
285  * call entirely.  We only need the second call if the
286  * stack-allocated buffer is too small.  But a modest buffer
287  * of 1024 bytes or so will often be big enough.  Same applies
288  * to listxattr().
289  */
290
291
292 static int
293 setup_xattr(struct archive_read_disk *a,
294     struct archive_entry *entry, const char *name, int fd)
295 {
296         ssize_t size;
297         void *value = NULL;
298         const char *accpath;
299
300         (void)fd; /* UNUSED */
301
302         accpath = archive_entry_sourcepath(entry);
303         if (accpath == NULL)
304                 accpath = archive_entry_pathname(entry);
305
306         if (!a->follow_symlinks)
307                 size = lgetxattr(accpath, name, NULL, 0);
308         else
309                 size = getxattr(accpath, name, NULL, 0);
310
311         if (size == -1) {
312                 archive_set_error(&a->archive, errno,
313                     "Couldn't query extended attribute");
314                 return (ARCHIVE_WARN);
315         }
316
317         if (size > 0 && (value = malloc(size)) == NULL) {
318                 archive_set_error(&a->archive, errno, "Out of memory");
319                 return (ARCHIVE_FATAL);
320         }
321
322         if (!a->follow_symlinks)
323                 size = lgetxattr(accpath, name, value, size);
324         else
325                 size = getxattr(accpath, name, value, size);
326
327         if (size == -1) {
328                 archive_set_error(&a->archive, errno,
329                     "Couldn't read extended attribute");
330                 return (ARCHIVE_WARN);
331         }
332
333         archive_entry_xattr_add_entry(entry, name, value, size);
334
335         free(value);
336         return (ARCHIVE_OK);
337 }
338
339 static int
340 setup_xattrs(struct archive_read_disk *a,
341     struct archive_entry *entry, int fd)
342 {
343         char *list, *p;
344         const char *path;
345         ssize_t list_size;
346
347
348         path = archive_entry_sourcepath(entry);
349         if (path == NULL)
350                 path = archive_entry_pathname(entry);
351
352         if (!a->follow_symlinks)
353                 list_size = llistxattr(path, NULL, 0);
354         else
355                 list_size = listxattr(path, NULL, 0);
356
357         if (list_size == -1) {
358                 if (errno == ENOTSUP)
359                         return (ARCHIVE_OK);
360                 archive_set_error(&a->archive, errno,
361                         "Couldn't list extended attributes");
362                 return (ARCHIVE_WARN);
363         }
364
365         if (list_size == 0)
366                 return (ARCHIVE_OK);
367
368         if ((list = malloc(list_size)) == NULL) {
369                 archive_set_error(&a->archive, errno, "Out of memory");
370                 return (ARCHIVE_FATAL);
371         }
372
373         if (!a->follow_symlinks)
374                 list_size = llistxattr(path, list, list_size);
375         else
376                 list_size = listxattr(path, list, list_size);
377
378         if (list_size == -1) {
379                 archive_set_error(&a->archive, errno,
380                         "Couldn't retrieve extended attributes");
381                 free(list);
382                 return (ARCHIVE_WARN);
383         }
384
385         for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
386                 if (strncmp(p, "system.", 7) == 0 ||
387                                 strncmp(p, "xfsroot.", 8) == 0)
388                         continue;
389                 setup_xattr(a, entry, p, fd);
390         }
391
392         free(list);
393         return (ARCHIVE_OK);
394 }
395
396 #elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE
397
398 /*
399  * FreeBSD extattr interface.
400  */
401
402 /* TODO: Implement this.  Follow the Linux model above, but
403  * with FreeBSD-specific system calls, of course.  Be careful
404  * to not include the system extattrs that hold ACLs; we handle
405  * those separately.
406  */
407 int
408 setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
409     int namespace, const char *name, const char *fullname, int fd);
410
411 int
412 setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
413     int namespace, const char *name, const char *fullname, int fd)
414 {
415         ssize_t size;
416         void *value = NULL;
417         const char *accpath;
418
419         (void)fd; /* UNUSED */
420
421         accpath = archive_entry_sourcepath(entry);
422         if (accpath == NULL)
423                 accpath = archive_entry_pathname(entry);
424
425         if (!a->follow_symlinks)
426                 size = extattr_get_link(accpath, namespace, name, NULL, 0);
427         else
428                 size = extattr_get_file(accpath, namespace, name, NULL, 0);
429
430         if (size == -1) {
431                 archive_set_error(&a->archive, errno,
432                     "Couldn't query extended attribute");
433                 return (ARCHIVE_WARN);
434         }
435
436         if (size > 0 && (value = malloc(size)) == NULL) {
437                 archive_set_error(&a->archive, errno, "Out of memory");
438                 return (ARCHIVE_FATAL);
439         }
440
441         if (!a->follow_symlinks)
442                 size = extattr_get_link(accpath, namespace, name, value, size);
443         else
444                 size = extattr_get_file(accpath, namespace, name, value, size);
445
446         if (size == -1) {
447                 archive_set_error(&a->archive, errno,
448                     "Couldn't read extended attribute");
449                 return (ARCHIVE_WARN);
450         }
451
452         archive_entry_xattr_add_entry(entry, fullname, value, size);
453
454         free(value);
455         return (ARCHIVE_OK);
456 }
457
458 static int
459 setup_xattrs(struct archive_read_disk *a,
460     struct archive_entry *entry, int fd)
461 {
462         char buff[512];
463         char *list, *p;
464         ssize_t list_size;
465         const char *path;
466         int namespace = EXTATTR_NAMESPACE_USER;
467
468         path = archive_entry_sourcepath(entry);
469         if (path == NULL)
470                 path = archive_entry_pathname(entry);
471
472         if (!a->follow_symlinks)
473                 list_size = extattr_list_link(path, namespace, NULL, 0);
474         else
475                 list_size = extattr_list_file(path, namespace, NULL, 0);
476
477         if (list_size == -1 && errno == EOPNOTSUPP)
478                 return (ARCHIVE_OK);
479         if (list_size == -1) {
480                 archive_set_error(&a->archive, errno,
481                         "Couldn't list extended attributes");
482                 return (ARCHIVE_WARN);
483         }
484
485         if (list_size == 0)
486                 return (ARCHIVE_OK);
487
488         if ((list = malloc(list_size)) == NULL) {
489                 archive_set_error(&a->archive, errno, "Out of memory");
490                 return (ARCHIVE_FATAL);
491         }
492
493         if (!a->follow_symlinks)
494                 list_size = extattr_list_link(path, namespace, list, list_size);
495         else
496                 list_size = extattr_list_file(path, namespace, list, list_size);
497
498         if (list_size == -1) {
499                 archive_set_error(&a->archive, errno,
500                         "Couldn't retrieve extended attributes");
501                 free(list);
502                 return (ARCHIVE_WARN);
503         }
504
505         p = list;
506         while ((p - list) < list_size) {
507                 size_t len = 255 & (int)*p;
508                 char *name;
509
510                 strcpy(buff, "user.");
511                 name = buff + strlen(buff);
512                 memcpy(name, p + 1, len);
513                 name[len] = '\0';
514                 setup_xattr(a, entry, namespace, name, buff, fd);
515                 p += 1 + len;
516         }
517
518         free(list);
519         return (ARCHIVE_OK);
520 }
521
522 #else
523
524 /*
525  * Generic (stub) extended attribute support.
526  */
527 static int
528 setup_xattrs(struct archive_read_disk *a,
529     struct archive_entry *entry, int fd)
530 {
531         (void)a;     /* UNUSED */
532         (void)entry; /* UNUSED */
533         (void)fd;    /* UNUSED */
534         return (ARCHIVE_OK);
535 }
536
537 #endif