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 a 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), "%llx",
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 fattr_getlinktarget(const struct fattr *fa)
541 return (fa->linktarget);
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.
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...
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
558 fattr_scanattr(struct fattr *fa, int type, const char *attr)
560 char *attrend, *attrstart, *end;
562 unsigned long attrlen;
570 attrlen = strtoul(attr, &end, 10);
571 if (errno || *end != '#')
575 attrend = attrstart + attrlen;
579 /* Using FA_MASK here is a bit bogus semantically. */
582 fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX);
583 if (errno || end != attrend)
588 fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX);
589 if (errno || end != attrend)
594 fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX);
595 if (errno || end != attrend)
600 fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX);
601 if (errno || end != attrend)
605 fa->linktarget = xstrdup(attrstart);
609 fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX);
610 if (errno || end != attrend)
614 error = getuidbyname(attrstart, &fa->uid);
616 fa->mask &= ~FA_OWNER;
619 error = getgidbyname(attrstart, &fa->gid);
621 fa->mask &= ~FA_GROUP;
625 fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX);
626 if (errno || end != attrend)
628 if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
629 modemask = FA_SETIDMASK | FA_PERMMASK;
631 modemask = FA_PERMMASK;
632 fa->mode &= modemask;
636 fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX);
637 if (errno || end != attrend)
642 fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX);
643 if (errno || end != attrend)
648 fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX);
649 if (errno || end != attrend)
654 fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX);
655 if (errno || end != attrend)
666 /* Return a file attribute structure built from the RCS file attributes. */
668 fattr_forcheckout(const struct fattr *rcsattr, mode_t mask)
672 fa = fattr_new(FT_FILE, -1);
673 if (rcsattr->mask & FA_MODE) {
674 if ((rcsattr->mode & 0111) > 0)
684 /* Merge attributes from "from" that aren't present in "fa". */
686 fattr_merge(struct fattr *fa, const struct fattr *from)
689 fattr_override(fa, from, from->mask & ~fa->mask);
692 /* Merge default attributes. */
694 fattr_mergedefault(struct fattr *fa)
697 fattr_merge(fa, defaults[fa->type]);
700 /* Override selected attributes of "fa" with values from "from". */
702 fattr_override(struct fattr *fa, const struct fattr *from, int mask)
706 if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET)
707 free(fa->linktarget);
709 if (mask & FA_FILETYPE)
710 fa->type = from->type;
711 if (mask & FA_MODTIME)
712 fa->modtime = from->modtime;
714 fa->size = from->size;
715 if (mask & FA_LINKTARGET)
716 fa->linktarget = xstrdup(from->linktarget);
718 fa->rdev = from->rdev;
724 fa->mode = from->mode;
726 fa->flags = from->flags;
727 if (mask & FA_LINKCOUNT)
728 fa->linkcount = from->linkcount;
732 fa->inode = from->inode;
737 fattr_makenode(const struct fattr *fa, const char *path)
739 mode_t modemask, mode;
744 if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
745 modemask = FA_SETIDMASK | FA_PERMMASK;
747 modemask = FA_PERMMASK;
749 /* We only implement fattr_makenode() for dirs for now. */
750 if (fa->mask & FA_MODE)
751 mode = fa->mode & modemask;
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");
768 fattr_delete(const char *path)
773 fa = fattr_frompath(path, FATTR_NOFOLLOW);
782 if (fa->mask & FA_FLAGS && fa->flags != 0) {
784 (void)chflags(path, fa->flags);
788 if (fa->type == FT_DIRECTORY)
791 error = unlink(path);
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.
802 fattr_install(struct fattr *fa, const char *topath, const char *frompath)
804 struct timeval tv[2];
806 int error, inplace, mask;
807 mode_t modemask, newmode;
811 mask = fa->mask & fattr_supported(fa->type);
812 if (mask & FA_OWNER && mask & FA_GROUP)
813 modemask = FA_SETIDMASK | FA_PERMMASK;
815 modemask = FA_PERMMASK;
818 if (frompath == NULL) {
819 /* Changing attributes in place. */
823 old = fattr_frompath(topath, FATTR_NOFOLLOW);
825 if (inplace && fattr_equal(fa, old)) {
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.
837 if ((old->mask & FA_FLAGS) && old->flags > 0) {
838 (void)chflags(topath, 0);
844 * If it is changed from a file to a symlink, remove the file
845 * and create the symlink.
847 if (inplace && (fa->type == FT_SYMLINK) &&
848 (old->type == FT_FILE)) {
849 error = unlink(topath);
852 error = symlink(fa->linktarget, topath);
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);
862 error = unlink(topath);
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. */
874 error = utimes(frompath, tv);
878 if (mask & FA_OWNER || mask & FA_GROUP) {
885 error = chown(frompath, uid, gid);
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);
897 error = chmod(frompath, newmode);
903 error = rename(frompath, topath);
911 (void)chflags(topath, fa->flags);
921 * Returns 1 if both attributes are equal, 0 otherwise.
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.
928 fattr_equal(const struct fattr *fa1, const struct fattr *fa2)
932 mask = fa1->mask & fa2->mask;
933 if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN)
935 if (mask & FA_FILETYPE)
936 if (fa1->type != fa2->type)
938 if (mask & FA_MODTIME)
939 if (fa1->modtime != fa2->modtime)
942 if (fa1->size != fa2->size)
944 if (mask & FA_LINKTARGET)
945 if (strcmp(fa1->linktarget, fa2->linktarget) != 0)
948 if (fa1->rdev != fa2->rdev)
951 if (fa1->uid != fa2->uid)
954 if (fa1->gid != fa2->gid)
957 if (fa1->mode != fa2->mode)
960 if (fa1->flags != fa2->flags)
962 if (mask & FA_LINKCOUNT)
963 if (fa1->linkcount != fa2->linkcount)
966 if (fa1->dev != fa2->dev)
969 if (fa1->inode != fa2->inode)
975 * Must have to get the correct filesize sendt by the server.
978 fattr_filesize(const struct fattr *fa)