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