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