1156230Smux/*- 2156230Smux * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3156230Smux * All rights reserved. 4156230Smux * 5156230Smux * Redistribution and use in source and binary forms, with or without 6156230Smux * modification, are permitted provided that the following conditions 7156230Smux * are met: 8156230Smux * 1. Redistributions of source code must retain the above copyright 9156230Smux * notice, this list of conditions and the following disclaimer. 10156230Smux * 2. Redistributions in binary form must reproduce the above copyright 11156230Smux * notice, this list of conditions and the following disclaimer in the 12156230Smux * documentation and/or other materials provided with the distribution. 13156230Smux * 14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17156230Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24156230Smux * SUCH DAMAGE. 25156230Smux * 26156230Smux * $FreeBSD$ 27156230Smux */ 28156230Smux 29156230Smux#include <sys/time.h> 30156230Smux#include <sys/types.h> 31156230Smux#include <sys/stat.h> 32156230Smux 33156230Smux#include <assert.h> 34156230Smux#include <errno.h> 35156230Smux#include <stdio.h> 36156230Smux#include <stdlib.h> 37156230Smux#include <string.h> 38156230Smux#include <unistd.h> 39156230Smux 40156230Smux#include "fattr.h" 41156701Smux#include "idcache.h" 42156230Smux#include "misc.h" 43156230Smux 44156230Smux/* 45156230Smux * Include the appropriate definition for the file attributes we support. 46156230Smux * There are two different files: fattr_bsd.h for BSD-like systems that 47186781Slulf * support the extended file flags a la chflags() and fattr_posix.h for 48156230Smux * bare POSIX systems that don't. 49156230Smux */ 50156230Smux#ifdef HAVE_FFLAGS 51156230Smux#include "fattr_bsd.h" 52156230Smux#else 53156230Smux#include "fattr_posix.h" 54156230Smux#endif 55156230Smux 56156230Smux#ifdef __FreeBSD__ 57156230Smux#include <osreldate.h> 58156230Smux#endif 59156230Smux 60156230Smux/* Define fflags_t if we're on a system that doesn't have it. */ 61156230Smux#if !defined(__FreeBSD_version) || __FreeBSD_version < 500030 62156230Smuxtypedef uint32_t fflags_t; 63156230Smux#endif 64156230Smux 65156230Smux#define FA_MASKRADIX 16 66156230Smux#define FA_FILETYPERADIX 10 67156230Smux#define FA_MODTIMERADIX 10 68156230Smux#define FA_SIZERADIX 10 69156230Smux#define FA_RDEVRADIX 16 70156230Smux#define FA_MODERADIX 8 71156230Smux#define FA_FLAGSRADIX 16 72156230Smux#define FA_LINKCOUNTRADIX 10 73156230Smux#define FA_DEVRADIX 16 74156230Smux#define FA_INODERADIX 10 75156230Smux 76156230Smux#define FA_PERMMASK (S_IRWXU | S_IRWXG | S_IRWXO) 77156230Smux#define FA_SETIDMASK (S_ISUID | S_ISGID | S_ISVTX) 78156230Smux 79156230Smuxstruct fattr { 80156230Smux int mask; 81156230Smux int type; 82156230Smux time_t modtime; 83156230Smux off_t size; 84156230Smux char *linktarget; 85156230Smux dev_t rdev; 86156230Smux uid_t uid; 87156230Smux gid_t gid; 88156230Smux mode_t mode; 89156230Smux fflags_t flags; 90156230Smux nlink_t linkcount; 91156230Smux dev_t dev; 92156230Smux ino_t inode; 93156230Smux}; 94156230Smux 95156230Smuxstatic const struct fattr bogus = { 96156230Smux FA_MODTIME | FA_SIZE | FA_MODE, 97156230Smux FT_UNKNOWN, 98156230Smux 1, 99156230Smux 0, 100156230Smux NULL, 101156230Smux 0, 102156230Smux 0, 103156230Smux 0, 104156230Smux 0, 105156230Smux 0, 106156230Smux 0, 107156230Smux 0, 108156230Smux 0 109156230Smux}; 110156230Smux 111156230Smuxstatic struct fattr *defaults[FT_NUMBER]; 112156230Smux 113156230Smuxvoid 114156230Smuxfattr_init(void) 115156230Smux{ 116156230Smux struct fattr *fa; 117156230Smux int i; 118156230Smux 119156230Smux for (i = 0; i < FT_NUMBER; i++) { 120156230Smux fa = fattr_new(i, -1); 121156230Smux if (i == FT_DIRECTORY) 122156230Smux fa->mode = 0777; 123156230Smux else 124156230Smux fa->mode = 0666; 125156230Smux fa->mask |= FA_MODE; 126156230Smux defaults[i] = fa; 127156230Smux } 128156701Smux /* Initialize the uid/gid lookup cache. */ 129156701Smux idcache_init(); 130156230Smux} 131156230Smux 132156230Smuxvoid 133156230Smuxfattr_fini(void) 134156230Smux{ 135156230Smux int i; 136156230Smux 137156701Smux idcache_fini(); 138156230Smux for (i = 0; i < FT_NUMBER; i++) 139156230Smux fattr_free(defaults[i]); 140156230Smux} 141156230Smux 142156230Smuxconst struct fattr *fattr_bogus = &bogus; 143156230Smux 144156230Smuxstatic char *fattr_scanattr(struct fattr *, int, const char *); 145156230Smux 146156230Smuxint 147156230Smuxfattr_supported(int type) 148156230Smux{ 149156230Smux 150156230Smux return (fattr_support[type]); 151156230Smux} 152156230Smux 153156230Smuxstruct fattr * 154156230Smuxfattr_new(int type, time_t modtime) 155156230Smux{ 156156230Smux struct fattr *new; 157156230Smux 158156230Smux new = xmalloc(sizeof(struct fattr)); 159156230Smux memset(new, 0, sizeof(struct fattr)); 160156230Smux new->type = type; 161156230Smux if (type != FT_UNKNOWN) 162156230Smux new->mask |= FA_FILETYPE; 163156230Smux if (modtime != -1) { 164156230Smux new->modtime = modtime; 165156230Smux new->mask |= FA_MODTIME; 166156230Smux } 167156230Smux if (fattr_supported(new->type) & FA_LINKCOUNT) { 168156230Smux new->mask |= FA_LINKCOUNT; 169156230Smux new->linkcount = 1; 170156230Smux } 171156230Smux return (new); 172156230Smux} 173156230Smux 174156230Smux/* Returns a new file attribute structure based on a stat structure. */ 175156230Smuxstruct fattr * 176156230Smuxfattr_fromstat(struct stat *sb) 177156230Smux{ 178156230Smux struct fattr *fa; 179156230Smux 180156230Smux fa = fattr_new(FT_UNKNOWN, -1); 181156230Smux if (S_ISREG(sb->st_mode)) 182156230Smux fa->type = FT_FILE; 183156230Smux else if (S_ISDIR(sb->st_mode)) 184156230Smux fa->type = FT_DIRECTORY; 185156230Smux else if (S_ISCHR(sb->st_mode)) 186156230Smux fa->type = FT_CDEV; 187156230Smux else if (S_ISBLK(sb->st_mode)) 188156230Smux fa->type = FT_BDEV; 189156230Smux else if (S_ISLNK(sb->st_mode)) 190156230Smux fa->type = FT_SYMLINK; 191156230Smux else 192156230Smux fa->type = FT_UNKNOWN; 193156230Smux 194156230Smux fa->mask = FA_FILETYPE | fattr_supported(fa->type); 195156230Smux if (fa->mask & FA_MODTIME) 196156230Smux fa->modtime = sb->st_mtime; 197156230Smux if (fa->mask & FA_SIZE) 198156230Smux fa->size = sb->st_size; 199156230Smux if (fa->mask & FA_RDEV) 200156230Smux fa->rdev = sb->st_rdev; 201156230Smux if (fa->mask & FA_OWNER) 202156230Smux fa->uid = sb->st_uid; 203156230Smux if (fa->mask & FA_GROUP) 204156230Smux fa->gid = sb->st_gid; 205156230Smux if (fa->mask & FA_MODE) 206156230Smux fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK); 207156230Smux#ifdef HAVE_FFLAGS 208156230Smux if (fa->mask & FA_FLAGS) 209156230Smux fa->flags = sb->st_flags; 210156230Smux#endif 211156230Smux if (fa->mask & FA_LINKCOUNT) 212156230Smux fa->linkcount = sb->st_nlink; 213156230Smux if (fa->mask & FA_DEV) 214156230Smux fa->dev = sb->st_dev; 215156230Smux if (fa->mask & FA_INODE) 216156230Smux fa->inode = sb->st_ino; 217156230Smux return (fa); 218156230Smux} 219156230Smux 220156230Smuxstruct fattr * 221156230Smuxfattr_frompath(const char *path, int nofollow) 222156230Smux{ 223156230Smux struct fattr *fa; 224156230Smux struct stat sb; 225156230Smux int error, len; 226156230Smux 227156230Smux if (nofollow) 228156230Smux error = lstat(path, &sb); 229156230Smux else 230156230Smux error = stat(path, &sb); 231156230Smux if (error) 232156230Smux return (NULL); 233156230Smux fa = fattr_fromstat(&sb); 234156230Smux if (fa->mask & FA_LINKTARGET) { 235156230Smux char buf[1024]; 236156230Smux 237156230Smux len = readlink(path, buf, sizeof(buf)); 238156230Smux if (len == -1) { 239156230Smux fattr_free(fa); 240156230Smux return (NULL); 241156230Smux } 242156230Smux if ((unsigned)len > sizeof(buf) - 1) { 243156230Smux fattr_free(fa); 244156230Smux errno = ENAMETOOLONG; 245156230Smux return (NULL); 246156230Smux } 247156230Smux buf[len] = '\0'; 248156230Smux fa->linktarget = xstrdup(buf); 249156230Smux } 250156230Smux return (fa); 251156230Smux} 252156230Smux 253156230Smuxstruct fattr * 254156230Smuxfattr_fromfd(int fd) 255156230Smux{ 256156230Smux struct fattr *fa; 257156230Smux struct stat sb; 258156230Smux int error; 259156230Smux 260156230Smux error = fstat(fd, &sb); 261156230Smux if (error) 262156230Smux return (NULL); 263156230Smux fa = fattr_fromstat(&sb); 264156230Smux return (fa); 265156230Smux} 266156230Smux 267156230Smuxint 268156230Smuxfattr_type(const struct fattr *fa) 269156230Smux{ 270156230Smux 271156230Smux return (fa->type); 272156230Smux} 273156230Smux 274156230Smux/* Returns a new file attribute structure from its encoded text form. */ 275156230Smuxstruct fattr * 276156230Smuxfattr_decode(char *attr) 277156230Smux{ 278156230Smux struct fattr *fa; 279156230Smux char *next; 280156230Smux 281156230Smux fa = fattr_new(FT_UNKNOWN, -1); 282156230Smux next = fattr_scanattr(fa, FA_MASK, attr); 283156230Smux if (next == NULL || (fa->mask & ~FA_MASK) > 0) 284156230Smux goto bad; 285156230Smux if (fa->mask & FA_FILETYPE) { 286156230Smux next = fattr_scanattr(fa, FA_FILETYPE, next); 287156230Smux if (next == NULL) 288156230Smux goto bad; 289156230Smux if (fa->type < 0 || fa->type > FT_MAX) 290156230Smux fa->type = FT_UNKNOWN; 291156230Smux } else { 292156230Smux /* The filetype attribute is always valid. */ 293156230Smux fa->mask |= FA_FILETYPE; 294156230Smux fa->type = FT_UNKNOWN; 295156230Smux } 296156230Smux fa->mask = fa->mask & fattr_supported(fa->type); 297156230Smux if (fa->mask & FA_MODTIME) 298156230Smux next = fattr_scanattr(fa, FA_MODTIME, next); 299156230Smux if (fa->mask & FA_SIZE) 300156230Smux next = fattr_scanattr(fa, FA_SIZE, next); 301156230Smux if (fa->mask & FA_LINKTARGET) 302156230Smux next = fattr_scanattr(fa, FA_LINKTARGET, next); 303156230Smux if (fa->mask & FA_RDEV) 304156230Smux next = fattr_scanattr(fa, FA_RDEV, next); 305156230Smux if (fa->mask & FA_OWNER) 306156230Smux next = fattr_scanattr(fa, FA_OWNER, next); 307156230Smux if (fa->mask & FA_GROUP) 308156230Smux next = fattr_scanattr(fa, FA_GROUP, next); 309156230Smux if (fa->mask & FA_MODE) 310156230Smux next = fattr_scanattr(fa, FA_MODE, next); 311156230Smux if (fa->mask & FA_FLAGS) 312156230Smux next = fattr_scanattr(fa, FA_FLAGS, next); 313156230Smux if (fa->mask & FA_LINKCOUNT) { 314156230Smux next = fattr_scanattr(fa, FA_LINKCOUNT, next); 315156230Smux } else if (fattr_supported(fa->type) & FA_LINKCOUNT) { 316156230Smux /* If the link count is missing but supported, fake it as 1. */ 317156230Smux fa->mask |= FA_LINKCOUNT; 318156230Smux fa->linkcount = 1; 319156230Smux } 320156230Smux if (fa->mask & FA_DEV) 321156230Smux next = fattr_scanattr(fa, FA_DEV, next); 322156230Smux if (fa->mask & FA_INODE) 323156230Smux next = fattr_scanattr(fa, FA_INODE, next); 324156230Smux if (next == NULL) 325156230Smux goto bad; 326156230Smux return (fa); 327156230Smuxbad: 328156230Smux fattr_free(fa); 329156230Smux return (NULL); 330156230Smux} 331156230Smux 332156230Smuxchar * 333156230Smuxfattr_encode(const struct fattr *fa, fattr_support_t support, int ignore) 334156230Smux{ 335156230Smux struct { 336156230Smux char val[32]; 337156230Smux char len[4]; 338156230Smux int extval; 339156230Smux char *ext; 340156230Smux } pieces[FA_NUMBER], *piece; 341156701Smux char *cp, *s, *username, *groupname; 342156230Smux size_t len, vallen; 343156230Smux mode_t mode, modemask; 344156230Smux int mask, n, i; 345156230Smux 346156701Smux username = NULL; 347156701Smux groupname = NULL; 348156230Smux if (support == NULL) 349156230Smux mask = fa->mask; 350156230Smux else 351156230Smux mask = fa->mask & support[fa->type]; 352156230Smux mask &= ~ignore; 353156230Smux if (fa->mask & FA_OWNER) { 354156701Smux username = getuserbyid(fa->uid); 355156701Smux if (username == NULL) 356156230Smux mask &= ~FA_OWNER; 357156230Smux } 358156230Smux if (fa->mask & FA_GROUP) { 359156701Smux groupname = getgroupbyid(fa->gid); 360156701Smux if (groupname == NULL) 361156230Smux mask &= ~FA_GROUP; 362156230Smux } 363156230Smux if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1) 364156230Smux mask &= ~FA_LINKCOUNT; 365156230Smux 366156230Smux memset(pieces, 0, FA_NUMBER * sizeof(*pieces)); 367156230Smux len = 0; 368156230Smux piece = pieces; 369156230Smux vallen = snprintf(piece->val, sizeof(piece->val), "%x", mask); 370156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 371156230Smux (long long)vallen) + vallen + 1; 372156230Smux piece++; 373156230Smux if (mask & FA_FILETYPE) { 374156230Smux vallen = snprintf(piece->val, sizeof(piece->val), 375156230Smux "%d", fa->type); 376156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 377156230Smux (long long)vallen) + vallen + 1; 378156230Smux piece++; 379156230Smux } 380156230Smux if (mask & FA_MODTIME) { 381156230Smux vallen = snprintf(piece->val, sizeof(piece->val), 382156230Smux "%lld", (long long)fa->modtime); 383156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 384156230Smux (long long)vallen) + vallen + 1; 385156230Smux piece++; 386156230Smux } 387156230Smux if (mask & FA_SIZE) { 388156230Smux vallen = snprintf(piece->val, sizeof(piece->val), 389156230Smux "%lld", (long long)fa->size); 390156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 391156230Smux (long long)vallen) + vallen + 1; 392156230Smux piece++; 393156230Smux } 394156230Smux if (mask & FA_LINKTARGET) { 395156230Smux vallen = strlen(fa->linktarget); 396156230Smux piece->extval = 1; 397156230Smux piece->ext = fa->linktarget; 398156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 399156230Smux (long long)vallen) + vallen + 1; 400156230Smux piece++; 401156230Smux } 402156230Smux if (mask & FA_RDEV) { 403156230Smux vallen = snprintf(piece->val, sizeof(piece->val), 404156230Smux "%lld", (long long)fa->rdev); 405156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 406156230Smux (long long)vallen) + vallen + 1; 407156230Smux piece++; 408156230Smux } 409156230Smux if (mask & FA_OWNER) { 410156701Smux vallen = strlen(username); 411156230Smux piece->extval = 1; 412156701Smux piece->ext = username; 413156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 414156230Smux (long long)vallen) + vallen + 1; 415156230Smux piece++; 416156230Smux } 417156230Smux if (mask & FA_GROUP) { 418156701Smux vallen = strlen(groupname); 419156230Smux piece->extval = 1; 420156701Smux piece->ext = groupname; 421156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 422156230Smux (long long)vallen) + vallen + 1; 423156230Smux piece++; 424156230Smux } 425156230Smux if (mask & FA_MODE) { 426156230Smux if (mask & FA_OWNER && mask & FA_GROUP) 427156230Smux modemask = FA_SETIDMASK | FA_PERMMASK; 428156230Smux else 429156230Smux modemask = FA_PERMMASK; 430156230Smux mode = fa->mode & modemask; 431156230Smux vallen = snprintf(piece->val, sizeof(piece->val), 432156230Smux "%o", mode); 433156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 434156230Smux (long long)vallen) + vallen + 1; 435156230Smux piece++; 436156230Smux } 437156230Smux if (mask & FA_FLAGS) { 438156230Smux vallen = snprintf(piece->val, sizeof(piece->val), "%llx", 439156230Smux (long long)fa->flags); 440156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 441156230Smux (long long)vallen) + vallen + 1; 442156230Smux piece++; 443156230Smux } 444156230Smux if (mask & FA_LINKCOUNT) { 445156230Smux vallen = snprintf(piece->val, sizeof(piece->val), "%lld", 446156230Smux (long long)fa->linkcount); 447156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 448156230Smux (long long)vallen) + vallen + 1; 449156230Smux piece++; 450156230Smux } 451156230Smux if (mask & FA_DEV) { 452186781Slulf vallen = snprintf(piece->val, sizeof(piece->val), "%llx", 453156230Smux (long long)fa->dev); 454156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 455156230Smux (long long)vallen) + vallen + 1; 456156230Smux piece++; 457156230Smux } 458156230Smux if (mask & FA_INODE) { 459156230Smux vallen = snprintf(piece->val, sizeof(piece->val), "%lld", 460156230Smux (long long)fa->inode); 461156230Smux len += snprintf(piece->len, sizeof(piece->len), "%lld", 462156230Smux (long long)vallen) + vallen + 1; 463156230Smux piece++; 464156230Smux } 465156230Smux 466156230Smux s = xmalloc(len + 1); 467156230Smux 468156230Smux n = piece - pieces; 469156230Smux piece = pieces; 470156230Smux cp = s; 471156230Smux for (i = 0; i < n; i++) { 472156230Smux if (piece->extval) 473156230Smux len = sprintf(cp, "%s#%s", piece->len, piece->ext); 474156230Smux else 475156230Smux len = sprintf(cp, "%s#%s", piece->len, piece->val); 476156230Smux cp += len; 477156230Smux piece++; 478156230Smux } 479156230Smux return (s); 480156230Smux} 481156230Smux 482156230Smuxstruct fattr * 483156230Smuxfattr_dup(const struct fattr *from) 484156230Smux{ 485156230Smux struct fattr *fa; 486156230Smux 487156230Smux fa = fattr_new(FT_UNKNOWN, -1); 488156230Smux fattr_override(fa, from, FA_MASK); 489156230Smux return (fa); 490156230Smux} 491156230Smux 492156230Smuxvoid 493156230Smuxfattr_free(struct fattr *fa) 494156230Smux{ 495156230Smux 496156230Smux if (fa == NULL) 497156230Smux return; 498156230Smux if (fa->linktarget != NULL) 499156230Smux free(fa->linktarget); 500156230Smux free(fa); 501156230Smux} 502156230Smux 503156230Smuxvoid 504156230Smuxfattr_umask(struct fattr *fa, mode_t newumask) 505156230Smux{ 506156230Smux 507156230Smux if (fa->mask & FA_MODE) 508156230Smux fa->mode = fa->mode & ~newumask; 509156230Smux} 510156230Smux 511156230Smuxvoid 512156230Smuxfattr_maskout(struct fattr *fa, int mask) 513156230Smux{ 514156230Smux 515156230Smux /* Don't forget to free() the linktarget attribute if we remove it. */ 516156230Smux if (mask & FA_LINKTARGET && fa->mask & FA_LINKTARGET) { 517156230Smux free(fa->linktarget); 518156230Smux fa->linktarget = NULL; 519156230Smux } 520156230Smux fa->mask &= ~mask; 521156230Smux} 522156230Smux 523156230Smuxint 524156230Smuxfattr_getmask(const struct fattr *fa) 525156230Smux{ 526156230Smux 527156230Smux return (fa->mask); 528156230Smux} 529156230Smux 530156230Smuxnlink_t 531156230Smuxfattr_getlinkcount(const struct fattr *fa) 532156230Smux{ 533156230Smux 534156230Smux return (fa->linkcount); 535156230Smux} 536156230Smux 537186781Slulfchar * 538186781Slulffattr_getlinktarget(const struct fattr *fa) 539186781Slulf{ 540186781Slulf 541186781Slulf return (fa->linktarget); 542186781Slulf} 543186781Slulf 544156230Smux/* 545156230Smux * Eat the specified attribute and put it in the file attribute 546156230Smux * structure. Returns NULL on error, or a pointer to the next 547156230Smux * attribute to parse. 548156230Smux * 549156230Smux * This would be much prettier if we had strntol() so that we're 550156230Smux * not forced to write '\0' to the string before calling strtol() 551156230Smux * and then put back the old value... 552156230Smux * 553156230Smux * We need to use (unsigned) long long types here because some 554156230Smux * of the opaque types we're parsing (off_t, time_t...) may need 555156230Smux * 64bits to fit. 556156230Smux */ 557156230Smuxstatic char * 558156230Smuxfattr_scanattr(struct fattr *fa, int type, const char *attr) 559156230Smux{ 560156230Smux char *attrend, *attrstart, *end; 561156230Smux size_t len; 562156230Smux unsigned long attrlen; 563156701Smux int error; 564156230Smux mode_t modemask; 565156230Smux char tmp; 566156230Smux 567156230Smux if (attr == NULL) 568156230Smux return (NULL); 569156230Smux errno = 0; 570156230Smux attrlen = strtoul(attr, &end, 10); 571156230Smux if (errno || *end != '#') 572156230Smux return (NULL); 573156230Smux len = strlen(attr); 574156230Smux attrstart = end + 1; 575156230Smux attrend = attrstart + attrlen; 576156230Smux tmp = *attrend; 577156230Smux *attrend = '\0'; 578156230Smux switch (type) { 579156230Smux /* Using FA_MASK here is a bit bogus semantically. */ 580156230Smux case FA_MASK: 581156230Smux errno = 0; 582156230Smux fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX); 583156230Smux if (errno || end != attrend) 584156230Smux goto bad; 585156230Smux break; 586156230Smux case FA_FILETYPE: 587156230Smux errno = 0; 588156230Smux fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX); 589156230Smux if (errno || end != attrend) 590156230Smux goto bad; 591156230Smux break; 592156230Smux case FA_MODTIME: 593156230Smux errno = 0; 594156230Smux fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX); 595156230Smux if (errno || end != attrend) 596156230Smux goto bad; 597156230Smux break; 598156230Smux case FA_SIZE: 599156230Smux errno = 0; 600156230Smux fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX); 601156230Smux if (errno || end != attrend) 602156230Smux goto bad; 603156230Smux break; 604156230Smux case FA_LINKTARGET: 605156230Smux fa->linktarget = xstrdup(attrstart); 606156230Smux break; 607156230Smux case FA_RDEV: 608156230Smux errno = 0; 609156230Smux fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX); 610156230Smux if (errno || end != attrend) 611156230Smux goto bad; 612156230Smux break; 613156230Smux case FA_OWNER: 614156701Smux error = getuidbyname(attrstart, &fa->uid); 615156701Smux if (error) 616156230Smux fa->mask &= ~FA_OWNER; 617156230Smux break; 618156230Smux case FA_GROUP: 619156701Smux error = getgidbyname(attrstart, &fa->gid); 620156701Smux if (error) 621156230Smux fa->mask &= ~FA_GROUP; 622156230Smux break; 623156230Smux case FA_MODE: 624156230Smux errno = 0; 625156230Smux fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX); 626156230Smux if (errno || end != attrend) 627156230Smux goto bad; 628156230Smux if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) 629156230Smux modemask = FA_SETIDMASK | FA_PERMMASK; 630156230Smux else 631156230Smux modemask = FA_PERMMASK; 632156230Smux fa->mode &= modemask; 633156230Smux break; 634156230Smux case FA_FLAGS: 635156230Smux errno = 0; 636156230Smux fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX); 637156230Smux if (errno || end != attrend) 638156230Smux goto bad; 639156230Smux break; 640156230Smux case FA_LINKCOUNT: 641156230Smux errno = 0; 642156230Smux fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX); 643156230Smux if (errno || end != attrend) 644156230Smux goto bad; 645156230Smux break; 646156230Smux case FA_DEV: 647156230Smux errno = 0; 648156230Smux fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX); 649156230Smux if (errno || end != attrend) 650156230Smux goto bad; 651156230Smux break; 652156230Smux case FA_INODE: 653156230Smux errno = 0; 654156230Smux fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX); 655156230Smux if (errno || end != attrend) 656156230Smux goto bad; 657156230Smux break; 658156230Smux } 659156230Smux *attrend = tmp; 660156230Smux return (attrend); 661156230Smuxbad: 662156230Smux *attrend = tmp; 663156230Smux return (NULL); 664156230Smux} 665156230Smux 666156230Smux/* Return a file attribute structure built from the RCS file attributes. */ 667156230Smuxstruct fattr * 668156230Smuxfattr_forcheckout(const struct fattr *rcsattr, mode_t mask) 669156230Smux{ 670156230Smux struct fattr *fa; 671156230Smux 672156230Smux fa = fattr_new(FT_FILE, -1); 673156230Smux if (rcsattr->mask & FA_MODE) { 674156230Smux if ((rcsattr->mode & 0111) > 0) 675156230Smux fa->mode = 0777; 676156230Smux else 677156230Smux fa->mode = 0666; 678156230Smux fa->mode &= ~mask; 679156230Smux fa->mask |= FA_MODE; 680156230Smux } 681156230Smux return (fa); 682156230Smux} 683156230Smux 684156230Smux/* Merge attributes from "from" that aren't present in "fa". */ 685156230Smuxvoid 686156230Smuxfattr_merge(struct fattr *fa, const struct fattr *from) 687156230Smux{ 688156230Smux 689156230Smux fattr_override(fa, from, from->mask & ~fa->mask); 690156230Smux} 691156230Smux 692156230Smux/* Merge default attributes. */ 693156230Smuxvoid 694156230Smuxfattr_mergedefault(struct fattr *fa) 695156230Smux{ 696156230Smux 697156230Smux fattr_merge(fa, defaults[fa->type]); 698156230Smux} 699156230Smux 700156230Smux/* Override selected attributes of "fa" with values from "from". */ 701156230Smuxvoid 702156230Smuxfattr_override(struct fattr *fa, const struct fattr *from, int mask) 703156230Smux{ 704156230Smux 705156230Smux mask &= from->mask; 706156230Smux if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET) 707156230Smux free(fa->linktarget); 708156230Smux fa->mask |= mask; 709156230Smux if (mask & FA_FILETYPE) 710156230Smux fa->type = from->type; 711156230Smux if (mask & FA_MODTIME) 712156230Smux fa->modtime = from->modtime; 713156230Smux if (mask & FA_SIZE) 714156230Smux fa->size = from->size; 715156230Smux if (mask & FA_LINKTARGET) 716156230Smux fa->linktarget = xstrdup(from->linktarget); 717156230Smux if (mask & FA_RDEV) 718156230Smux fa->rdev = from->rdev; 719156230Smux if (mask & FA_OWNER) 720156230Smux fa->uid = from->uid; 721156230Smux if (mask & FA_GROUP) 722156230Smux fa->gid = from->gid; 723156230Smux if (mask & FA_MODE) 724156230Smux fa->mode = from->mode; 725156230Smux if (mask & FA_FLAGS) 726156230Smux fa->flags = from->flags; 727156230Smux if (mask & FA_LINKCOUNT) 728156230Smux fa->linkcount = from->linkcount; 729156230Smux if (mask & FA_DEV) 730156230Smux fa->dev = from->dev; 731156230Smux if (mask & FA_INODE) 732156230Smux fa->inode = from->inode; 733156230Smux} 734156230Smux 735156230Smux/* Create a node. */ 736156230Smuxint 737156230Smuxfattr_makenode(const struct fattr *fa, const char *path) 738156230Smux{ 739156230Smux mode_t modemask, mode; 740156230Smux int error; 741156230Smux 742186781Slulf error = 0; 743186781Slulf 744156230Smux if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) 745156230Smux modemask = FA_SETIDMASK | FA_PERMMASK; 746156230Smux else 747156230Smux modemask = FA_PERMMASK; 748156230Smux 749156230Smux /* We only implement fattr_makenode() for dirs for now. */ 750156230Smux if (fa->mask & FA_MODE) 751156230Smux mode = fa->mode & modemask; 752156230Smux else 753156230Smux mode = 0700; 754186781Slulf 755186781Slulf if (fa->type == FT_DIRECTORY) 756186781Slulf error = mkdir(path, mode); 757186781Slulf else if (fa->type == FT_SYMLINK) { 758186781Slulf error = symlink(fa->linktarget, path); 759186781Slulf } else if (fa->type == FT_CDEV) { 760186781Slulf lprintf(-1, "Character devices not supported!\n"); 761186781Slulf } else if (fa->type == FT_BDEV) { 762186781Slulf lprintf(-1, "Block devices not supported!\n"); 763186781Slulf } 764156230Smux return (error); 765156230Smux} 766156230Smux 767156230Smuxint 768156230Smuxfattr_delete(const char *path) 769156230Smux{ 770156230Smux struct fattr *fa; 771156230Smux int error; 772156230Smux 773156230Smux fa = fattr_frompath(path, FATTR_NOFOLLOW); 774156230Smux if (fa == NULL) { 775156230Smux if (errno == ENOENT) 776156230Smux return (0); 777156230Smux return (-1); 778156230Smux } 779156230Smux 780156701Smux#ifdef HAVE_FFLAGS 781156230Smux /* Clear flags. */ 782156230Smux if (fa->mask & FA_FLAGS && fa->flags != 0) { 783156230Smux fa->flags = 0; 784156230Smux (void)chflags(path, fa->flags); 785156230Smux } 786156701Smux#endif 787156230Smux 788156230Smux if (fa->type == FT_DIRECTORY) 789156230Smux error = rmdir(path); 790156230Smux else 791156230Smux error = unlink(path); 792156230Smux fattr_free(fa); 793156230Smux return (error); 794156230Smux} 795156230Smux 796156230Smux/* 797156230Smux * Changes those attributes we can change. Returns -1 on error, 798156230Smux * 0 if no update was needed, and 1 if an update was needed and 799156230Smux * it has been applied successfully. 800156230Smux */ 801156230Smuxint 802156230Smuxfattr_install(struct fattr *fa, const char *topath, const char *frompath) 803156230Smux{ 804156230Smux struct timeval tv[2]; 805156230Smux struct fattr *old; 806156230Smux int error, inplace, mask; 807156230Smux mode_t modemask, newmode; 808156230Smux uid_t uid; 809156230Smux gid_t gid; 810156230Smux 811156230Smux mask = fa->mask & fattr_supported(fa->type); 812156230Smux if (mask & FA_OWNER && mask & FA_GROUP) 813156230Smux modemask = FA_SETIDMASK | FA_PERMMASK; 814156230Smux else 815156230Smux modemask = FA_PERMMASK; 816156230Smux 817156230Smux inplace = 0; 818156230Smux if (frompath == NULL) { 819156230Smux /* Changing attributes in place. */ 820156230Smux frompath = topath; 821156230Smux inplace = 1; 822156230Smux } 823156230Smux old = fattr_frompath(topath, FATTR_NOFOLLOW); 824156701Smux if (old != NULL) { 825156701Smux if (inplace && fattr_equal(fa, old)) { 826156701Smux fattr_free(old); 827156701Smux return (0); 828156701Smux } 829156230Smux 830156230Smux#ifdef HAVE_FFLAGS 831156701Smux /* 832156701Smux * Determine whether we need to clear the flags of the target. 833156701Smux * This is bogus in that it assumes a value of 0 is safe and 834156701Smux * that non-zero is unsafe. I'm not really worried by that 835156701Smux * since as far as I know that's the way things are. 836156701Smux */ 837156701Smux if ((old->mask & FA_FLAGS) && old->flags > 0) { 838156701Smux (void)chflags(topath, 0); 839156701Smux old->flags = 0; 840156701Smux } 841156230Smux#endif 842156230Smux 843186781Slulf /* 844186781Slulf * If it is changed from a file to a symlink, remove the file 845186781Slulf * and create the symlink. 846186781Slulf */ 847186781Slulf if (inplace && (fa->type == FT_SYMLINK) && 848186781Slulf (old->type == FT_FILE)) { 849186781Slulf error = unlink(topath); 850186781Slulf if (error) 851186781Slulf goto bad; 852186781Slulf error = symlink(fa->linktarget, topath); 853186781Slulf if (error) 854186781Slulf goto bad; 855186781Slulf } 856156701Smux /* Determine whether we need to remove the target first. */ 857156701Smux if (!inplace && (fa->type == FT_DIRECTORY) != 858156701Smux (old->type == FT_DIRECTORY)) { 859156701Smux if (old->type == FT_DIRECTORY) 860156701Smux error = rmdir(topath); 861156701Smux else 862156701Smux error = unlink(topath); 863156701Smux if (error) 864156701Smux goto bad; 865156701Smux } 866156230Smux } 867156230Smux 868156230Smux /* Change those attributes that we can before moving the file 869156230Smux * into place. That makes installation atomic in most cases. */ 870156230Smux if (mask & FA_MODTIME) { 871156230Smux gettimeofday(tv, NULL); /* Access time. */ 872156230Smux tv[1].tv_sec = fa->modtime; /* Modification time. */ 873156230Smux tv[1].tv_usec = 0; 874156230Smux error = utimes(frompath, tv); 875156230Smux if (error) 876156230Smux goto bad; 877156230Smux } 878156230Smux if (mask & FA_OWNER || mask & FA_GROUP) { 879156230Smux uid = -1; 880156230Smux gid = -1; 881156230Smux if (mask & FA_OWNER) 882156230Smux uid = fa->uid; 883156230Smux if (mask & FA_GROUP) 884156230Smux gid = fa->gid; 885156230Smux error = chown(frompath, uid, gid); 886186781Slulf if (error) { 887156230Smux goto bad; 888186781Slulf } 889156230Smux } 890156230Smux if (mask & FA_MODE) { 891156230Smux newmode = fa->mode & modemask; 892156701Smux /* Merge in set*id bits from the old attribute. */ 893156701Smux if (old != NULL && old->mask & FA_MODE) { 894156230Smux newmode |= (old->mode & ~modemask); 895156230Smux newmode &= (FA_SETIDMASK | FA_PERMMASK); 896156230Smux } 897156230Smux error = chmod(frompath, newmode); 898156230Smux if (error) 899156230Smux goto bad; 900156230Smux } 901156230Smux 902156230Smux if (!inplace) { 903156230Smux error = rename(frompath, topath); 904156230Smux if (error) 905156230Smux goto bad; 906156230Smux } 907156230Smux 908156230Smux#ifdef HAVE_FFLAGS 909156230Smux /* Set the flags. */ 910156230Smux if (mask & FA_FLAGS) 911156230Smux (void)chflags(topath, fa->flags); 912156230Smux#endif 913156230Smux fattr_free(old); 914156230Smux return (1); 915156230Smuxbad: 916156230Smux fattr_free(old); 917156230Smux return (-1); 918156230Smux} 919156230Smux 920156230Smux/* 921156230Smux * Returns 1 if both attributes are equal, 0 otherwise. 922156230Smux * 923156230Smux * This function only compares attributes that are valid in both 924156230Smux * files. A file of unknown type ("FT_UNKNOWN") is unequal to 925156230Smux * anything, including itself. 926156230Smux */ 927156230Smuxint 928156230Smuxfattr_equal(const struct fattr *fa1, const struct fattr *fa2) 929156230Smux{ 930156230Smux int mask; 931156230Smux 932156230Smux mask = fa1->mask & fa2->mask; 933156230Smux if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN) 934156230Smux return (0); 935186781Slulf if (mask & FA_FILETYPE) 936186781Slulf if (fa1->type != fa2->type) 937186781Slulf return (0); 938156230Smux if (mask & FA_MODTIME) 939156230Smux if (fa1->modtime != fa2->modtime) 940156230Smux return (0); 941156230Smux if (mask & FA_SIZE) 942156230Smux if (fa1->size != fa2->size) 943156230Smux return (0); 944156230Smux if (mask & FA_LINKTARGET) 945156230Smux if (strcmp(fa1->linktarget, fa2->linktarget) != 0) 946156230Smux return (0); 947156230Smux if (mask & FA_RDEV) 948156230Smux if (fa1->rdev != fa2->rdev) 949156230Smux return (0); 950156230Smux if (mask & FA_OWNER) 951156230Smux if (fa1->uid != fa2->uid) 952156230Smux return (0); 953156230Smux if (mask & FA_GROUP) 954156230Smux if (fa1->gid != fa2->gid) 955156230Smux return (0); 956156230Smux if (mask & FA_MODE) 957156230Smux if (fa1->mode != fa2->mode) 958156230Smux return (0); 959156230Smux if (mask & FA_FLAGS) 960156230Smux if (fa1->flags != fa2->flags) 961156230Smux return (0); 962156230Smux if (mask & FA_LINKCOUNT) 963156230Smux if (fa1->linkcount != fa2->linkcount) 964156230Smux return (0); 965156230Smux if (mask & FA_DEV) 966156230Smux if (fa1->dev != fa2->dev) 967156230Smux return (0); 968156230Smux if (mask & FA_INODE) 969156230Smux if (fa1->inode != fa2->inode) 970156230Smux return (0); 971156230Smux return (1); 972156230Smux} 973186781Slulf 974186781Slulf/* 975186781Slulf * Must have to get the correct filesize sendt by the server. 976186781Slulf */ 977186781Slulfoff_t 978186781Slulffattr_filesize(const struct fattr *fa) 979186781Slulf{ 980186781Slulf return (fa->size); 981186781Slulf} 982