/*- * Copyright (c) 2003-2006, Maxime Henrion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "fattr.h" #include "idcache.h" #include "misc.h" /* * Include the appropriate definition for the file attributes we support. * There are two different files: fattr_bsd.h for BSD-like systems that * support the extended file flags a la chflags() and fattr_posix.h for * bare POSIX systems that don't. */ #ifdef HAVE_FFLAGS #include "fattr_bsd.h" #else #include "fattr_posix.h" #endif #ifdef __FreeBSD__ #include #endif /* Define fflags_t if we're on a system that doesn't have it. */ #if !defined(__FreeBSD_version) || __FreeBSD_version < 500030 typedef uint32_t fflags_t; #endif #define FA_MASKRADIX 16 #define FA_FILETYPERADIX 10 #define FA_MODTIMERADIX 10 #define FA_SIZERADIX 10 #define FA_RDEVRADIX 16 #define FA_MODERADIX 8 #define FA_FLAGSRADIX 16 #define FA_LINKCOUNTRADIX 10 #define FA_DEVRADIX 16 #define FA_INODERADIX 10 #define FA_PERMMASK (S_IRWXU | S_IRWXG | S_IRWXO) #define FA_SETIDMASK (S_ISUID | S_ISGID | S_ISVTX) struct fattr { int mask; int type; time_t modtime; off_t size; char *linktarget; dev_t rdev; uid_t uid; gid_t gid; mode_t mode; fflags_t flags; nlink_t linkcount; dev_t dev; ino_t inode; }; static const struct fattr bogus = { FA_MODTIME | FA_SIZE | FA_MODE, FT_UNKNOWN, 1, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0 }; static struct fattr *defaults[FT_NUMBER]; void fattr_init(void) { struct fattr *fa; int i; for (i = 0; i < FT_NUMBER; i++) { fa = fattr_new(i, -1); if (i == FT_DIRECTORY) fa->mode = 0777; else fa->mode = 0666; fa->mask |= FA_MODE; defaults[i] = fa; } /* Initialize the uid/gid lookup cache. */ idcache_init(); } void fattr_fini(void) { int i; idcache_fini(); for (i = 0; i < FT_NUMBER; i++) fattr_free(defaults[i]); } const struct fattr *fattr_bogus = &bogus; static char *fattr_scanattr(struct fattr *, int, const char *); int fattr_supported(int type) { return (fattr_support[type]); } struct fattr * fattr_new(int type, time_t modtime) { struct fattr *new; new = xmalloc(sizeof(struct fattr)); memset(new, 0, sizeof(struct fattr)); new->type = type; if (type != FT_UNKNOWN) new->mask |= FA_FILETYPE; if (modtime != -1) { new->modtime = modtime; new->mask |= FA_MODTIME; } if (fattr_supported(new->type) & FA_LINKCOUNT) { new->mask |= FA_LINKCOUNT; new->linkcount = 1; } return (new); } /* Returns a new file attribute structure based on a stat structure. */ struct fattr * fattr_fromstat(struct stat *sb) { struct fattr *fa; fa = fattr_new(FT_UNKNOWN, -1); if (S_ISREG(sb->st_mode)) fa->type = FT_FILE; else if (S_ISDIR(sb->st_mode)) fa->type = FT_DIRECTORY; else if (S_ISCHR(sb->st_mode)) fa->type = FT_CDEV; else if (S_ISBLK(sb->st_mode)) fa->type = FT_BDEV; else if (S_ISLNK(sb->st_mode)) fa->type = FT_SYMLINK; else fa->type = FT_UNKNOWN; fa->mask = FA_FILETYPE | fattr_supported(fa->type); if (fa->mask & FA_MODTIME) fa->modtime = sb->st_mtime; if (fa->mask & FA_SIZE) fa->size = sb->st_size; if (fa->mask & FA_RDEV) fa->rdev = sb->st_rdev; if (fa->mask & FA_OWNER) fa->uid = sb->st_uid; if (fa->mask & FA_GROUP) fa->gid = sb->st_gid; if (fa->mask & FA_MODE) fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK); #ifdef HAVE_FFLAGS if (fa->mask & FA_FLAGS) fa->flags = sb->st_flags; #endif if (fa->mask & FA_LINKCOUNT) fa->linkcount = sb->st_nlink; if (fa->mask & FA_DEV) fa->dev = sb->st_dev; if (fa->mask & FA_INODE) fa->inode = sb->st_ino; return (fa); } struct fattr * fattr_frompath(const char *path, int nofollow) { struct fattr *fa; struct stat sb; int error, len; if (nofollow) error = lstat(path, &sb); else error = stat(path, &sb); if (error) return (NULL); fa = fattr_fromstat(&sb); if (fa->mask & FA_LINKTARGET) { char buf[1024]; len = readlink(path, buf, sizeof(buf)); if (len == -1) { fattr_free(fa); return (NULL); } if ((unsigned)len > sizeof(buf) - 1) { fattr_free(fa); errno = ENAMETOOLONG; return (NULL); } buf[len] = '\0'; fa->linktarget = xstrdup(buf); } return (fa); } struct fattr * fattr_fromfd(int fd) { struct fattr *fa; struct stat sb; int error; error = fstat(fd, &sb); if (error) return (NULL); fa = fattr_fromstat(&sb); return (fa); } int fattr_type(const struct fattr *fa) { return (fa->type); } /* Returns a new file attribute structure from its encoded text form. */ struct fattr * fattr_decode(char *attr) { struct fattr *fa; char *next; fa = fattr_new(FT_UNKNOWN, -1); next = fattr_scanattr(fa, FA_MASK, attr); if (next == NULL || (fa->mask & ~FA_MASK) > 0) goto bad; if (fa->mask & FA_FILETYPE) { next = fattr_scanattr(fa, FA_FILETYPE, next); if (next == NULL) goto bad; if (fa->type < 0 || fa->type > FT_MAX) fa->type = FT_UNKNOWN; } else { /* The filetype attribute is always valid. */ fa->mask |= FA_FILETYPE; fa->type = FT_UNKNOWN; } fa->mask = fa->mask & fattr_supported(fa->type); if (fa->mask & FA_MODTIME) next = fattr_scanattr(fa, FA_MODTIME, next); if (fa->mask & FA_SIZE) next = fattr_scanattr(fa, FA_SIZE, next); if (fa->mask & FA_LINKTARGET) next = fattr_scanattr(fa, FA_LINKTARGET, next); if (fa->mask & FA_RDEV) next = fattr_scanattr(fa, FA_RDEV, next); if (fa->mask & FA_OWNER) next = fattr_scanattr(fa, FA_OWNER, next); if (fa->mask & FA_GROUP) next = fattr_scanattr(fa, FA_GROUP, next); if (fa->mask & FA_MODE) next = fattr_scanattr(fa, FA_MODE, next); if (fa->mask & FA_FLAGS) next = fattr_scanattr(fa, FA_FLAGS, next); if (fa->mask & FA_LINKCOUNT) { next = fattr_scanattr(fa, FA_LINKCOUNT, next); } else if (fattr_supported(fa->type) & FA_LINKCOUNT) { /* If the link count is missing but supported, fake it as 1. */ fa->mask |= FA_LINKCOUNT; fa->linkcount = 1; } if (fa->mask & FA_DEV) next = fattr_scanattr(fa, FA_DEV, next); if (fa->mask & FA_INODE) next = fattr_scanattr(fa, FA_INODE, next); if (next == NULL) goto bad; return (fa); bad: fattr_free(fa); return (NULL); } char * fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore) { struct { char val[32]; char len[4]; int extval; char *ext; } pieces[FA_NUMBER], *piece; char *cp, *s, *username, *groupname; size_t len, vallen; mode_t mode, modemask; int mask, n, i; username = NULL; groupname = NULL; if (support == NULL) mask = fa->mask; else mask = fa->mask & support[fa->type]; mask &= ~ignore; if (fa->mask & FA_OWNER) { username = getuserbyid(fa->uid); if (username == NULL) mask &= ~FA_OWNER; } if (fa->mask & FA_GROUP) { groupname = getgroupbyid(fa->gid); if (groupname == NULL) mask &= ~FA_GROUP; } if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1) mask &= ~FA_LINKCOUNT; memset(pieces, 0, FA_NUMBER * sizeof(*pieces)); len = 0; piece = pieces; vallen = snprintf(piece->val, sizeof(piece->val), "%x", mask); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; if (mask & FA_FILETYPE) { vallen = snprintf(piece->val, sizeof(piece->val), "%d", fa->type); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_MODTIME) { vallen = snprintf(piece->val, sizeof(piece->val), "%lld", (long long)fa->modtime); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_SIZE) { vallen = snprintf(piece->val, sizeof(piece->val), "%lld", (long long)fa->size); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_LINKTARGET) { vallen = strlen(fa->linktarget); piece->extval = 1; piece->ext = fa->linktarget; len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_RDEV) { vallen = snprintf(piece->val, sizeof(piece->val), "%lld", (long long)fa->rdev); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_OWNER) { vallen = strlen(username); piece->extval = 1; piece->ext = username; len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_GROUP) { vallen = strlen(groupname); piece->extval = 1; piece->ext = groupname; len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_MODE) { if (mask & FA_OWNER && mask & FA_GROUP) modemask = FA_SETIDMASK | FA_PERMMASK; else modemask = FA_PERMMASK; mode = fa->mode & modemask; vallen = snprintf(piece->val, sizeof(piece->val), "%o", mode); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_FLAGS) { vallen = snprintf(piece->val, sizeof(piece->val), "%llx", (long long)fa->flags); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_LINKCOUNT) { vallen = snprintf(piece->val, sizeof(piece->val), "%lld", (long long)fa->linkcount); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_DEV) { vallen = snprintf(piece->val, sizeof(piece->val), "%llx", (long long)fa->dev); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } if (mask & FA_INODE) { vallen = snprintf(piece->val, sizeof(piece->val), "%lld", (long long)fa->inode); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; piece++; } s = xmalloc(len + 1); n = piece - pieces; piece = pieces; cp = s; for (i = 0; i < n; i++) { if (piece->extval) len = sprintf(cp, "%s#%s", piece->len, piece->ext); else len = sprintf(cp, "%s#%s", piece->len, piece->val); cp += len; piece++; } return (s); } struct fattr * fattr_dup(const struct fattr *from) { struct fattr *fa; fa = fattr_new(FT_UNKNOWN, -1); fattr_override(fa, from, FA_MASK); return (fa); } void fattr_free(struct fattr *fa) { if (fa == NULL) return; if (fa->linktarget != NULL) free(fa->linktarget); free(fa); } void fattr_umask(struct fattr *fa, mode_t newumask) { if (fa->mask & FA_MODE) fa->mode = fa->mode & ~newumask; } void fattr_maskout(struct fattr *fa, int mask) { /* Don't forget to free() the linktarget attribute if we remove it. */ if (mask & FA_LINKTARGET && fa->mask & FA_LINKTARGET) { free(fa->linktarget); fa->linktarget = NULL; } fa->mask &= ~mask; } int fattr_getmask(const struct fattr *fa) { return (fa->mask); } nlink_t fattr_getlinkcount(const struct fattr *fa) { return (fa->linkcount); } char * fattr_getlinktarget(const struct fattr *fa) { return (fa->linktarget); } /* * Eat the specified attribute and put it in the file attribute * structure. Returns NULL on error, or a pointer to the next * attribute to parse. * * This would be much prettier if we had strntol() so that we're * not forced to write '\0' to the string before calling strtol() * and then put back the old value... * * We need to use (unsigned) long long types here because some * of the opaque types we're parsing (off_t, time_t...) may need * 64bits to fit. */ static char * fattr_scanattr(struct fattr *fa, int type, const char *attr) { char *attrend, *attrstart, *end; size_t len; unsigned long attrlen; int error; mode_t modemask; char tmp; if (attr == NULL) return (NULL); errno = 0; attrlen = strtoul(attr, &end, 10); if (errno || *end != '#') return (NULL); len = strlen(attr); attrstart = end + 1; attrend = attrstart + attrlen; tmp = *attrend; *attrend = '\0'; switch (type) { /* Using FA_MASK here is a bit bogus semantically. */ case FA_MASK: errno = 0; fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX); if (errno || end != attrend) goto bad; break; case FA_FILETYPE: errno = 0; fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX); if (errno || end != attrend) goto bad; break; case FA_MODTIME: errno = 0; fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX); if (errno || end != attrend) goto bad; break; case FA_SIZE: errno = 0; fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX); if (errno || end != attrend) goto bad; break; case FA_LINKTARGET: fa->linktarget = xstrdup(attrstart); break; case FA_RDEV: errno = 0; fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX); if (errno || end != attrend) goto bad; break; case FA_OWNER: error = getuidbyname(attrstart, &fa->uid); if (error) fa->mask &= ~FA_OWNER; break; case FA_GROUP: error = getgidbyname(attrstart, &fa->gid); if (error) fa->mask &= ~FA_GROUP; break; case FA_MODE: errno = 0; fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX); if (errno || end != attrend) goto bad; if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) modemask = FA_SETIDMASK | FA_PERMMASK; else modemask = FA_PERMMASK; fa->mode &= modemask; break; case FA_FLAGS: errno = 0; fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX); if (errno || end != attrend) goto bad; break; case FA_LINKCOUNT: errno = 0; fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX); if (errno || end != attrend) goto bad; break; case FA_DEV: errno = 0; fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX); if (errno || end != attrend) goto bad; break; case FA_INODE: errno = 0; fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX); if (errno || end != attrend) goto bad; break; } *attrend = tmp; return (attrend); bad: *attrend = tmp; return (NULL); } /* Return a file attribute structure built from the RCS file attributes. */ struct fattr * fattr_forcheckout(const struct fattr *rcsattr, mode_t mask) { struct fattr *fa; fa = fattr_new(FT_FILE, -1); if (rcsattr->mask & FA_MODE) { if ((rcsattr->mode & 0111) > 0) fa->mode = 0777; else fa->mode = 0666; fa->mode &= ~mask; fa->mask |= FA_MODE; } return (fa); } /* Merge attributes from "from" that aren't present in "fa". */ void fattr_merge(struct fattr *fa, const struct fattr *from) { fattr_override(fa, from, from->mask & ~fa->mask); } /* Merge default attributes. */ void fattr_mergedefault(struct fattr *fa) { fattr_merge(fa, defaults[fa->type]); } /* Override selected attributes of "fa" with values from "from". */ void fattr_override(struct fattr *fa, const struct fattr *from, int mask) { mask &= from->mask; if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET) free(fa->linktarget); fa->mask |= mask; if (mask & FA_FILETYPE) fa->type = from->type; if (mask & FA_MODTIME) fa->modtime = from->modtime; if (mask & FA_SIZE) fa->size = from->size; if (mask & FA_LINKTARGET) fa->linktarget = xstrdup(from->linktarget); if (mask & FA_RDEV) fa->rdev = from->rdev; if (mask & FA_OWNER) fa->uid = from->uid; if (mask & FA_GROUP) fa->gid = from->gid; if (mask & FA_MODE) fa->mode = from->mode; if (mask & FA_FLAGS) fa->flags = from->flags; if (mask & FA_LINKCOUNT) fa->linkcount = from->linkcount; if (mask & FA_DEV) fa->dev = from->dev; if (mask & FA_INODE) fa->inode = from->inode; } /* Create a node. */ int fattr_makenode(const struct fattr *fa, const char *path) { mode_t modemask, mode; int error; error = 0; if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) modemask = FA_SETIDMASK | FA_PERMMASK; else modemask = FA_PERMMASK; /* We only implement fattr_makenode() for dirs for now. */ if (fa->mask & FA_MODE) mode = fa->mode & modemask; else mode = 0700; if (fa->type == FT_DIRECTORY) error = mkdir(path, mode); else if (fa->type == FT_SYMLINK) { error = symlink(fa->linktarget, path); } else if (fa->type == FT_CDEV) { lprintf(-1, "Character devices not supported!\n"); } else if (fa->type == FT_BDEV) { lprintf(-1, "Block devices not supported!\n"); } return (error); } int fattr_delete(const char *path) { struct fattr *fa; int error; fa = fattr_frompath(path, FATTR_NOFOLLOW); if (fa == NULL) { if (errno == ENOENT) return (0); return (-1); } #ifdef HAVE_FFLAGS /* Clear flags. */ if (fa->mask & FA_FLAGS && fa->flags != 0) { fa->flags = 0; (void)chflags(path, fa->flags); } #endif if (fa->type == FT_DIRECTORY) error = rmdir(path); else error = unlink(path); fattr_free(fa); return (error); } /* * Changes those attributes we can change. Returns -1 on error, * 0 if no update was needed, and 1 if an update was needed and * it has been applied successfully. */ int fattr_install(struct fattr *fa, const char *topath, const char *frompath) { struct timeval tv[2]; struct fattr *old; int error, inplace, mask; mode_t modemask, newmode; uid_t uid; gid_t gid; mask = fa->mask & fattr_supported(fa->type); if (mask & FA_OWNER && mask & FA_GROUP) modemask = FA_SETIDMASK | FA_PERMMASK; else modemask = FA_PERMMASK; inplace = 0; if (frompath == NULL) { /* Changing attributes in place. */ frompath = topath; inplace = 1; } old = fattr_frompath(topath, FATTR_NOFOLLOW); if (old != NULL) { if (inplace && fattr_equal(fa, old)) { fattr_free(old); return (0); } #ifdef HAVE_FFLAGS /* * Determine whether we need to clear the flags of the target. * This is bogus in that it assumes a value of 0 is safe and * that non-zero is unsafe. I'm not really worried by that * since as far as I know that's the way things are. */ if ((old->mask & FA_FLAGS) && old->flags > 0) { (void)chflags(topath, 0); old->flags = 0; } #endif /* * If it is changed from a file to a symlink, remove the file * and create the symlink. */ if (inplace && (fa->type == FT_SYMLINK) && (old->type == FT_FILE)) { error = unlink(topath); if (error) goto bad; error = symlink(fa->linktarget, topath); if (error) goto bad; } /* Determine whether we need to remove the target first. */ if (!inplace && (fa->type == FT_DIRECTORY) != (old->type == FT_DIRECTORY)) { if (old->type == FT_DIRECTORY) error = rmdir(topath); else error = unlink(topath); if (error) goto bad; } } /* Change those attributes that we can before moving the file * into place. That makes installation atomic in most cases. */ if (mask & FA_MODTIME) { gettimeofday(tv, NULL); /* Access time. */ tv[1].tv_sec = fa->modtime; /* Modification time. */ tv[1].tv_usec = 0; error = utimes(frompath, tv); if (error) goto bad; } if (mask & FA_OWNER || mask & FA_GROUP) { uid = -1; gid = -1; if (mask & FA_OWNER) uid = fa->uid; if (mask & FA_GROUP) gid = fa->gid; error = chown(frompath, uid, gid); if (error) { goto bad; } } if (mask & FA_MODE) { newmode = fa->mode & modemask; /* Merge in set*id bits from the old attribute. */ if (old != NULL && old->mask & FA_MODE) { newmode |= (old->mode & ~modemask); newmode &= (FA_SETIDMASK | FA_PERMMASK); } error = chmod(frompath, newmode); if (error) goto bad; } if (!inplace) { error = rename(frompath, topath); if (error) goto bad; } #ifdef HAVE_FFLAGS /* Set the flags. */ if (mask & FA_FLAGS) (void)chflags(topath, fa->flags); #endif fattr_free(old); return (1); bad: fattr_free(old); return (-1); } /* * Returns 1 if both attributes are equal, 0 otherwise. * * This function only compares attributes that are valid in both * files. A file of unknown type ("FT_UNKNOWN") is unequal to * anything, including itself. */ int fattr_equal(const struct fattr *fa1, const struct fattr *fa2) { int mask; mask = fa1->mask & fa2->mask; if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN) return (0); if (mask & FA_FILETYPE) if (fa1->type != fa2->type) return (0); if (mask & FA_MODTIME) if (fa1->modtime != fa2->modtime) return (0); if (mask & FA_SIZE) if (fa1->size != fa2->size) return (0); if (mask & FA_LINKTARGET) if (strcmp(fa1->linktarget, fa2->linktarget) != 0) return (0); if (mask & FA_RDEV) if (fa1->rdev != fa2->rdev) return (0); if (mask & FA_OWNER) if (fa1->uid != fa2->uid) return (0); if (mask & FA_GROUP) if (fa1->gid != fa2->gid) return (0); if (mask & FA_MODE) if (fa1->mode != fa2->mode) return (0); if (mask & FA_FLAGS) if (fa1->flags != fa2->flags) return (0); if (mask & FA_LINKCOUNT) if (fa1->linkcount != fa2->linkcount) return (0); if (mask & FA_DEV) if (fa1->dev != fa2->dev) return (0); if (mask & FA_INODE) if (fa1->inode != fa2->inode) return (0); return (1); } /* * Must have to get the correct filesize sendt by the server. */ off_t fattr_filesize(const struct fattr *fa) { return (fa->size); }