2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/types.h>
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 à la chflags() and fattr_posix.h for
48 * bare POSIX systems that don't.
51 #include "fattr_bsd.h"
53 #include "fattr_posix.h"
57 #include <osreldate.h>
60 /* Define fflags_t if we're on a system that doesn't have it. */
61 #if !defined(__FreeBSD_version) || __FreeBSD_version < 500030
62 typedef uint32_t fflags_t;
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
76 #define FA_PERMMASK (S_IRWXU | S_IRWXG | S_IRWXO)
77 #define FA_SETIDMASK (S_ISUID | S_ISGID | S_ISVTX)
95 static const struct fattr bogus = {
96 FA_MODTIME | FA_SIZE | FA_MODE,
111 static struct fattr *defaults[FT_NUMBER];
119 for (i = 0; i < FT_NUMBER; i++) {
120 fa = fattr_new(i, -1);
121 if (i == FT_DIRECTORY)
128 /* Initialize the uid/gid lookup cache. */
138 for (i = 0; i < FT_NUMBER; i++)
139 fattr_free(defaults[i]);
142 const struct fattr *fattr_bogus = &bogus;
144 static char *fattr_scanattr(struct fattr *, int, const char *);
147 fattr_supported(int type)
150 return (fattr_support[type]);
154 fattr_new(int type, time_t modtime)
158 new = xmalloc(sizeof(struct fattr));
159 memset(new, 0, sizeof(struct fattr));
161 if (type != FT_UNKNOWN)
162 new->mask |= FA_FILETYPE;
164 new->modtime = modtime;
165 new->mask |= FA_MODTIME;
167 if (fattr_supported(new->type) & FA_LINKCOUNT) {
168 new->mask |= FA_LINKCOUNT;
174 /* Returns a new file attribute structure based on a stat structure. */
176 fattr_fromstat(struct stat *sb)
180 fa = fattr_new(FT_UNKNOWN, -1);
181 if (S_ISREG(sb->st_mode))
183 else if (S_ISDIR(sb->st_mode))
184 fa->type = FT_DIRECTORY;
185 else if (S_ISCHR(sb->st_mode))
187 else if (S_ISBLK(sb->st_mode))
189 else if (S_ISLNK(sb->st_mode))
190 fa->type = FT_SYMLINK;
192 fa->type = FT_UNKNOWN;
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);
208 if (fa->mask & FA_FLAGS)
209 fa->flags = sb->st_flags;
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;
221 fattr_frompath(const char *path, int nofollow)
228 error = lstat(path, &sb);
230 error = stat(path, &sb);
233 fa = fattr_fromstat(&sb);
234 if (fa->mask & FA_LINKTARGET) {
237 len = readlink(path, buf, sizeof(buf));
242 if ((unsigned)len > sizeof(buf) - 1) {
244 errno = ENAMETOOLONG;
248 fa->linktarget = xstrdup(buf);
260 error = fstat(fd, &sb);
263 fa = fattr_fromstat(&sb);
268 fattr_type(const struct fattr *fa)
274 /* Returns a new file attribute structure from its encoded text form. */
276 fattr_decode(char *attr)
281 fa = fattr_new(FT_UNKNOWN, -1);
282 next = fattr_scanattr(fa, FA_MASK, attr);
283 if (next == NULL || (fa->mask & ~FA_MASK) > 0)
285 if (fa->mask & FA_FILETYPE) {
286 next = fattr_scanattr(fa, FA_FILETYPE, next);
289 if (fa->type < 0 || fa->type > FT_MAX)
290 fa->type = FT_UNKNOWN;
292 /* The filetype attribute is always valid. */
293 fa->mask |= FA_FILETYPE;
294 fa->type = FT_UNKNOWN;
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;
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);
333 fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore)
340 } pieces[FA_NUMBER], *piece;
341 char *cp, *s, *username, *groupname;
343 mode_t mode, modemask;
351 mask = fa->mask & support[fa->type];
353 if (fa->mask & FA_OWNER) {
354 username = getuserbyid(fa->uid);
355 if (username == NULL)
358 if (fa->mask & FA_GROUP) {
359 groupname = getgroupbyid(fa->gid);
360 if (groupname == NULL)
363 if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1)
364 mask &= ~FA_LINKCOUNT;
366 memset(pieces, 0, FA_NUMBER * sizeof(*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;
373 if (mask & FA_FILETYPE) {
374 vallen = snprintf(piece->val, sizeof(piece->val),
376 len += snprintf(piece->len, sizeof(piece->len), "%lld",
377 (long long)vallen) + vallen + 1;
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;
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;
394 if (mask & FA_LINKTARGET) {
395 vallen = strlen(fa->linktarget);
397 piece->ext = fa->linktarget;
398 len += snprintf(piece->len, sizeof(piece->len), "%lld",
399 (long long)vallen) + vallen + 1;
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;
409 if (mask & FA_OWNER) {
410 vallen = strlen(username);
412 piece->ext = username;
413 len += snprintf(piece->len, sizeof(piece->len), "%lld",
414 (long long)vallen) + vallen + 1;
417 if (mask & FA_GROUP) {
418 vallen = strlen(groupname);
420 piece->ext = groupname;
421 len += snprintf(piece->len, sizeof(piece->len), "%lld",
422 (long long)vallen) + vallen + 1;
425 if (mask & FA_MODE) {
426 if (mask & FA_OWNER && mask & FA_GROUP)
427 modemask = FA_SETIDMASK | FA_PERMMASK;
429 modemask = FA_PERMMASK;
430 mode = fa->mode & modemask;
431 vallen = snprintf(piece->val, sizeof(piece->val),
433 len += snprintf(piece->len, sizeof(piece->len), "%lld",
434 (long long)vallen) + vallen + 1;
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;
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;
452 vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
454 len += snprintf(piece->len, sizeof(piece->len), "%lld",
455 (long long)vallen) + vallen + 1;
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;
466 s = xmalloc(len + 1);
471 for (i = 0; i < n; i++) {
473 len = sprintf(cp, "%s#%s", piece->len, piece->ext);
475 len = sprintf(cp, "%s#%s", piece->len, piece->val);
483 fattr_dup(const struct fattr *from)
487 fa = fattr_new(FT_UNKNOWN, -1);
488 fattr_override(fa, from, FA_MASK);
493 fattr_free(struct fattr *fa)
498 if (fa->linktarget != NULL)
499 free(fa->linktarget);
504 fattr_umask(struct fattr *fa, mode_t newumask)
507 if (fa->mask & FA_MODE)
508 fa->mode = fa->mode & ~newumask;
512 fattr_maskout(struct fattr *fa, int mask)
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;
524 fattr_getmask(const struct fattr *fa)
531 fattr_getlinkcount(const struct fattr *fa)
534 return (fa->linkcount);
538 * Eat the specified attribute and put it in the file attribute
539 * structure. Returns NULL on error, or a pointer to the next
540 * attribute to parse.
542 * This would be much prettier if we had strntol() so that we're
543 * not forced to write '\0' to the string before calling strtol()
544 * and then put back the old value...
546 * We need to use (unsigned) long long types here because some
547 * of the opaque types we're parsing (off_t, time_t...) may need
551 fattr_scanattr(struct fattr *fa, int type, const char *attr)
553 char *attrend, *attrstart, *end;
555 unsigned long attrlen;
563 attrlen = strtoul(attr, &end, 10);
564 if (errno || *end != '#')
568 attrend = attrstart + attrlen;
572 /* Using FA_MASK here is a bit bogus semantically. */
575 fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX);
576 if (errno || end != attrend)
581 fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX);
582 if (errno || end != attrend)
587 fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX);
588 if (errno || end != attrend)
593 fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX);
594 if (errno || end != attrend)
598 fa->linktarget = xstrdup(attrstart);
602 fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX);
603 if (errno || end != attrend)
607 error = getuidbyname(attrstart, &fa->uid);
609 fa->mask &= ~FA_OWNER;
612 error = getgidbyname(attrstart, &fa->gid);
614 fa->mask &= ~FA_GROUP;
618 fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX);
619 if (errno || end != attrend)
621 if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
622 modemask = FA_SETIDMASK | FA_PERMMASK;
624 modemask = FA_PERMMASK;
625 fa->mode &= modemask;
629 fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX);
630 if (errno || end != attrend)
635 fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX);
636 if (errno || end != attrend)
641 fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX);
642 if (errno || end != attrend)
647 fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX);
648 if (errno || end != attrend)
659 /* Return a file attribute structure built from the RCS file attributes. */
661 fattr_forcheckout(const struct fattr *rcsattr, mode_t mask)
665 fa = fattr_new(FT_FILE, -1);
666 if (rcsattr->mask & FA_MODE) {
667 if ((rcsattr->mode & 0111) > 0)
677 /* Merge attributes from "from" that aren't present in "fa". */
679 fattr_merge(struct fattr *fa, const struct fattr *from)
682 fattr_override(fa, from, from->mask & ~fa->mask);
685 /* Merge default attributes. */
687 fattr_mergedefault(struct fattr *fa)
690 fattr_merge(fa, defaults[fa->type]);
693 /* Override selected attributes of "fa" with values from "from". */
695 fattr_override(struct fattr *fa, const struct fattr *from, int mask)
699 if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET)
700 free(fa->linktarget);
702 if (mask & FA_FILETYPE)
703 fa->type = from->type;
704 if (mask & FA_MODTIME)
705 fa->modtime = from->modtime;
707 fa->size = from->size;
708 if (mask & FA_LINKTARGET)
709 fa->linktarget = xstrdup(from->linktarget);
711 fa->rdev = from->rdev;
717 fa->mode = from->mode;
719 fa->flags = from->flags;
720 if (mask & FA_LINKCOUNT)
721 fa->linkcount = from->linkcount;
725 fa->inode = from->inode;
730 fattr_makenode(const struct fattr *fa, const char *path)
732 mode_t modemask, mode;
735 if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
736 modemask = FA_SETIDMASK | FA_PERMMASK;
738 modemask = FA_PERMMASK;
740 /* We only implement fattr_makenode() for dirs for now. */
741 assert(fa->type == FT_DIRECTORY);
742 if (fa->mask & FA_MODE)
743 mode = fa->mode & modemask;
746 error = mkdir(path, mode);
751 fattr_delete(const char *path)
756 fa = fattr_frompath(path, FATTR_NOFOLLOW);
765 if (fa->mask & FA_FLAGS && fa->flags != 0) {
767 (void)chflags(path, fa->flags);
771 if (fa->type == FT_DIRECTORY)
774 error = unlink(path);
780 * Changes those attributes we can change. Returns -1 on error,
781 * 0 if no update was needed, and 1 if an update was needed and
782 * it has been applied successfully.
785 fattr_install(struct fattr *fa, const char *topath, const char *frompath)
787 struct timeval tv[2];
789 int error, inplace, mask;
790 mode_t modemask, newmode;
794 mask = fa->mask & fattr_supported(fa->type);
795 if (mask & FA_OWNER && mask & FA_GROUP)
796 modemask = FA_SETIDMASK | FA_PERMMASK;
798 modemask = FA_PERMMASK;
801 if (frompath == NULL) {
802 /* Changing attributes in place. */
806 old = fattr_frompath(topath, FATTR_NOFOLLOW);
808 if (inplace && fattr_equal(fa, old)) {
815 * Determine whether we need to clear the flags of the target.
816 * This is bogus in that it assumes a value of 0 is safe and
817 * that non-zero is unsafe. I'm not really worried by that
818 * since as far as I know that's the way things are.
820 if ((old->mask & FA_FLAGS) && old->flags > 0) {
821 (void)chflags(topath, 0);
826 /* Determine whether we need to remove the target first. */
827 if (!inplace && (fa->type == FT_DIRECTORY) !=
828 (old->type == FT_DIRECTORY)) {
829 if (old->type == FT_DIRECTORY)
830 error = rmdir(topath);
832 error = unlink(topath);
838 /* Change those attributes that we can before moving the file
839 * into place. That makes installation atomic in most cases. */
840 if (mask & FA_MODTIME) {
841 gettimeofday(tv, NULL); /* Access time. */
842 tv[1].tv_sec = fa->modtime; /* Modification time. */
844 error = utimes(frompath, tv);
848 if (mask & FA_OWNER || mask & FA_GROUP) {
855 error = chown(frompath, uid, gid);
859 if (mask & FA_MODE) {
860 newmode = fa->mode & modemask;
861 /* Merge in set*id bits from the old attribute. */
862 if (old != NULL && old->mask & FA_MODE) {
863 newmode |= (old->mode & ~modemask);
864 newmode &= (FA_SETIDMASK | FA_PERMMASK);
866 error = chmod(frompath, newmode);
872 error = rename(frompath, topath);
880 (void)chflags(topath, fa->flags);
890 * Returns 1 if both attributes are equal, 0 otherwise.
892 * This function only compares attributes that are valid in both
893 * files. A file of unknown type ("FT_UNKNOWN") is unequal to
894 * anything, including itself.
897 fattr_equal(const struct fattr *fa1, const struct fattr *fa2)
901 mask = fa1->mask & fa2->mask;
902 if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN)
904 if (mask & FA_MODTIME)
905 if (fa1->modtime != fa2->modtime)
908 if (fa1->size != fa2->size)
910 if (mask & FA_LINKTARGET)
911 if (strcmp(fa1->linktarget, fa2->linktarget) != 0)
914 if (fa1->rdev != fa2->rdev)
917 if (fa1->uid != fa2->uid)
920 if (fa1->gid != fa2->gid)
923 if (fa1->mode != fa2->mode)
926 if (fa1->flags != fa2->flags)
928 if (mask & FA_LINKCOUNT)
929 if (fa1->linkcount != fa2->linkcount)
932 if (fa1->dev != fa2->dev)
935 if (fa1->inode != fa2->inode)