fattr.c revision 256281
1285612Sdelphij/*- 2285612Sdelphij * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3285612Sdelphij * All rights reserved. 4285612Sdelphij * 5285612Sdelphij * Redistribution and use in source and binary forms, with or without 6285612Sdelphij * modification, are permitted provided that the following conditions 7285612Sdelphij * are met: 8181834Sroberto * 1. Redistributions of source code must retain the above copyright 9181834Sroberto * notice, this list of conditions and the following disclaimer. 10285612Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 11285612Sdelphij * notice, this list of conditions and the following disclaimer in the 12285612Sdelphij * documentation and/or other materials provided with the distribution. 13181834Sroberto * 14285612Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15285612Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16285612Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17181834Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18285612Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19285612Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20181834Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21285612Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22285612Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23181834Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24285612Sdelphij * SUCH DAMAGE. 25285612Sdelphij * 26285612Sdelphij * $FreeBSD: stable/10/usr.bin/csup/fattr.c 204556 2010-03-02 07:26:07Z lulf $ 27285612Sdelphij */ 28285612Sdelphij 29181834Sroberto#include <sys/time.h> 30285612Sdelphij#include <sys/types.h> 31181834Sroberto#include <sys/stat.h> 32181834Sroberto 33181834Sroberto#include <assert.h> 34181834Sroberto#include <errno.h> 35181834Sroberto#include <stdio.h> 36181834Sroberto#include <stdlib.h> 37181834Sroberto#include <string.h> 38181834Sroberto#include <unistd.h> 39285612Sdelphij 40181834Sroberto#include "fattr.h" 41181834Sroberto#include "idcache.h" 42285612Sdelphij#include "misc.h" 43285612Sdelphij 44285612Sdelphij/* 45285612Sdelphij * Include the appropriate definition for the file attributes we support. 46181834Sroberto * There are two different files: fattr_bsd.h for BSD-like systems that 47181834Sroberto * support the extended file flags a la chflags() and fattr_posix.h for 48181834Sroberto * bare POSIX systems that don't. 49285612Sdelphij */ 50181834Sroberto#ifdef HAVE_FFLAGS 51285612Sdelphij#include "fattr_bsd.h" 52285612Sdelphij#else 53181834Sroberto#include "fattr_posix.h" 54181834Sroberto#endif 55181834Sroberto 56181834Sroberto#ifdef __FreeBSD__ 57181834Sroberto#include <osreldate.h> 58181834Sroberto#endif 59181834Sroberto 60294569Sdelphij/* Define fflags_t if we're on a system that doesn't have it. */ 61181834Sroberto#if !defined(__FreeBSD_version) || __FreeBSD_version < 500030 62181834Srobertotypedef uint32_t fflags_t; 63181834Sroberto#endif 64181834Sroberto 65181834Sroberto#define FA_MASKRADIX 16 66181834Sroberto#define FA_FILETYPERADIX 10 67181834Sroberto#define FA_MODTIMERADIX 10 68181834Sroberto#define FA_SIZERADIX 10 69181834Sroberto#define FA_RDEVRADIX 16 70181834Sroberto#define FA_MODERADIX 8 71285612Sdelphij#define FA_FLAGSRADIX 16 72181834Sroberto#define FA_LINKCOUNTRADIX 10 73181834Sroberto#define FA_DEVRADIX 16 74181834Sroberto#define FA_INODERADIX 10 75181834Sroberto 76181834Sroberto#define FA_PERMMASK (S_IRWXU | S_IRWXG | S_IRWXO) 77285612Sdelphij#define FA_SETIDMASK (S_ISUID | S_ISGID | S_ISVTX) 78181834Sroberto 79285612Sdelphijstruct fattr { 80285612Sdelphij int mask; 81181834Sroberto int type; 82181834Sroberto time_t modtime; 83181834Sroberto off_t size; 84181834Sroberto char *linktarget; 85181834Sroberto dev_t rdev; 86181834Sroberto uid_t uid; 87181834Sroberto gid_t gid; 88181834Sroberto mode_t mode; 89181834Sroberto fflags_t flags; 90181834Sroberto nlink_t linkcount; 91181834Sroberto dev_t dev; 92181834Sroberto ino_t inode; 93181834Sroberto}; 94181834Sroberto 95285612Sdelphijstatic const struct fattr bogus = { 96181834Sroberto FA_MODTIME | FA_SIZE | FA_MODE, 97181834Sroberto FT_UNKNOWN, 98181834Sroberto 1, 99285612Sdelphij 0, 100181834Sroberto NULL, 101181834Sroberto 0, 102181834Sroberto 0, 103181834Sroberto 0, 104181834Sroberto 0, 105181834Sroberto 0, 106181834Sroberto 0, 107181834Sroberto 0, 108181834Sroberto 0 109181834Sroberto}; 110181834Sroberto 111181834Srobertostatic struct fattr *defaults[FT_NUMBER]; 112181834Sroberto 113181834Srobertovoid 114181834Srobertofattr_init(void) 115181834Sroberto{ 116181834Sroberto struct fattr *fa; 117181834Sroberto int i; 118181834Sroberto 119181834Sroberto for (i = 0; i < FT_NUMBER; i++) { 120181834Sroberto fa = fattr_new(i, -1); 121181834Sroberto if (i == FT_DIRECTORY) 122181834Sroberto fa->mode = 0777; 123285612Sdelphij else 124285612Sdelphij fa->mode = 0666; 125285612Sdelphij fa->mask |= FA_MODE; 126285612Sdelphij defaults[i] = fa; 127181834Sroberto } 128285612Sdelphij /* Initialize the uid/gid lookup cache. */ 129285612Sdelphij idcache_init(); 130285612Sdelphij} 131285612Sdelphij 132285612Sdelphijvoid 133285612Sdelphijfattr_fini(void) 134285612Sdelphij{ 135285612Sdelphij int i; 136285612Sdelphij 137285612Sdelphij idcache_fini(); 138285612Sdelphij for (i = 0; i < FT_NUMBER; i++) 139285612Sdelphij fattr_free(defaults[i]); 140285612Sdelphij} 141285612Sdelphij 142285612Sdelphijconst struct fattr *fattr_bogus = &bogus; 143285612Sdelphij 144285612Sdelphijstatic char *fattr_scanattr(struct fattr *, int, const char *); 145285612Sdelphij 146285612Sdelphijint 147285612Sdelphijfattr_supported(int type) 148285612Sdelphij{ 149285612Sdelphij 150285612Sdelphij return (fattr_support[type]); 151285612Sdelphij} 152285612Sdelphij 153285612Sdelphijstruct fattr * 154285612Sdelphijfattr_new(int type, time_t modtime) 155285612Sdelphij{ 156285612Sdelphij struct fattr *new; 157285612Sdelphij 158285612Sdelphij new = xmalloc(sizeof(struct fattr)); 159285612Sdelphij memset(new, 0, sizeof(struct fattr)); 160285612Sdelphij new->type = type; 161285612Sdelphij if (type != FT_UNKNOWN) 162285612Sdelphij new->mask |= FA_FILETYPE; 163285612Sdelphij if (modtime != -1) { 164285612Sdelphij new->modtime = modtime; 165285612Sdelphij new->mask |= FA_MODTIME; 166285612Sdelphij } 167285612Sdelphij if (fattr_supported(new->type) & FA_LINKCOUNT) { 168285612Sdelphij new->mask |= FA_LINKCOUNT; 169181834Sroberto new->linkcount = 1; 170181834Sroberto } 171181834Sroberto return (new); 172181834Sroberto} 173285612Sdelphij 174181834Sroberto/* Returns a new file attribute structure based on a stat structure. */ 175285612Sdelphijstruct fattr * 176181834Srobertofattr_fromstat(struct stat *sb) 177181834Sroberto{ 178181834Sroberto struct fattr *fa; 179181834Sroberto 180181834Sroberto fa = fattr_new(FT_UNKNOWN, -1); 181181834Sroberto if (S_ISREG(sb->st_mode)) 182181834Sroberto fa->type = FT_FILE; 183181834Sroberto else if (S_ISDIR(sb->st_mode)) 184181834Sroberto fa->type = FT_DIRECTORY; 185181834Sroberto else if (S_ISCHR(sb->st_mode)) 186181834Sroberto fa->type = FT_CDEV; 187181834Sroberto else if (S_ISBLK(sb->st_mode)) 188181834Sroberto fa->type = FT_BDEV; 189181834Sroberto else if (S_ISLNK(sb->st_mode)) 190181834Sroberto fa->type = FT_SYMLINK; 191181834Sroberto else 192181834Sroberto fa->type = FT_UNKNOWN; 193181834Sroberto 194181834Sroberto fa->mask = FA_FILETYPE | fattr_supported(fa->type); 195181834Sroberto if (fa->mask & FA_MODTIME) 196181834Sroberto fa->modtime = sb->st_mtime; 197181834Sroberto if (fa->mask & FA_SIZE) 198181834Sroberto fa->size = sb->st_size; 199181834Sroberto if (fa->mask & FA_RDEV) 200181834Sroberto fa->rdev = sb->st_rdev; 201181834Sroberto if (fa->mask & FA_OWNER) 202181834Sroberto fa->uid = sb->st_uid; 203181834Sroberto if (fa->mask & FA_GROUP) 204181834Sroberto fa->gid = sb->st_gid; 205181834Sroberto if (fa->mask & FA_MODE) 206181834Sroberto fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK); 207181834Sroberto#ifdef HAVE_FFLAGS 208181834Sroberto if (fa->mask & FA_FLAGS) 209181834Sroberto fa->flags = sb->st_flags; 210181834Sroberto#endif 211181834Sroberto if (fa->mask & FA_LINKCOUNT) 212181834Sroberto fa->linkcount = sb->st_nlink; 213181834Sroberto if (fa->mask & FA_DEV) 214181834Sroberto fa->dev = sb->st_dev; 215181834Sroberto if (fa->mask & FA_INODE) 216285612Sdelphij fa->inode = sb->st_ino; 217181834Sroberto return (fa); 218285612Sdelphij} 219285612Sdelphij 220181834Srobertostruct fattr * 221181834Srobertofattr_frompath(const char *path, int nofollow) 222181834Sroberto{ 223181834Sroberto struct fattr *fa; 224181834Sroberto struct stat sb; 225181834Sroberto int error, len; 226181834Sroberto 227181834Sroberto if (nofollow) 228181834Sroberto error = lstat(path, &sb); 229181834Sroberto else 230181834Sroberto error = stat(path, &sb); 231181834Sroberto if (error) 232181834Sroberto return (NULL); 233181834Sroberto fa = fattr_fromstat(&sb); 234285612Sdelphij if (fa->mask & FA_LINKTARGET) { 235285612Sdelphij char buf[1024]; 236181834Sroberto 237285612Sdelphij len = readlink(path, buf, sizeof(buf)); 238285612Sdelphij if (len == -1) { 239181834Sroberto fattr_free(fa); 240181834Sroberto return (NULL); 241285612Sdelphij } 242181834Sroberto if ((unsigned)len > sizeof(buf) - 1) { 243285612Sdelphij fattr_free(fa); 244285612Sdelphij errno = ENAMETOOLONG; 245181834Sroberto return (NULL); 246285612Sdelphij } 247285612Sdelphij buf[len] = '\0'; 248181834Sroberto fa->linktarget = xstrdup(buf); 249285612Sdelphij } 250285612Sdelphij return (fa); 251285612Sdelphij} 252285612Sdelphij 253285612Sdelphijstruct fattr * 254285612Sdelphijfattr_fromfd(int fd) 255285612Sdelphij{ 256285612Sdelphij struct fattr *fa; 257285612Sdelphij struct stat sb; 258181834Sroberto int error; 259285612Sdelphij 260285612Sdelphij error = fstat(fd, &sb); 261285612Sdelphij if (error) 262285612Sdelphij return (NULL); 263285612Sdelphij fa = fattr_fromstat(&sb); 264285612Sdelphij return (fa); 265285612Sdelphij} 266285612Sdelphij 267285612Sdelphijint 268285612Sdelphijfattr_type(const struct fattr *fa) 269285612Sdelphij{ 270181834Sroberto 271285612Sdelphij return (fa->type); 272285612Sdelphij} 273285612Sdelphij 274285612Sdelphij/* Returns a new file attribute structure from its encoded text form. */ 275285612Sdelphijstruct fattr * 276285612Sdelphijfattr_decode(char *attr) 277181834Sroberto{ 278285612Sdelphij struct fattr *fa; 279285612Sdelphij char *next; 280285612Sdelphij 281181834Sroberto fa = fattr_new(FT_UNKNOWN, -1); 282285612Sdelphij next = fattr_scanattr(fa, FA_MASK, attr); 283285612Sdelphij if (next == NULL || (fa->mask & ~FA_MASK) > 0) 284181834Sroberto goto bad; 285285612Sdelphij if (fa->mask & FA_FILETYPE) { 286285612Sdelphij next = fattr_scanattr(fa, FA_FILETYPE, next); 287285612Sdelphij if (next == NULL) 288285612Sdelphij goto bad; 289285612Sdelphij if (fa->type < 0 || fa->type > FT_MAX) 290181834Sroberto fa->type = FT_UNKNOWN; 291285612Sdelphij } else { 292285612Sdelphij /* The filetype attribute is always valid. */ 293285612Sdelphij fa->mask |= FA_FILETYPE; 294285612Sdelphij fa->type = FT_UNKNOWN; 295285612Sdelphij } 296181834Sroberto fa->mask = fa->mask & fattr_supported(fa->type); 297285612Sdelphij if (fa->mask & FA_MODTIME) 298181834Sroberto next = fattr_scanattr(fa, FA_MODTIME, next); 299181834Sroberto if (fa->mask & FA_SIZE) 300181834Sroberto next = fattr_scanattr(fa, FA_SIZE, next); 301181834Sroberto if (fa->mask & FA_LINKTARGET) 302181834Sroberto next = fattr_scanattr(fa, FA_LINKTARGET, next); 303181834Sroberto if (fa->mask & FA_RDEV) 304181834Sroberto next = fattr_scanattr(fa, FA_RDEV, next); 305181834Sroberto if (fa->mask & FA_OWNER) 306181834Sroberto next = fattr_scanattr(fa, FA_OWNER, next); 307285612Sdelphij if (fa->mask & FA_GROUP) 308181834Sroberto next = fattr_scanattr(fa, FA_GROUP, next); 309181834Sroberto if (fa->mask & FA_MODE) 310181834Sroberto next = fattr_scanattr(fa, FA_MODE, next); 311181834Sroberto if (fa->mask & FA_FLAGS) 312181834Sroberto next = fattr_scanattr(fa, FA_FLAGS, next); 313181834Sroberto if (fa->mask & FA_LINKCOUNT) { 314285612Sdelphij next = fattr_scanattr(fa, FA_LINKCOUNT, next); 315285612Sdelphij } else if (fattr_supported(fa->type) & FA_LINKCOUNT) { 316181834Sroberto /* If the link count is missing but supported, fake it as 1. */ 317285612Sdelphij fa->mask |= FA_LINKCOUNT; 318285612Sdelphij fa->linkcount = 1; 319181834Sroberto } 320181834Sroberto if (fa->mask & FA_DEV) 321285612Sdelphij next = fattr_scanattr(fa, FA_DEV, next); 322181834Sroberto if (fa->mask & FA_INODE) 323285612Sdelphij next = fattr_scanattr(fa, FA_INODE, next); 324181834Sroberto if (next == NULL) 325181834Sroberto goto bad; 326181834Sroberto return (fa); 327181834Srobertobad: 328181834Sroberto fattr_free(fa); 329181834Sroberto return (NULL); 330181834Sroberto} 331181834Sroberto 332285612Sdelphijchar * 333285612Sdelphijfattr_encode(const struct fattr *fa, fattr_support_t support, int ignore) 334181834Sroberto{ 335181834Sroberto struct { 336181834Sroberto char val[32]; 337181834Sroberto char len[4]; 338181834Sroberto int extval; 339181834Sroberto 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