diff options
-rw-r--r-- | include/protocols/dumprestore.h | 3 | ||||
-rw-r--r-- | sbin/dump/dump.h | 2 | ||||
-rw-r--r-- | sbin/dump/traverse.c | 184 | ||||
-rw-r--r-- | sbin/restore/dirs.c | 66 | ||||
-rw-r--r-- | sbin/restore/extern.h | 4 | ||||
-rw-r--r-- | sbin/restore/restore.h | 1 | ||||
-rw-r--r-- | sbin/restore/tape.c | 303 |
7 files changed, 516 insertions, 47 deletions
diff --git a/include/protocols/dumprestore.h b/include/protocols/dumprestore.h index 6abf922..0b69e2d 100644 --- a/include/protocols/dumprestore.h +++ b/include/protocols/dumprestore.h @@ -97,7 +97,8 @@ union u_spcl { int64_t c_birthtime; /* creation time, seconds */ int64_t c_atime; /* last access time, seconds */ int64_t c_mtime; /* last modified time, seconds */ - int32_t c_spare4[7]; /* old block pointers */ + int32_t c_extsize; /* external attribute size */ + int32_t c_spare4[6]; /* old block pointers */ u_int32_t c_file_flags; /* status flags (chflags) */ int32_t c_spare5[2]; /* old blocks, generation number */ u_int32_t c_uid; /* file owner */ diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h index 2224782..d467668 100644 --- a/sbin/dump/dump.h +++ b/sbin/dump/dump.h @@ -105,8 +105,6 @@ int mapfiles(ino_t maxino, long *tapesize); int mapdirs(ino_t maxino, long *tapesize); /* file dumping routines */ -void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino); -void ufs2_blksout(ufs2_daddr_t *blkp, int frags, ino_t ino); void bread(ufs2_daddr_t blkno, char *buf, int size); ssize_t cread(int fd, void *buf, size_t nbytes, off_t offset); void dumpino(union dinode *dp, ino_t ino); diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c index b05e7a6..cb170d1 100644 --- a/sbin/dump/traverse.c +++ b/sbin/dump/traverse.c @@ -75,7 +75,13 @@ union dinode { static int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size, long *tapesize, int nodump, ino_t maxino); -static void dmpindir(ino_t ino, ufs2_daddr_t blk, int level, off_t *size); +static void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int level, + off_t *size); +static void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino); +static void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, + ino_t ino, int last); +static int appendextdata(union dinode *dp); +static void writeextdata(union dinode *dp, ino_t ino, int added); static int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize, long *tapesize, int nodump, ino_t maxino); static long blockest(union dinode *dp); @@ -450,7 +456,7 @@ searchdir( void dumpino(union dinode *dp, ino_t ino) { - int ind_level, cnt; + int ind_level, cnt, last, added; off_t size; char buf[TP_BSIZE]; @@ -470,6 +476,7 @@ dumpino(union dinode *dp, ino_t ino) if (sblock->fs_magic == FS_UFS1_MAGIC) { spcl.c_mode = dp->dp1.di_mode; spcl.c_size = dp->dp1.di_size; + spcl.c_extsize = 0; spcl.c_atime = _time32_to_time(dp->dp1.di_atime); spcl.c_atimensec = dp->dp1.di_atimensec; spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime); @@ -483,6 +490,7 @@ dumpino(union dinode *dp, ino_t ino) } else { spcl.c_mode = dp->dp2.di_mode; spcl.c_size = dp->dp2.di_size; + spcl.c_extsize = dp->dp2.di_extsize; spcl.c_atime = _time64_to_time(dp->dp2.di_atime); spcl.c_atimensec = dp->dp2.di_atimensec; spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime); @@ -512,6 +520,7 @@ dumpino(union dinode *dp, ino_t ino) DIP(dp, di_size) < sblock->fs_maxsymlinklen) { spcl.c_addr[0] = 1; spcl.c_count = 1; + added = appendextdata(dp); writeheader(ino); if (sblock->fs_magic == FS_UFS1_MAGIC) memmove(buf, (caddr_t)dp->dp1.di_db, @@ -521,6 +530,7 @@ dumpino(union dinode *dp, ino_t ino) (u_long)DIP(dp, di_size)); buf[DIP(dp, di_size)] = '\0'; writerec(buf, 0); + writeextdata(dp, ino, added); return; } /* FALLTHROUGH */ @@ -535,7 +545,9 @@ dumpino(union dinode *dp, ino_t ino) case S_IFSOCK: case S_IFCHR: case S_IFBLK: + added = appendextdata(dp); writeheader(ino); + writeextdata(dp, ino, added); return; default: @@ -543,18 +555,21 @@ dumpino(union dinode *dp, ino_t ino) DIP(dp, di_mode) & IFMT); return; } - if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize) + if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize) { cnt = NDADDR * sblock->fs_frag; - else + last = 0; + } else { cnt = howmany(DIP(dp, di_size), sblock->fs_fsize); + last = 1; + } if (sblock->fs_magic == FS_UFS1_MAGIC) ufs1_blksout(&dp->dp1.di_db[0], cnt, ino); else - ufs2_blksout(&dp->dp2.di_db[0], cnt, ino); + ufs2_blksout(dp, &dp->dp2.di_db[0], cnt, ino, last); if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0) return; for (ind_level = 0; ind_level < NIADDR; ind_level++) { - dmpindir(ino, DIP(dp, di_ib[ind_level]), ind_level, &size); + dmpindir(dp, ino, DIP(dp, di_ib[ind_level]), ind_level, &size); if (size <= 0) return; } @@ -564,13 +579,14 @@ dumpino(union dinode *dp, ino_t ino) * Read indirect blocks, and pass the data blocks to be dumped. */ static void -dmpindir(ino_t ino, ufs2_daddr_t blk, int ind_level, off_t *size) +dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int ind_level, + off_t *size) { union { ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)]; ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)]; } idblk; - int i, cnt; + int i, cnt, last; if (blk != 0) bread(fsbtodb(sblock, blk), (char *)&idblk, @@ -578,23 +594,26 @@ dmpindir(ino_t ino, ufs2_daddr_t blk, int ind_level, off_t *size) else memset(&idblk, 0, sblock->fs_bsize); if (ind_level <= 0) { - if (*size < NINDIR(sblock) * sblock->fs_bsize) - cnt = howmany(*size, sblock->fs_fsize); - else + if (*size > NINDIR(sblock) * sblock->fs_bsize) { cnt = NINDIR(sblock) * sblock->fs_frag; + last = 0; + } else { + cnt = howmany(*size, sblock->fs_fsize); + last = 1; + } *size -= NINDIR(sblock) * sblock->fs_bsize; if (sblock->fs_magic == FS_UFS1_MAGIC) ufs1_blksout(idblk.ufs1, cnt, ino); else - ufs2_blksout(idblk.ufs2, cnt, ino); + ufs2_blksout(dp, idblk.ufs2, cnt, ino, last); return; } ind_level--; for (i = 0; i < NINDIR(sblock); i++) { if (sblock->fs_magic == FS_UFS1_MAGIC) - dmpindir(ino, idblk.ufs1[i], ind_level, size); + dmpindir(dp, ino, idblk.ufs1[i], ind_level, size); else - dmpindir(ino, idblk.ufs2[i], ind_level, size); + dmpindir(dp, ino, idblk.ufs2[i], ind_level, size); if (*size <= 0) return; } @@ -603,7 +622,7 @@ dmpindir(ino_t ino, ufs2_daddr_t blk, int ind_level, off_t *size) /* * Collect up the data into tape record sized buffers and output them. */ -void +static void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino) { ufs1_daddr_t *bp; @@ -638,13 +657,25 @@ ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino) /* * Collect up the data into tape record sized buffers and output them. */ -void -ufs2_blksout(ufs2_daddr_t *blkp, int frags, ino_t ino) +static void +ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino, + int last) { ufs2_daddr_t *bp; - int i, j, count, blks, tbperdb; + int i, j, count, resid, blks, tbperdb, added; + static int writingextdata = 0; + /* + * Calculate the number of TP_BSIZE blocks to be dumped. + * For filesystems with a fragment size bigger than TP_BSIZE, + * only part of the final fragment may need to be dumped. + */ blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); + if (last) { + resid = howmany(fragoff(sblock, dp->dp2.di_size), TP_BSIZE); + if (resid > 0) + blks -= howmany(sblock->fs_fsize, TP_BSIZE) - resid; + } tbperdb = sblock->fs_bsize >> tp_bshift; for (i = 0; i < blks; i += TP_NINDIR) { if (i + TP_NINDIR > blks) @@ -657,6 +688,8 @@ ufs2_blksout(ufs2_daddr_t *blkp, int frags, ino_t ino) else spcl.c_addr[j - i] = 0; spcl.c_count = count - i; + if (last && !writingextdata) + added = appendextdata(dp); writeheader(ino); bp = &blkp[i / tbperdb]; for (j = i; j < count; j += tbperdb, bp++) @@ -667,7 +700,122 @@ ufs2_blksout(ufs2_daddr_t *blkp, int frags, ino_t ino) dumpblock(*bp, (count - j) * TP_BSIZE); } spcl.c_type = TS_ADDR; + spcl.c_count = 0; + if (last && !writingextdata) { + writingextdata = 1; + writeextdata(dp, ino, added); + writingextdata = 0; + } + } +} + +/* + * If there is room in the current block for the extended attributes + * as well as the file data, update the header to reflect the added + * attribute data at the end. Attributes are placed at the end so that + * old versions of restore will correctly restore the file and simply + * discard the extra data at the end that it does not understand. + * The attribute data is dumped following the file data by the + * writeextdata() function (below). + */ +static int +appendextdata(union dinode *dp) +{ + int i, blks, tbperdb; + + /* + * If no extended attributes, there is nothing to do. + */ + if (spcl.c_extsize == 0) + return (0); + /* + * If there is not enough room at the end of this block + * to add the extended attributes, then rather than putting + * part of them here, we simply push them entirely into a + * new block rather than putting some here and some later. + */ + if (spcl.c_extsize > NXADDR * sblock->fs_bsize) + blks = howmany(NXADDR * sblock->fs_bsize, TP_BSIZE); + else + blks = howmany(spcl.c_extsize, TP_BSIZE); + if (spcl.c_count + blks > TP_NINDIR) + return (0); + /* + * Update the block map in the header to indicate the added + * extended attribute. They will be appended after the file + * data by the writeextdata() routine. + */ + tbperdb = sblock->fs_bsize >> tp_bshift; + for (i = 0; i < blks; i++) + if (&dp->dp2.di_extb[i / tbperdb] != 0) + spcl.c_addr[spcl.c_count + i] = 1; + else + spcl.c_addr[spcl.c_count + i] = 0; + spcl.c_count += blks; + return (blks); +} + +/* + * Dump the extended attribute data. If there was room in the file + * header, then all we need to do is output the data blocks. If there + * was not room in the file header, then an additional TS_ADDR header + * is created to hold the attribute data. + */ +static void +writeextdata(union dinode *dp, ino_t ino, int added) +{ + int i, frags, blks, tbperdb, last; + ufs2_daddr_t *bp; + off_t size; + + /* + * If no extended attributes, there is nothing to do. + */ + if (spcl.c_extsize == 0) + return; + /* + * If there was no room in the file block for the attributes, + * dump them out in a new block, otherwise just dump the data. + */ + if (added == 0) { + if (spcl.c_extsize > NXADDR * sblock->fs_bsize) { + frags = NXADDR * sblock->fs_frag; + last = 0; + } else { + frags = howmany(spcl.c_extsize, sblock->fs_fsize); + last = 1; + } + ufs2_blksout(dp, &dp->dp2.di_extb[0], frags, ino, last); + } else { + if (spcl.c_extsize > NXADDR * sblock->fs_bsize) + blks = howmany(NXADDR * sblock->fs_bsize, TP_BSIZE); + else + blks = howmany(spcl.c_extsize, TP_BSIZE); + tbperdb = sblock->fs_bsize >> tp_bshift; + for (i = 0; i < blks; i += tbperdb) { + bp = &dp->dp2.di_extb[i / tbperdb]; + if (*bp != 0) { + if (i + tbperdb <= blks) + dumpblock(*bp, (int)sblock->fs_bsize); + else + dumpblock(*bp, (blks - i) * TP_BSIZE); + } + } + } + /* + * If an indirect block is added for extended attributes, then + * di_exti below should be changed to the structure element + * that references the extended attribute indirect block. This + * definition is here only to make it compile without complaint. + */ +#define di_exti di_spare[0] + /* + * If the extended attributes fall into an indirect block, + * dump it as well. + */ + if ((size = spcl.c_extsize - NXADDR * sblock->fs_bsize) > 0) + dmpindir(dp, ino, dp->dp2.di_exti, 0, &size); } /* diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c index f12f932..406a0f0 100644 --- a/sbin/restore/dirs.c +++ b/sbin/restore/dirs.c @@ -85,6 +85,7 @@ struct modeinfo { uid_t uid; gid_t gid; int flags; + int extsize; }; /* @@ -114,6 +115,7 @@ static void flushent(void); static struct inotab *inotablookup(ino_t); static RST_DIR *opendirfile(const char *); static void putdir(char *, long); +static void putdirattrs(char *, long); static void putent(struct direct *); static void rst_seekdir(RST_DIR *, long, long); static long rst_telldir(RST_DIR *); @@ -184,7 +186,7 @@ extractdirs(int genmode) return; } itp = allocinotab(&curfile, seekpt); - getfile(putdir, xtrnull); + getfile(putdir, putdirattrs, xtrnull); putent(&nulldir); flushent(); itp->t_size = seekpt - itp->t_seekpt; @@ -410,6 +412,17 @@ flushent(void) } /* + * Save extended attributes for a directory entry to a file. + */ +static void +putdirattrs(char *buf, long size) +{ + + if (mf != NULL) + (void) fwrite(buf, 1, size, mf); +} + +/* * Seek to an entry in a directory. * Only values returned by rst_telldir should be passed to rst_seekdir. * This routine handles many directories in a single file. @@ -543,8 +556,9 @@ setdirmodes(int flags) FILE *mf; struct modeinfo node; struct entry *ep; - char *cp; + char *cp, *buf; const char *tmpdir; + int bufsize; vprintf(stdout, "Set directory mode, owner, and times.\n"); if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') @@ -564,10 +578,27 @@ setdirmodes(int flags) return; } clearerr(mf); + bufsize = 0; for (;;) { (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); if (feof(mf)) break; + if (node.extsize > 0) { + if (bufsize < node.extsize) { + if (bufsize > 0) + free(buf); + if ((buf = malloc(node.extsize)) != 0) { + bufsize = node.extsize; + } else { + bufsize = 0; + } + } + if (bufsize >= node.extsize) { + (void) fread(buf, 1, node.extsize, mf); + } else { + (void) fseek(mf, node.extsize, SEEK_CUR); + } + } ep = lookupino(node.ino); if (command == 'i' || command == 'x') { if (ep == NULL) @@ -582,18 +613,28 @@ setdirmodes(int flags) } if (ep == NULL) { panic("cannot find directory inode %d\n", node.ino); - } else { - cp = myname(ep); - if (!Nflag) { - (void) chown(cp, node.uid, node.gid); - (void) chmod(cp, node.mode); - utimes(cp, node.ctimep); - utimes(cp, node.mtimep); - (void) chflags(cp, node.flags); + continue; + } + cp = myname(ep); + if (!Nflag) { + if (node.extsize > 0) { + if (bufsize >= node.extsize) { + set_extattr_file(cp, buf, node.extsize); + } else { + fprintf(stderr, "Cannot restore %s%s\n", + "extended attributes for ", cp); + } } - ep->e_flags &= ~NEW; + (void) chown(cp, node.uid, node.gid); + (void) chmod(cp, node.mode); + utimes(cp, node.ctimep); + utimes(cp, node.mtimep); + (void) chflags(cp, node.flags); } + ep->e_flags &= ~NEW; } + if (bufsize > 0) + free(buf); if (ferror(mf)) panic("error setting directory modes\n"); (void) fclose(mf); @@ -668,7 +709,7 @@ allocinotab(struct context *ctxp, long seekpt) itp = calloc(1, sizeof(struct inotab)); if (itp == NULL) - panic("no memory directory table\n"); + panic("no memory for directory table\n"); itp->t_next = inotab[INOHASH(ctxp->ino)]; inotab[INOHASH(ctxp->ino)] = itp; itp->t_ino = ctxp->ino; @@ -684,6 +725,7 @@ allocinotab(struct context *ctxp, long seekpt) node.ctimep[0].tv_usec = ctxp->atime_nsec / 1000; node.ctimep[1].tv_sec = ctxp->birthtime_sec; node.ctimep[1].tv_usec = ctxp->birthtime_nsec / 1000; + node.extsize = ctxp->extsize; node.mode = ctxp->mode; node.flags = ctxp->file_flags; node.uid = ctxp->uid; diff --git a/sbin/restore/extern.h b/sbin/restore/extern.h index 603a561..cbecc86 100644 --- a/sbin/restore/extern.h +++ b/sbin/restore/extern.h @@ -54,7 +54,8 @@ void freeentry(struct entry *); void freename(char *); int genliteraldir(char *, ino_t); char *gentempname(struct entry *); -void getfile(void (*)(char *, long), void (*)(char *, long)); +void getfile(void (*)(char *, long), void (*)(char *, long), + void (*)(char *, long)); void getvol(long); void initsymtable(char *); int inodetype(ino_t); @@ -86,6 +87,7 @@ struct direct *rst_readdir(RST_DIR *); void rst_closedir(void *); void runcmdshell(void); char *savename(char *); +void set_extattr_file(char *, void *, int); void setdirmodes(int); void setinput(char *, int); void setup(void); diff --git a/sbin/restore/restore.h b/sbin/restore/restore.h index 43e4db5..8c6a87b 100644 --- a/sbin/restore/restore.h +++ b/sbin/restore/restore.h @@ -115,6 +115,7 @@ struct context { int atime_nsec; /* access time nanoseconds */ int mtime_nsec; /* modified time nanoseconds */ int birthtime_nsec; /* creation time nanoseconds */ + int extsize; /* size of extended attribute data */ off_t size; /* size of file */ char *name; /* name of file */ } curfile; diff --git a/sbin/restore/tape.c b/sbin/restore/tape.c index eb9802f..15c204ac 100644 --- a/sbin/restore/tape.c +++ b/sbin/restore/tape.c @@ -46,6 +46,8 @@ __FBSDID("$FreeBSD$"); #include <sys/mtio.h> #include <sys/stat.h> #include <sys/time.h> +#include <sys/extattr.h> +#include <sys/acl.h> #include <ufs/ufs/dinode.h> #include <protocols/dumprestore.h> @@ -94,10 +96,16 @@ int oldinofmt; /* FreeBSD 1 inode format needs cvt */ #define FLUSHTAPEBUF() blkcnt = ntrec + 1 +char *namespace_names[] = EXTATTR_NAMESPACE_NAMES; + static void accthdr(struct s_spcl *); static int checksum(int *); static void findinode(struct s_spcl *); static void findtapeblksize(void); +static char *setupextattr(int); +static void xtrattr(char *, long); +static void set_extattr_link(char *, void *, int); +static void set_extattr_fd(int, char *, void *, int); static int gethead(struct s_spcl *); static void readtape(char *); static void setdumpnum(void); @@ -271,7 +279,7 @@ setup(void) panic("no memory for active inode map\n"); usedinomap = map; curfile.action = USING; - getfile(xtrmap, xtrmapskip); + getfile(xtrmap, xtrmapskip, xtrmapskip); if (spcl.c_type != TS_BITS) { fprintf(stderr, "Cannot find file dump list\n"); done(1); @@ -281,7 +289,7 @@ setup(void) panic("no memory for file dump list\n"); dumpmap = map; curfile.action = USING; - getfile(xtrmap, xtrmapskip); + getfile(xtrmap, xtrmapskip, xtrmapskip); /* * If there may be whiteout entries on the tape, pretend that the * whiteout inode exists, so that the whiteout entries can be @@ -557,8 +565,10 @@ extractfile(char *name) uid_t uid; gid_t gid; mode_t mode; + int extsize; struct timeval mtimep[2], ctimep[2]; struct entry *ep; + char *buf; curfile.name = name; curfile.action = USING; @@ -570,6 +580,7 @@ extractfile(char *name) ctimep[0].tv_usec = curfile.atime_nsec / 1000; ctimep[1].tv_sec = curfile.birthtime_sec; ctimep[1].tv_usec = curfile.birthtime_nsec / 1000; + extsize = curfile.extsize; uid = curfile.uid; gid = curfile.gid; mode = curfile.mode; @@ -600,13 +611,16 @@ extractfile(char *name) case IFLNK: lnkbuf[0] = '\0'; pathlen = 0; - getfile(xtrlnkfile, xtrlnkskip); + buf = setupextattr(extsize); + getfile(xtrlnkfile, xtrattr, xtrlnkskip); if (pathlen == 0) { vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); return (GOOD); } if (linkit(lnkbuf, name, SYMLINK) == GOOD) { + if (extsize > 0) + set_extattr_link(name, buf, extsize); (void) lchown(name, uid, gid); (void) lchmod(name, mode); (void) lutimes(name, ctimep); @@ -630,7 +644,13 @@ extractfile(char *name) skipfile(); return (FAIL); } - skipfile(); + if (extsize == 0) { + skipfile(); + } else { + buf = setupextattr(extsize); + getfile(xtrnull, xtrattr, xtrnull); + set_extattr_file(name, buf, extsize); + } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimes(name, ctimep); @@ -654,7 +674,13 @@ extractfile(char *name) skipfile(); return (FAIL); } - skipfile(); + if (extsize == 0) { + skipfile(); + } else { + buf = setupextattr(extsize); + getfile(xtrnull, xtrattr, xtrnull); + set_extattr_file(name, buf, extsize); + } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimes(name, ctimep); @@ -677,7 +703,10 @@ extractfile(char *name) skipfile(); return (FAIL); } - getfile(xtrfile, xtrskip); + buf = setupextattr(extsize); + getfile(xtrfile, xtrattr, xtrskip); + if (extsize > 0) + set_extattr_fd(ofile, name, buf, extsize); (void) fchown(ofile, uid, gid); (void) fchmod(ofile, mode); (void) futimes(ofile, ctimep); @@ -690,6 +719,185 @@ extractfile(char *name) } /* + * Set attributes for a file. + */ +void +set_extattr_file(char *name, void *buf, int size) +{ + struct extattr *eap, *eaend; + + vprintf(stdout, "Set attributes for %s:", name); + eaend = buf + size; + for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) { + /* + * Make sure this entry is complete. + */ + if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) { + dprintf(stdout, "\n\t%scorrupted", + eap == buf ? "" : "remainder "); + break; + } + if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY) + continue; + vprintf(stdout, "\n\t%s, (%d bytes), %*s", + namespace_names[eap->ea_namespace], eap->ea_length, + eap->ea_namelength, eap->ea_name); + /* + * First we try the general attribute setting interface. + * However, some attributes can only be set by root or + * by using special interfaces (for example, ACLs). + */ + if (extattr_set_file(name, eap->ea_namespace, eap->ea_name, + EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) { + dprintf(stdout, " (set using extattr_set_file)"); + continue; + } + /* + * If the general interface refuses to set the attribute, + * then we try all the specialized interfaces that we + * know about. + */ + if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && + !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) { + if (acl_set_file(name, ACL_TYPE_ACCESS, + EXTATTR_CONTENT(eap)) != -1) { + dprintf(stdout, " (set using acl_set_file)"); + continue; + } + } + if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && + !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) { + if (acl_set_file(name, ACL_TYPE_DEFAULT, + EXTATTR_CONTENT(eap)) != -1) { + dprintf(stdout, " (set using acl_set_file)"); + continue; + } + } + vprintf(stdout, " (unable to set)"); + } + vprintf(stdout, "\n"); +} + +/* + * Set attributes for a symbolic link. + */ +static void +set_extattr_link(char *name, void *buf, int size) +{ + struct extattr *eap, *eaend; + + vprintf(stdout, "Set attributes for %s:", name); + eaend = buf + size; + for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) { + /* + * Make sure this entry is complete. + */ + if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) { + dprintf(stdout, "\n\t%scorrupted", + eap == buf ? "" : "remainder "); + break; + } + if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY) + continue; + vprintf(stdout, "\n\t%s, (%d bytes), %*s", + namespace_names[eap->ea_namespace], eap->ea_length, + eap->ea_namelength, eap->ea_name); + /* + * First we try the general attribute setting interface. + * However, some attributes can only be set by root or + * by using special interfaces (for example, ACLs). + */ + if (extattr_set_link(name, eap->ea_namespace, eap->ea_name, + EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) { + dprintf(stdout, " (set using extattr_set_link)"); + continue; + } + /* + * If the general interface refuses to set the attribute, + * then we try all the specialized interfaces that we + * know about. + */ + if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && + !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) { + if (acl_set_link_np(name, ACL_TYPE_ACCESS, + EXTATTR_CONTENT(eap)) != -1) { + dprintf(stdout, " (set using acl_set_link_np)"); + continue; + } + } + if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && + !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) { + if (acl_set_link_np(name, ACL_TYPE_DEFAULT, + EXTATTR_CONTENT(eap)) != -1) { + dprintf(stdout, " (set using acl_set_link_np)"); + continue; + } + } + vprintf(stdout, " (unable to set)"); + } + vprintf(stdout, "\n"); +} + +/* + * Set attributes on a file descriptor. + */ +static void +set_extattr_fd(int fd, char *name, void *buf, int size) +{ + struct extattr *eap, *eaend; + + vprintf(stdout, "Set attributes for %s:", name); + eaend = buf + size; + for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) { + /* + * Make sure this entry is complete. + */ + if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) { + dprintf(stdout, "\n\t%scorrupted", + eap == buf ? "" : "remainder "); + break; + } + if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY) + continue; + vprintf(stdout, "\n\t%s, (%d bytes), %*s", + namespace_names[eap->ea_namespace], eap->ea_length, + eap->ea_namelength, eap->ea_name); + /* + * First we try the general attribute setting interface. + * However, some attributes can only be set by root or + * by using special interfaces (for example, ACLs). + */ + if (extattr_set_fd(fd, eap->ea_namespace, eap->ea_name, + EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) { + dprintf(stdout, " (set using extattr_set_fd)"); + continue; + } + /* + * If the general interface refuses to set the attribute, + * then we try all the specialized interfaces that we + * know about. + */ + if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && + !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) { + if (acl_set_fd(fd, EXTATTR_CONTENT(eap)) != -1) { + dprintf(stdout, " (set using acl_set_fd)"); + continue; + } + } + if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && + !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) { + if (acl_set_file(name, ACL_TYPE_DEFAULT, + EXTATTR_CONTENT(eap)) != -1) { + dprintf(stdout, " (set using acl_set_file)"); + continue; + } + } + vprintf(stdout, " (unable to set)"); + } + vprintf(stdout, "\n"); +} + +/* * skip over bit maps on the tape */ void @@ -708,7 +916,7 @@ skipfile(void) { curfile.action = SKIP; - getfile(xtrnull, xtrnull); + getfile(xtrnull, xtrnull, xtrnull); } /* @@ -718,15 +926,20 @@ skipfile(void) * to the skip function. */ void -getfile(void (*fill)(char *, long), void (*skip)(char *, long)) +getfile(void (*datafill)(char *, long), void (*attrfill)(char *, long), + void (*skip)(char *, long)) { int i; - int curblk = 0; - quad_t size = spcl.c_size; + off_t size; + int curblk, attrsize; + void (*fillit)(char *, long); static char clearedbuf[MAXBSIZE]; char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE]; char junk[TP_BSIZE]; + curblk = 0; + size = spcl.c_size; + attrsize = spcl.c_extsize; if (spcl.c_type == TS_END) panic("ran off end of tape\n"); if (spcl.c_magic != FS_UFS2_MAGIC) @@ -734,6 +947,12 @@ getfile(void (*fill)(char *, long), void (*skip)(char *, long)) if (!gettingfile && setjmp(restart) != 0) return; gettingfile++; + fillit = datafill; + if (size == 0 && attrsize > 0) { + fillit = attrfill; + size = attrsize; + attrsize = 0; + } loop: for (i = 0; i < spcl.c_count; i++) { if (!readmapflag && i > TP_NINDIR) { @@ -748,13 +967,13 @@ loop: if (readmapflag || spcl.c_addr[i]) { readtape(&buf[curblk++][0]); if (curblk == fssize / TP_BSIZE) { - (*fill)((char *)buf, (long)(size > TP_BSIZE ? + (*fillit)((char *)buf, (long)(size > TP_BSIZE ? fssize : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } } else { if (curblk > 0) { - (*fill)((char *)buf, (long)(size > TP_BSIZE ? + (*fillit)((char *)buf, (long)(size > TP_BSIZE ? curblk * TP_BSIZE : (curblk - 1) * TP_BSIZE + size)); curblk = 0; @@ -763,6 +982,20 @@ loop: TP_BSIZE : size)); } if ((size -= TP_BSIZE) <= 0) { + if (size > -TP_BSIZE && curblk > 0) { + (*fillit)((char *)buf, + (long)((curblk * TP_BSIZE) + size)); + curblk = 0; + } + if (attrsize > 0) { + fillit = attrfill; + size = attrsize; + attrsize = 0; + continue; + } + if (spcl.c_count - i > 1) + dprintf(stdout, "skipping %d junk block(s)\n", + spcl.c_count - i - 1); for (i++; i < spcl.c_count; i++) { if (!readmapflag && i > TP_NINDIR) { if (Dflag) { @@ -788,12 +1021,55 @@ loop: curfile.name, blksread); } if (curblk > 0) - (*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size)); + panic("getfile: lost data\n"); findinode(&spcl); gettingfile = 0; } /* + * These variables are shared between the next two functions. + */ +static int extbufsize = 0; +static char *extbuf; +static int extloc; + +/* + * Allocate a buffer into which to extract extended attributes. + */ +static char * +setupextattr(int extsize) +{ + + extloc = 0; + if (extsize <= extbufsize) + return (extbuf); + if (extbufsize > 0) + free(extbuf); + if ((extbuf = malloc(extsize)) != NULL) { + extbufsize = extsize; + return (extbuf); + } + extbufsize = 0; + extbuf = NULL; + fprintf(stderr, "Cannot extract %d bytes %s for inode %d, name %s\n", + extsize, "of extended attributes", curfile.ino, curfile.name); + return (NULL); +} + +/* + * Extract the next block of extended attributes. + */ +static void +xtrattr(char *buf, long size) +{ + + if (extloc + size > extbufsize) + panic("overrun attribute buffer\n"); + memmove(&extbuf[extloc], buf, size); + extloc += size; +} + +/* * Write out the next block of a file. */ static void @@ -1283,6 +1559,7 @@ findinode(struct s_spcl *header) curfile.mtime_nsec = header->c_mtimensec; curfile.birthtime_sec = header->c_birthtime; curfile.birthtime_nsec = header->c_birthtimensec; + curfile.extsize = header->c_extsize; curfile.size = header->c_size; curfile.ino = header->c_inumber; break; |