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: head/lib/libarchive/archive_read_disk_entry_from_file.c 201084 2009-12-28 02:14:09Z kientzle $"); 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_SYS_XATTR_H 46#include <sys/xattr.h> 47#endif 48#ifdef HAVE_ACL_LIBACL_H 49#include <acl/libacl.h> 50#endif 51#ifdef HAVE_ATTR_XATTR_H 52#include <attr/xattr.h> 53#endif 54#ifdef HAVE_ERRNO_H 55#include <errno.h> 56#endif 57#ifdef HAVE_LIMITS_H 58#include <limits.h> 59#endif 60#ifdef HAVE_WINDOWS_H 61#include <windows.h> 62#endif 63 64#include "archive.h" 65#include "archive_entry.h" 66#include "archive_private.h" 67#include "archive_read_disk_private.h" 68 69/* 70 * Linux and FreeBSD plug this obvious hole in POSIX.1e in 71 * different ways. 72 */ 73#if HAVE_ACL_GET_PERM 74#define ACL_GET_PERM acl_get_perm 75#elif HAVE_ACL_GET_PERM_NP 76#define ACL_GET_PERM acl_get_perm_np 77#endif 78 79static int setup_acls_posix1e(struct archive_read_disk *, 80 struct archive_entry *, int fd); 81static int setup_xattrs(struct archive_read_disk *, 82 struct archive_entry *, int fd); 83 84int 85archive_read_disk_entry_from_file(struct archive *_a, 86 struct archive_entry *entry, 87 int fd, const struct stat *st) 88{ 89 struct archive_read_disk *a = (struct archive_read_disk *)_a; 90 const char *path, *name; 91 struct stat s; 92 int initial_fd = fd; 93 int r, r1; 94 95 archive_clear_error(_a); 96 path = archive_entry_sourcepath(entry); 97 if (path == NULL) 98 path = archive_entry_pathname(entry); 99 100#ifdef EXT2_IOC_GETFLAGS 101 /* Linux requires an extra ioctl to pull the flags. Although 102 * this is an extra step, it has a nice side-effect: We get an 103 * open file descriptor which we can use in the subsequent lookups. */ 104 if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { 105 if (fd < 0) 106 fd = open(pathname, O_RDONLY | O_NONBLOCK | O_BINARY); 107 if (fd >= 0) { 108 unsigned long stflags; 109 int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); 110 if (r == 0 && stflags != 0) 111 archive_entry_set_fflags(entry, stflags, 0); 112 } 113 } 114#endif 115 116 if (st == NULL) { 117 /* TODO: On Windows, use GetFileInfoByHandle() here. 118 * Using Windows stat() call is badly broken, but 119 * even the stat() wrapper has problems because 120 * 'struct stat' is broken on Windows. 121 */ 122#if HAVE_FSTAT 123 if (fd >= 0) { 124 if (fstat(fd, &s) != 0) { 125 archive_set_error(&a->archive, errno, 126 "Can't fstat"); 127 return (ARCHIVE_FAILED); 128 } 129 } else 130#endif 131#if HAVE_LSTAT 132 if (!a->follow_symlinks) { 133 if (lstat(path, &s) != 0) { 134 archive_set_error(&a->archive, errno, 135 "Can't lstat %s", path); 136 return (ARCHIVE_FAILED); 137 } 138 } else 139#endif 140 if (stat(path, &s) != 0) { 141 archive_set_error(&a->archive, errno, 142 "Can't lstat %s", path); 143 return (ARCHIVE_FAILED); 144 } 145 st = &s; 146 } 147 archive_entry_copy_stat(entry, st); 148 149 /* Lookup uname/gname */ 150 name = archive_read_disk_uname(_a, archive_entry_uid(entry)); 151 if (name != NULL) 152 archive_entry_copy_uname(entry, name); 153 name = archive_read_disk_gname(_a, archive_entry_gid(entry)); 154 if (name != NULL) 155 archive_entry_copy_gname(entry, name); 156 157#ifdef HAVE_STRUCT_STAT_ST_FLAGS 158 /* On FreeBSD, we get flags for free with the stat. */ 159 /* TODO: Does this belong in copy_stat()? */ 160 if (st->st_flags != 0) 161 archive_entry_set_fflags(entry, st->st_flags, 0); 162#endif 163 164#ifdef HAVE_READLINK 165 if (S_ISLNK(st->st_mode)) { 166 char linkbuffer[PATH_MAX + 1]; 167 int lnklen = readlink(path, linkbuffer, PATH_MAX); 168 if (lnklen < 0) { 169 archive_set_error(&a->archive, errno, 170 "Couldn't read link data"); 171 return (ARCHIVE_FAILED); 172 } 173 linkbuffer[lnklen] = 0; 174 archive_entry_set_symlink(entry, linkbuffer); 175 } 176#endif 177 178 r = setup_acls_posix1e(a, entry, fd); 179 r1 = setup_xattrs(a, entry, fd); 180 if (r1 < r) 181 r = r1; 182 /* If we opened the file earlier in this function, close it. */ 183 if (initial_fd != fd) 184 close(fd); 185 return (r); 186} 187 188#ifdef HAVE_POSIX_ACL 189static void setup_acl_posix1e(struct archive_read_disk *a, 190 struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); 191 192static int 193setup_acls_posix1e(struct archive_read_disk *a, 194 struct archive_entry *entry, int fd) 195{ 196 const char *accpath; 197 acl_t acl; 198 199 accpath = archive_entry_sourcepath(entry); 200 if (accpath == NULL) 201 accpath = archive_entry_pathname(entry); 202 203 archive_entry_acl_clear(entry); 204 205 /* Retrieve access ACL from file. */ 206 if (fd >= 0) 207 acl = acl_get_fd(fd); 208#if HAVE_ACL_GET_LINK_NP 209 else if (!a->follow_symlinks) 210 acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); 211#else 212 else if ((!a->follow_symlinks) 213 && (archive_entry_filetype(entry) == AE_IFLNK)) 214 /* We can't get the ACL of a symlink, so we assume it can't 215 have one. */ 216 acl = NULL; 217#endif 218 else 219 acl = acl_get_file(accpath, ACL_TYPE_ACCESS); 220 if (acl != NULL) { 221 setup_acl_posix1e(a, entry, acl, 222 ARCHIVE_ENTRY_ACL_TYPE_ACCESS); 223 acl_free(acl); 224 } 225 226 /* Only directories can have default ACLs. */ 227 if (S_ISDIR(archive_entry_mode(entry))) { 228 acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); 229 if (acl != NULL) { 230 setup_acl_posix1e(a, entry, acl, 231 ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); 232 acl_free(acl); 233 } 234 } 235 return (ARCHIVE_OK); 236} 237 238/* 239 * Translate POSIX.1e ACL into libarchive internal structure. 240 */ 241static void 242setup_acl_posix1e(struct archive_read_disk *a, 243 struct archive_entry *entry, acl_t acl, int archive_entry_acl_type) 244{ 245 acl_tag_t acl_tag; 246 acl_entry_t acl_entry; 247 acl_permset_t acl_permset; 248 int s, ae_id, ae_tag, ae_perm; 249 const char *ae_name; 250 251 s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); 252 while (s == 1) { 253 ae_id = -1; 254 ae_name = NULL; 255 256 acl_get_tag_type(acl_entry, &acl_tag); 257 if (acl_tag == ACL_USER) { 258 ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); 259 ae_name = archive_read_disk_uname(&a->archive, ae_id); 260 ae_tag = ARCHIVE_ENTRY_ACL_USER; 261 } else if (acl_tag == ACL_GROUP) { 262 ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); 263 ae_name = archive_read_disk_gname(&a->archive, ae_id); 264 ae_tag = ARCHIVE_ENTRY_ACL_GROUP; 265 } else if (acl_tag == ACL_MASK) { 266 ae_tag = ARCHIVE_ENTRY_ACL_MASK; 267 } else if (acl_tag == ACL_USER_OBJ) { 268 ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 269 } else if (acl_tag == ACL_GROUP_OBJ) { 270 ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 271 } else if (acl_tag == ACL_OTHER) { 272 ae_tag = ARCHIVE_ENTRY_ACL_OTHER; 273 } else { 274 /* Skip types that libarchive can't support. */ 275 continue; 276 } 277 278 acl_get_permset(acl_entry, &acl_permset); 279 ae_perm = 0; 280 /* 281 * acl_get_perm() is spelled differently on different 282 * platforms; see above. 283 */ 284 if (ACL_GET_PERM(acl_permset, ACL_EXECUTE)) 285 ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE; 286 if (ACL_GET_PERM(acl_permset, ACL_READ)) 287 ae_perm |= ARCHIVE_ENTRY_ACL_READ; 288 if (ACL_GET_PERM(acl_permset, ACL_WRITE)) 289 ae_perm |= ARCHIVE_ENTRY_ACL_WRITE; 290 291 archive_entry_acl_add_entry(entry, 292 archive_entry_acl_type, ae_perm, ae_tag, 293 ae_id, ae_name); 294 295 s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); 296 } 297} 298#else 299static int 300setup_acls_posix1e(struct archive_read_disk *a, 301 struct archive_entry *entry, int fd) 302{ 303 (void)a; /* UNUSED */ 304 (void)entry; /* UNUSED */ 305 (void)fd; /* UNUSED */ 306 return (ARCHIVE_OK); 307} 308#endif 309 310#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR 311 312/* 313 * Linux extended attribute support. 314 * 315 * TODO: By using a stack-allocated buffer for the first 316 * call to getxattr(), we might be able to avoid the second 317 * call entirely. We only need the second call if the 318 * stack-allocated buffer is too small. But a modest buffer 319 * of 1024 bytes or so will often be big enough. Same applies 320 * to listxattr(). 321 */ 322 323 324static int 325setup_xattr(struct archive_read_disk *a, 326 struct archive_entry *entry, const char *name, int fd) 327{ 328 ssize_t size; 329 void *value = NULL; 330 const char *accpath; 331 332 (void)fd; /* UNUSED */ 333 334 accpath = archive_entry_sourcepath(entry); 335 if (accpath == NULL) 336 accpath = archive_entry_pathname(entry); 337 338 if (!a->follow_symlinks) 339 size = lgetxattr(accpath, name, NULL, 0); 340 else 341 size = getxattr(accpath, name, NULL, 0); 342 343 if (size == -1) { 344 archive_set_error(&a->archive, errno, 345 "Couldn't query extended attribute"); 346 return (ARCHIVE_WARN); 347 } 348 349 if (size > 0 && (value = malloc(size)) == NULL) { 350 archive_set_error(&a->archive, errno, "Out of memory"); 351 return (ARCHIVE_FATAL); 352 } 353 354 if (!a->follow_symlinks) 355 size = lgetxattr(accpath, name, value, size); 356 else 357 size = getxattr(accpath, name, value, size); 358 359 if (size == -1) { 360 archive_set_error(&a->archive, errno, 361 "Couldn't read extended attribute"); 362 return (ARCHIVE_WARN); 363 } 364 365 archive_entry_xattr_add_entry(entry, name, value, size); 366 367 free(value); 368 return (ARCHIVE_OK); 369} 370 371static int 372setup_xattrs(struct archive_read_disk *a, 373 struct archive_entry *entry, int fd) 374{ 375 char *list, *p; 376 const char *path; 377 ssize_t list_size; 378 379 380 path = archive_entry_sourcepath(entry); 381 if (path == NULL) 382 path = archive_entry_pathname(entry); 383 384 if (!a->follow_symlinks) 385 list_size = llistxattr(path, NULL, 0); 386 else 387 list_size = listxattr(path, NULL, 0); 388 389 if (list_size == -1) { 390 if (errno == ENOTSUP) 391 return (ARCHIVE_OK); 392 archive_set_error(&a->archive, errno, 393 "Couldn't list extended attributes"); 394 return (ARCHIVE_WARN); 395 } 396 397 if (list_size == 0) 398 return (ARCHIVE_OK); 399 400 if ((list = malloc(list_size)) == NULL) { 401 archive_set_error(&a->archive, errno, "Out of memory"); 402 return (ARCHIVE_FATAL); 403 } 404 405 if (!a->follow_symlinks) 406 list_size = llistxattr(path, list, list_size); 407 else 408 list_size = listxattr(path, list, list_size); 409 410 if (list_size == -1) { 411 archive_set_error(&a->archive, errno, 412 "Couldn't retrieve extended attributes"); 413 free(list); 414 return (ARCHIVE_WARN); 415 } 416 417 for (p = list; (p - list) < list_size; p += strlen(p) + 1) { 418 if (strncmp(p, "system.", 7) == 0 || 419 strncmp(p, "xfsroot.", 8) == 0) 420 continue; 421 setup_xattr(a, entry, p, fd); 422 } 423 424 free(list); 425 return (ARCHIVE_OK); 426} 427 428#elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE 429 430/* 431 * FreeBSD extattr interface. 432 */ 433 434/* TODO: Implement this. Follow the Linux model above, but 435 * with FreeBSD-specific system calls, of course. Be careful 436 * to not include the system extattrs that hold ACLs; we handle 437 * those separately. 438 */ 439static int 440setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, 441 int namespace, const char *name, const char *fullname, int fd); 442 443static int 444setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, 445 int namespace, const char *name, const char *fullname, int fd) 446{ 447 ssize_t size; 448 void *value = NULL; 449 const char *accpath; 450 451 (void)fd; /* UNUSED */ 452 453 accpath = archive_entry_sourcepath(entry); 454 if (accpath == NULL) 455 accpath = archive_entry_pathname(entry); 456 457 if (!a->follow_symlinks) 458 size = extattr_get_link(accpath, namespace, name, NULL, 0); 459 else 460 size = extattr_get_file(accpath, namespace, name, NULL, 0); 461 462 if (size == -1) { 463 archive_set_error(&a->archive, errno, 464 "Couldn't query extended attribute"); 465 return (ARCHIVE_WARN); 466 } 467 468 if (size > 0 && (value = malloc(size)) == NULL) { 469 archive_set_error(&a->archive, errno, "Out of memory"); 470 return (ARCHIVE_FATAL); 471 } 472 473 if (!a->follow_symlinks) 474 size = extattr_get_link(accpath, namespace, name, value, size); 475 else 476 size = extattr_get_file(accpath, namespace, name, value, size); 477 478 if (size == -1) { 479 archive_set_error(&a->archive, errno, 480 "Couldn't read extended attribute"); 481 return (ARCHIVE_WARN); 482 } 483 484 archive_entry_xattr_add_entry(entry, fullname, value, size); 485 486 free(value); 487 return (ARCHIVE_OK); 488} 489 490static int 491setup_xattrs(struct archive_read_disk *a, 492 struct archive_entry *entry, int fd) 493{ 494 char buff[512]; 495 char *list, *p; 496 ssize_t list_size; 497 const char *path; 498 int namespace = EXTATTR_NAMESPACE_USER; 499 500 path = archive_entry_sourcepath(entry); 501 if (path == NULL) 502 path = archive_entry_pathname(entry); 503 504 if (!a->follow_symlinks) 505 list_size = extattr_list_link(path, namespace, NULL, 0); 506 else 507 list_size = extattr_list_file(path, namespace, NULL, 0); 508 509 if (list_size == -1 && errno == EOPNOTSUPP) 510 return (ARCHIVE_OK); 511 if (list_size == -1) { 512 archive_set_error(&a->archive, errno, 513 "Couldn't list extended attributes"); 514 return (ARCHIVE_WARN); 515 } 516 517 if (list_size == 0) 518 return (ARCHIVE_OK); 519 520 if ((list = malloc(list_size)) == NULL) { 521 archive_set_error(&a->archive, errno, "Out of memory"); 522 return (ARCHIVE_FATAL); 523 } 524 525 if (!a->follow_symlinks) 526 list_size = extattr_list_link(path, namespace, list, list_size); 527 else 528 list_size = extattr_list_file(path, namespace, list, list_size); 529 530 if (list_size == -1) { 531 archive_set_error(&a->archive, errno, 532 "Couldn't retrieve extended attributes"); 533 free(list); 534 return (ARCHIVE_WARN); 535 } 536 537 p = list; 538 while ((p - list) < list_size) { 539 size_t len = 255 & (int)*p; 540 char *name; 541 542 strcpy(buff, "user."); 543 name = buff + strlen(buff); 544 memcpy(name, p + 1, len); 545 name[len] = '\0'; 546 setup_xattr(a, entry, namespace, name, buff, fd); 547 p += 1 + len; 548 } 549 550 free(list); 551 return (ARCHIVE_OK); 552} 553 554#else 555 556/* 557 * Generic (stub) extended attribute support. 558 */ 559static int 560setup_xattrs(struct archive_read_disk *a, 561 struct archive_entry *entry, int fd) 562{ 563 (void)a; /* UNUSED */ 564 (void)entry; /* UNUSED */ 565 (void)fd; /* UNUSED */ 566 return (ARCHIVE_OK); 567} 568 569#endif 570