1/*- 2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 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 AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29#include <sys/time.h> 30#include <sys/types.h> 31#include <sys/stat.h> 32 33#include <assert.h> 34#include <errno.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <unistd.h> 39 40#include "fattr.h" 41#include "idcache.h" 42#include "misc.h" 43 44/* 45 * Include the appropriate definition for the file attributes we support. 46 * There are two different files: fattr_bsd.h for BSD-like systems that 47 * support the extended file flags a la chflags() and fattr_posix.h for 48 * bare POSIX systems that don't. 49 */ 50#ifdef HAVE_FFLAGS 51#include "fattr_bsd.h" 52#else 53#include "fattr_posix.h" 54#endif 55 56#ifdef __FreeBSD__ 57#include <osreldate.h> 58#endif 59 60/* Define fflags_t if we're on a system that doesn't have it. */ 61#if !defined(__FreeBSD_version) || __FreeBSD_version < 500030 62typedef uint32_t fflags_t; 63#endif 64 65#define FA_MASKRADIX 16 66#define FA_FILETYPERADIX 10 67#define FA_MODTIMERADIX 10 68#define FA_SIZERADIX 10 69#define FA_RDEVRADIX 16 70#define FA_MODERADIX 8 71#define FA_FLAGSRADIX 16 72#define FA_LINKCOUNTRADIX 10 73#define FA_DEVRADIX 16 74#define FA_INODERADIX 10 75 76#define FA_PERMMASK (S_IRWXU | S_IRWXG | S_IRWXO) 77#define FA_SETIDMASK (S_ISUID | S_ISGID | S_ISVTX) 78 79struct fattr { 80 int mask; 81 int type; 82 time_t modtime; 83 off_t size; 84 char *linktarget; 85 dev_t rdev; 86 uid_t uid; 87 gid_t gid; 88 mode_t mode; 89 fflags_t flags; 90 nlink_t linkcount; 91 dev_t dev; 92 ino_t inode; 93}; 94 95static const struct fattr bogus = { 96 FA_MODTIME | FA_SIZE | FA_MODE, 97 FT_UNKNOWN, 98 1, 99 0, 100 NULL, 101 0, 102 0, 103 0, 104 0, 105 0, 106 0, 107 0, 108 0 109}; 110 111static struct fattr *defaults[FT_NUMBER]; 112 113void 114fattr_init(void) 115{ 116 struct fattr *fa; 117 int i; 118 119 for (i = 0; i < FT_NUMBER; i++) { 120 fa = fattr_new(i, -1); 121 if (i == FT_DIRECTORY) 122 fa->mode = 0777; 123 else 124 fa->mode = 0666; 125 fa->mask |= FA_MODE; 126 defaults[i] = fa; 127 } 128 /* Initialize the uid/gid lookup cache. */ 129 idcache_init(); 130} 131 132void 133fattr_fini(void) 134{ 135 int i; 136 137 idcache_fini(); 138 for (i = 0; i < FT_NUMBER; i++) 139 fattr_free(defaults[i]); 140} 141 142const struct fattr *fattr_bogus = &bogus; 143 144static char *fattr_scanattr(struct fattr *, int, const char *); 145 146int 147fattr_supported(int type) 148{ 149 150 return (fattr_support[type]); 151} 152 153struct fattr * 154fattr_new(int type, time_t modtime) 155{ 156 struct fattr *new; 157 158 new = xmalloc(sizeof(struct fattr)); 159 memset(new, 0, sizeof(struct fattr)); 160 new->type = type; 161 if (type != FT_UNKNOWN) 162 new->mask |= FA_FILETYPE; 163 if (modtime != -1) { 164 new->modtime = modtime; 165 new->mask |= FA_MODTIME; 166 } 167 if (fattr_supported(new->type) & FA_LINKCOUNT) { 168 new->mask |= FA_LINKCOUNT; 169 new->linkcount = 1; 170 } 171 return (new); 172} 173 174/* Returns a new file attribute structure based on a stat structure. */ 175struct fattr * 176fattr_fromstat(struct stat *sb) 177{ 178 struct fattr *fa; 179 180 fa = fattr_new(FT_UNKNOWN, -1); 181 if (S_ISREG(sb->st_mode)) 182 fa->type = FT_FILE; 183 else if (S_ISDIR(sb->st_mode)) 184 fa->type = FT_DIRECTORY; 185 else if (S_ISCHR(sb->st_mode)) 186 fa->type = FT_CDEV; 187 else if (S_ISBLK(sb->st_mode)) 188 fa->type = FT_BDEV; 189 else if (S_ISLNK(sb->st_mode)) 190 fa->type = FT_SYMLINK; 191 else 192 fa->type = FT_UNKNOWN; 193 194 fa->mask = FA_FILETYPE | fattr_supported(fa->type); 195 if (fa->mask & FA_MODTIME) 196 fa->modtime = sb->st_mtime; 197 if (fa->mask & FA_SIZE) 198 fa->size = sb->st_size; 199 if (fa->mask & FA_RDEV) 200 fa->rdev = sb->st_rdev; 201 if (fa->mask & FA_OWNER) 202 fa->uid = sb->st_uid; 203 if (fa->mask & FA_GROUP) 204 fa->gid = sb->st_gid; 205 if (fa->mask & FA_MODE) 206 fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK); 207#ifdef HAVE_FFLAGS 208 if (fa->mask & FA_FLAGS) 209 fa->flags = sb->st_flags; 210#endif 211 if (fa->mask & FA_LINKCOUNT) 212 fa->linkcount = sb->st_nlink; 213 if (fa->mask & FA_DEV) 214 fa->dev = sb->st_dev; 215 if (fa->mask & FA_INODE) 216 fa->inode = sb->st_ino; 217 return (fa); 218} 219 220struct fattr * 221fattr_frompath(const char *path, int nofollow) 222{ 223 struct fattr *fa; 224 struct stat sb; 225 int error, len; 226 227 if (nofollow) 228 error = lstat(path, &sb); 229 else 230 error = stat(path, &sb); 231 if (error) 232 return (NULL); 233 fa = fattr_fromstat(&sb); 234 if (fa->mask & FA_LINKTARGET) { 235 char buf[1024]; 236 237 len = readlink(path, buf, sizeof(buf)); 238 if (len == -1) { 239 fattr_free(fa); 240 return (NULL); 241 } 242 if ((unsigned)len > sizeof(buf) - 1) { 243 fattr_free(fa); 244 errno = ENAMETOOLONG; 245 return (NULL); 246 } 247 buf[len] = '\0'; 248 fa->linktarget = xstrdup(buf); 249 } 250 return (fa); 251} 252 253struct fattr * 254fattr_fromfd(int fd) 255{ 256 struct fattr *fa; 257 struct stat sb; 258 int error; 259 260 error = fstat(fd, &sb); 261 if (error) 262 return (NULL); 263 fa = fattr_fromstat(&sb); 264 return (fa); 265} 266 267int 268fattr_type(const struct fattr *fa) 269{ 270 271 return (fa->type); 272} 273 274/* Returns a new file attribute structure from its encoded text form. */ 275struct fattr * 276fattr_decode(char *attr) 277{ 278 struct fattr *fa; 279 char *next; 280 281 fa = fattr_new(FT_UNKNOWN, -1); 282 next = fattr_scanattr(fa, FA_MASK, attr); 283 if (next == NULL || (fa->mask & ~FA_MASK) > 0) 284 goto bad; 285 if (fa->mask & FA_FILETYPE) { 286 next = fattr_scanattr(fa, FA_FILETYPE, next); 287 if (next == NULL) 288 goto bad; 289 if (fa->type < 0 || fa->type > FT_MAX) 290 fa->type = FT_UNKNOWN; 291 } else { 292 /* The filetype attribute is always valid. */ 293 fa->mask |= FA_FILETYPE; 294 fa->type = FT_UNKNOWN; 295 } 296 fa->mask = fa->mask & fattr_supported(fa->type); 297 if (fa->mask & FA_MODTIME) 298 next = fattr_scanattr(fa, FA_MODTIME, next); 299 if (fa->mask & FA_SIZE) 300 next = fattr_scanattr(fa, FA_SIZE, next); 301 if (fa->mask & FA_LINKTARGET) 302 next = fattr_scanattr(fa, FA_LINKTARGET, next); 303 if (fa->mask & FA_RDEV) 304 next = fattr_scanattr(fa, FA_RDEV, next); 305 if (fa->mask & FA_OWNER) 306 next = fattr_scanattr(fa, FA_OWNER, next); 307 if (fa->mask & FA_GROUP) 308 next = fattr_scanattr(fa, FA_GROUP, next); 309 if (fa->mask & FA_MODE) 310 next = fattr_scanattr(fa, FA_MODE, next); 311 if (fa->mask & FA_FLAGS) 312 next = fattr_scanattr(fa, FA_FLAGS, next); 313 if (fa->mask & FA_LINKCOUNT) { 314 next = fattr_scanattr(fa, FA_LINKCOUNT, next); 315 } else if (fattr_supported(fa->type) & FA_LINKCOUNT) { 316 /* If the link count is missing but supported, fake it as 1. */ 317 fa->mask |= FA_LINKCOUNT; 318 fa->linkcount = 1; 319 } 320 if (fa->mask & FA_DEV) 321 next = fattr_scanattr(fa, FA_DEV, next); 322 if (fa->mask & FA_INODE) 323 next = fattr_scanattr(fa, FA_INODE, next); 324 if (next == NULL) 325 goto bad; 326 return (fa); 327bad: 328 fattr_free(fa); 329 return (NULL); 330} 331 332char * 333fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore) 334{ 335 struct { 336 char val[32]; 337 char len[4]; 338 int extval; 339 char *ext; 340 } pieces[FA_NUMBER], *piece; 341 char *cp, *s, *username, *groupname; 342 size_t len, vallen; 343 mode_t mode, modemask; 344 int mask, n, i; 345 346 username = NULL; 347 groupname = NULL; 348 if (support == NULL) 349 mask = fa->mask; 350 else 351 mask = fa->mask & support[fa->type]; 352 mask &= ~ignore; 353 if (fa->mask & FA_OWNER) { 354 username = getuserbyid(fa->uid); 355 if (username == NULL) 356 mask &= ~FA_OWNER; 357 } 358 if (fa->mask & FA_GROUP) { 359 groupname = getgroupbyid(fa->gid); 360 if (groupname == NULL) 361 mask &= ~FA_GROUP; 362 } 363 if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1) 364 mask &= ~FA_LINKCOUNT; 365 366 memset(pieces, 0, FA_NUMBER * sizeof(*pieces)); 367 len = 0; 368 piece = pieces; 369 vallen = snprintf(piece->val, sizeof(piece->val), "%x", mask); 370 len += snprintf(piece->len, sizeof(piece->len), "%lld", 371 (long long)vallen) + vallen + 1; 372 piece++; 373 if (mask & FA_FILETYPE) { 374 vallen = snprintf(piece->val, sizeof(piece->val), 375 "%d", fa->type); 376 len += snprintf(piece->len, sizeof(piece->len), "%lld", 377 (long long)vallen) + vallen + 1; 378 piece++; 379 } 380 if (mask & FA_MODTIME) { 381 vallen = snprintf(piece->val, sizeof(piece->val), 382 "%lld", (long long)fa->modtime); 383 len += snprintf(piece->len, sizeof(piece->len), "%lld", 384 (long long)vallen) + vallen + 1; 385 piece++; 386 } 387 if (mask & FA_SIZE) { 388 vallen = snprintf(piece->val, sizeof(piece->val), 389 "%lld", (long long)fa->size); 390 len += snprintf(piece->len, sizeof(piece->len), "%lld", 391 (long long)vallen) + vallen + 1; 392 piece++; 393 } 394 if (mask & FA_LINKTARGET) { 395 vallen = strlen(fa->linktarget); 396 piece->extval = 1; 397 piece->ext = fa->linktarget; 398 len += snprintf(piece->len, sizeof(piece->len), "%lld", 399 (long long)vallen) + vallen + 1; 400 piece++; 401 } 402 if (mask & FA_RDEV) { 403 vallen = snprintf(piece->val, sizeof(piece->val), 404 "%lld", (long long)fa->rdev); 405 len += snprintf(piece->len, sizeof(piece->len), "%lld", 406 (long long)vallen) + vallen + 1; 407 piece++; 408 } 409 if (mask & FA_OWNER) { 410 vallen = strlen(username); 411 piece->extval = 1; 412 piece->ext = username; 413 len += snprintf(piece->len, sizeof(piece->len), "%lld", 414 (long long)vallen) + vallen + 1; 415 piece++; 416 } 417 if (mask & FA_GROUP) { 418 vallen = strlen(groupname); 419 piece->extval = 1; 420 piece->ext = groupname; 421 len += snprintf(piece->len, sizeof(piece->len), "%lld", 422 (long long)vallen) + vallen + 1; 423 piece++; 424 } 425 if (mask & FA_MODE) { 426 if (mask & FA_OWNER && mask & FA_GROUP) 427 modemask = FA_SETIDMASK | FA_PERMMASK; 428 else 429 modemask = FA_PERMMASK; 430 mode = fa->mode & modemask; 431 vallen = snprintf(piece->val, sizeof(piece->val), 432 "%o", mode); 433 len += snprintf(piece->len, sizeof(piece->len), "%lld", 434 (long long)vallen) + vallen + 1; 435 piece++; 436 } 437 if (mask & FA_FLAGS) { 438 vallen = snprintf(piece->val, sizeof(piece->val), "%llx", 439 (long long)fa->flags); 440 len += snprintf(piece->len, sizeof(piece->len), "%lld", 441 (long long)vallen) + vallen + 1; 442 piece++; 443 } 444 if (mask & FA_LINKCOUNT) { 445 vallen = snprintf(piece->val, sizeof(piece->val), "%lld", 446 (long long)fa->linkcount); 447 len += snprintf(piece->len, sizeof(piece->len), "%lld", 448 (long long)vallen) + vallen + 1; 449 piece++; 450 } 451 if (mask & FA_DEV) { 452 vallen = snprintf(piece->val, sizeof(piece->val), "%llx", 453 (long long)fa->dev); 454 len += snprintf(piece->len, sizeof(piece->len), "%lld", 455 (long long)vallen) + vallen + 1; 456 piece++; 457 } 458 if (mask & FA_INODE) { 459 vallen = snprintf(piece->val, sizeof(piece->val), "%lld", 460 (long long)fa->inode); 461 len += snprintf(piece->len, sizeof(piece->len), "%lld", 462 (long long)vallen) + vallen + 1; 463 piece++; 464 } 465 466 s = xmalloc(len + 1); 467 468 n = piece - pieces; 469 piece = pieces; 470 cp = s; 471 for (i = 0; i < n; i++) { 472 if (piece->extval) 473 len = sprintf(cp, "%s#%s", piece->len, piece->ext); 474 else 475 len = sprintf(cp, "%s#%s", piece->len, piece->val); 476 cp += len; 477 piece++; 478 } 479 return (s); 480} 481 482struct fattr * 483fattr_dup(const struct fattr *from) 484{ 485 struct fattr *fa; 486 487 fa = fattr_new(FT_UNKNOWN, -1); 488 fattr_override(fa, from, FA_MASK); 489 return (fa); 490} 491 492void 493fattr_free(struct fattr *fa) 494{ 495 496 if (fa == NULL) 497 return; 498 if (fa->linktarget != NULL) 499 free(fa->linktarget); 500 free(fa); 501} 502 503void 504fattr_umask(struct fattr *fa, mode_t newumask) 505{ 506 507 if (fa->mask & FA_MODE) 508 fa->mode = fa->mode & ~newumask; 509} 510 511void 512fattr_maskout(struct fattr *fa, int mask) 513{ 514 515 /* Don't forget to free() the linktarget attribute if we remove it. */ 516 if (mask & FA_LINKTARGET && fa->mask & FA_LINKTARGET) { 517 free(fa->linktarget); 518 fa->linktarget = NULL; 519 } 520 fa->mask &= ~mask; 521} 522 523int 524fattr_getmask(const struct fattr *fa) 525{ 526 527 return (fa->mask); 528} 529 530nlink_t 531fattr_getlinkcount(const struct fattr *fa) 532{ 533 534 return (fa->linkcount); 535} 536 537char * 538fattr_getlinktarget(const struct fattr *fa) 539{ 540 541 return (fa->linktarget); 542} 543 544/* 545 * Eat the specified attribute and put it in the file attribute 546 * structure. Returns NULL on error, or a pointer to the next 547 * attribute to parse. 548 * 549 * This would be much prettier if we had strntol() so that we're 550 * not forced to write '\0' to the string before calling strtol() 551 * and then put back the old value... 552 * 553 * We need to use (unsigned) long long types here because some 554 * of the opaque types we're parsing (off_t, time_t...) may need 555 * 64bits to fit. 556 */ 557static char * 558fattr_scanattr(struct fattr *fa, int type, const char *attr) 559{ 560 char *attrend, *attrstart, *end; 561 size_t len; 562 unsigned long attrlen; 563 int error; 564 mode_t modemask; 565 char tmp; 566 567 if (attr == NULL) 568 return (NULL); 569 errno = 0; 570 attrlen = strtoul(attr, &end, 10); 571 if (errno || *end != '#') 572 return (NULL); 573 len = strlen(attr); 574 attrstart = end + 1; 575 attrend = attrstart + attrlen; 576 tmp = *attrend; 577 *attrend = '\0'; 578 switch (type) { 579 /* Using FA_MASK here is a bit bogus semantically. */ 580 case FA_MASK: 581 errno = 0; 582 fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX); 583 if (errno || end != attrend) 584 goto bad; 585 break; 586 case FA_FILETYPE: 587 errno = 0; 588 fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX); 589 if (errno || end != attrend) 590 goto bad; 591 break; 592 case FA_MODTIME: 593 errno = 0; 594 fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX); 595 if (errno || end != attrend) 596 goto bad; 597 break; 598 case FA_SIZE: 599 errno = 0; 600 fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX); 601 if (errno || end != attrend) 602 goto bad; 603 break; 604 case FA_LINKTARGET: 605 fa->linktarget = xstrdup(attrstart); 606 break; 607 case FA_RDEV: 608 errno = 0; 609 fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX); 610 if (errno || end != attrend) 611 goto bad; 612 break; 613 case FA_OWNER: 614 error = getuidbyname(attrstart, &fa->uid); 615 if (error) 616 fa->mask &= ~FA_OWNER; 617 break; 618 case FA_GROUP: 619 error = getgidbyname(attrstart, &fa->gid); 620 if (error) 621 fa->mask &= ~FA_GROUP; 622 break; 623 case FA_MODE: 624 errno = 0; 625 fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX); 626 if (errno || end != attrend) 627 goto bad; 628 if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) 629 modemask = FA_SETIDMASK | FA_PERMMASK; 630 else 631 modemask = FA_PERMMASK; 632 fa->mode &= modemask; 633 break; 634 case FA_FLAGS: 635 errno = 0; 636 fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX); 637 if (errno || end != attrend) 638 goto bad; 639 break; 640 case FA_LINKCOUNT: 641 errno = 0; 642 fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX); 643 if (errno || end != attrend) 644 goto bad; 645 break; 646 case FA_DEV: 647 errno = 0; 648 fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX); 649 if (errno || end != attrend) 650 goto bad; 651 break; 652 case FA_INODE: 653 errno = 0; 654 fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX); 655 if (errno || end != attrend) 656 goto bad; 657 break; 658 } 659 *attrend = tmp; 660 return (attrend); 661bad: 662 *attrend = tmp; 663 return (NULL); 664} 665 666/* Return a file attribute structure built from the RCS file attributes. */ 667struct fattr * 668fattr_forcheckout(const struct fattr *rcsattr, mode_t mask) 669{ 670 struct fattr *fa; 671 672 fa = fattr_new(FT_FILE, -1); 673 if (rcsattr->mask & FA_MODE) { 674 if ((rcsattr->mode & 0111) > 0) 675 fa->mode = 0777; 676 else 677 fa->mode = 0666; 678 fa->mode &= ~mask; 679 fa->mask |= FA_MODE; 680 } 681 return (fa); 682} 683 684/* Merge attributes from "from" that aren't present in "fa". */ 685void 686fattr_merge(struct fattr *fa, const struct fattr *from) 687{ 688 689 fattr_override(fa, from, from->mask & ~fa->mask); 690} 691 692/* Merge default attributes. */ 693void 694fattr_mergedefault(struct fattr *fa) 695{ 696 697 fattr_merge(fa, defaults[fa->type]); 698} 699 700/* Override selected attributes of "fa" with values from "from". */ 701void 702fattr_override(struct fattr *fa, const struct fattr *from, int mask) 703{ 704 705 mask &= from->mask; 706 if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET) 707 free(fa->linktarget); 708 fa->mask |= mask; 709 if (mask & FA_FILETYPE) 710 fa->type = from->type; 711 if (mask & FA_MODTIME) 712 fa->modtime = from->modtime; 713 if (mask & FA_SIZE) 714 fa->size = from->size; 715 if (mask & FA_LINKTARGET) 716 fa->linktarget = xstrdup(from->linktarget); 717 if (mask & FA_RDEV) 718 fa->rdev = from->rdev; 719 if (mask & FA_OWNER) 720 fa->uid = from->uid; 721 if (mask & FA_GROUP) 722 fa->gid = from->gid; 723 if (mask & FA_MODE) 724 fa->mode = from->mode; 725 if (mask & FA_FLAGS) 726 fa->flags = from->flags; 727 if (mask & FA_LINKCOUNT) 728 fa->linkcount = from->linkcount; 729 if (mask & FA_DEV) 730 fa->dev = from->dev; 731 if (mask & FA_INODE) 732 fa->inode = from->inode; 733} 734 735/* Create a node. */ 736int 737fattr_makenode(const struct fattr *fa, const char *path) 738{ 739 mode_t modemask, mode; 740 int error; 741 742 error = 0; 743 744 if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) 745 modemask = FA_SETIDMASK | FA_PERMMASK; 746 else 747 modemask = FA_PERMMASK; 748 749 /* We only implement fattr_makenode() for dirs for now. */ 750 if (fa->mask & FA_MODE) 751 mode = fa->mode & modemask; 752 else 753 mode = 0700; 754 755 if (fa->type == FT_DIRECTORY) 756 error = mkdir(path, mode); 757 else if (fa->type == FT_SYMLINK) { 758 error = symlink(fa->linktarget, path); 759 } else if (fa->type == FT_CDEV) { 760 lprintf(-1, "Character devices not supported!\n"); 761 } else if (fa->type == FT_BDEV) { 762 lprintf(-1, "Block devices not supported!\n"); 763 } 764 return (error); 765} 766 767int 768fattr_delete(const char *path) 769{ 770 struct fattr *fa; 771 int error; 772 773 fa = fattr_frompath(path, FATTR_NOFOLLOW); 774 if (fa == NULL) { 775 if (errno == ENOENT) 776 return (0); 777 return (-1); 778 } 779 780#ifdef HAVE_FFLAGS 781 /* Clear flags. */ 782 if (fa->mask & FA_FLAGS && fa->flags != 0) { 783 fa->flags = 0; 784 (void)chflags(path, fa->flags); 785 } 786#endif 787 788 if (fa->type == FT_DIRECTORY) 789 error = rmdir(path); 790 else 791 error = unlink(path); 792 fattr_free(fa); 793 return (error); 794} 795 796/* 797 * Changes those attributes we can change. Returns -1 on error, 798 * 0 if no update was needed, and 1 if an update was needed and 799 * it has been applied successfully. 800 */ 801int 802fattr_install(struct fattr *fa, const char *topath, const char *frompath) 803{ 804 struct timeval tv[2]; 805 struct fattr *old; 806 int error, inplace, mask; 807 mode_t modemask, newmode; 808 uid_t uid; 809 gid_t gid; 810 811 mask = fa->mask & fattr_supported(fa->type); 812 if (mask & FA_OWNER && mask & FA_GROUP) 813 modemask = FA_SETIDMASK | FA_PERMMASK; 814 else 815 modemask = FA_PERMMASK; 816 817 inplace = 0; 818 if (frompath == NULL) { 819 /* Changing attributes in place. */ 820 frompath = topath; 821 inplace = 1; 822 } 823 old = fattr_frompath(topath, FATTR_NOFOLLOW); 824 if (old != NULL) { 825 if (inplace && fattr_equal(fa, old)) { 826 fattr_free(old); 827 return (0); 828 } 829 830#ifdef HAVE_FFLAGS 831 /* 832 * Determine whether we need to clear the flags of the target. 833 * This is bogus in that it assumes a value of 0 is safe and 834 * that non-zero is unsafe. I'm not really worried by that 835 * since as far as I know that's the way things are. 836 */ 837 if ((old->mask & FA_FLAGS) && old->flags > 0) { 838 (void)chflags(topath, 0); 839 old->flags = 0; 840 } 841#endif 842 843 /* 844 * If it is changed from a file to a symlink, remove the file 845 * and create the symlink. 846 */ 847 if (inplace && (fa->type == FT_SYMLINK) && 848 (old->type == FT_FILE)) { 849 error = unlink(topath); 850 if (error) 851 goto bad; 852 error = symlink(fa->linktarget, topath); 853 if (error) 854 goto bad; 855 } 856 /* Determine whether we need to remove the target first. */ 857 if (!inplace && (fa->type == FT_DIRECTORY) != 858 (old->type == FT_DIRECTORY)) { 859 if (old->type == FT_DIRECTORY) 860 error = rmdir(topath); 861 else 862 error = unlink(topath); 863 if (error) 864 goto bad; 865 } 866 } 867 868 /* Change those attributes that we can before moving the file 869 * into place. That makes installation atomic in most cases. */ 870 if (mask & FA_MODTIME) { 871 gettimeofday(tv, NULL); /* Access time. */ 872 tv[1].tv_sec = fa->modtime; /* Modification time. */ 873 tv[1].tv_usec = 0; 874 error = utimes(frompath, tv); 875 if (error) 876 goto bad; 877 } 878 if (mask & FA_OWNER || mask & FA_GROUP) { 879 uid = -1; 880 gid = -1; 881 if (mask & FA_OWNER) 882 uid = fa->uid; 883 if (mask & FA_GROUP) 884 gid = fa->gid; 885 error = chown(frompath, uid, gid); 886 if (error) { 887 goto bad; 888 } 889 } 890 if (mask & FA_MODE) { 891 newmode = fa->mode & modemask; 892 /* Merge in set*id bits from the old attribute. */ 893 if (old != NULL && old->mask & FA_MODE) { 894 newmode |= (old->mode & ~modemask); 895 newmode &= (FA_SETIDMASK | FA_PERMMASK); 896 } 897 error = chmod(frompath, newmode); 898 if (error) 899 goto bad; 900 } 901 902 if (!inplace) { 903 error = rename(frompath, topath); 904 if (error) 905 goto bad; 906 } 907 908#ifdef HAVE_FFLAGS 909 /* Set the flags. */ 910 if (mask & FA_FLAGS) 911 (void)chflags(topath, fa->flags); 912#endif 913 fattr_free(old); 914 return (1); 915bad: 916 fattr_free(old); 917 return (-1); 918} 919 920/* 921 * Returns 1 if both attributes are equal, 0 otherwise. 922 * 923 * This function only compares attributes that are valid in both 924 * files. A file of unknown type ("FT_UNKNOWN") is unequal to 925 * anything, including itself. 926 */ 927int 928fattr_equal(const struct fattr *fa1, const struct fattr *fa2) 929{ 930 int mask; 931 932 mask = fa1->mask & fa2->mask; 933 if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN) 934 return (0); 935 if (mask & FA_FILETYPE) 936 if (fa1->type != fa2->type) 937 return (0); 938 if (mask & FA_MODTIME) 939 if (fa1->modtime != fa2->modtime) 940 return (0); 941 if (mask & FA_SIZE) 942 if (fa1->size != fa2->size) 943 return (0); 944 if (mask & FA_LINKTARGET) 945 if (strcmp(fa1->linktarget, fa2->linktarget) != 0) 946 return (0); 947 if (mask & FA_RDEV) 948 if (fa1->rdev != fa2->rdev) 949 return (0); 950 if (mask & FA_OWNER) 951 if (fa1->uid != fa2->uid) 952 return (0); 953 if (mask & FA_GROUP) 954 if (fa1->gid != fa2->gid) 955 return (0); 956 if (mask & FA_MODE) 957 if (fa1->mode != fa2->mode) 958 return (0); 959 if (mask & FA_FLAGS) 960 if (fa1->flags != fa2->flags) 961 return (0); 962 if (mask & FA_LINKCOUNT) 963 if (fa1->linkcount != fa2->linkcount) 964 return (0); 965 if (mask & FA_DEV) 966 if (fa1->dev != fa2->dev) 967 return (0); 968 if (mask & FA_INODE) 969 if (fa1->inode != fa2->inode) 970 return (0); 971 return (1); 972} 973 974/* 975 * Must have to get the correct filesize sendt by the server. 976 */ 977off_t 978fattr_filesize(const struct fattr *fa) 979{ 980 return (fa->size); 981} 982