summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormckusick <mckusick@FreeBSD.org>2007-02-26 08:15:56 +0000
committermckusick <mckusick@FreeBSD.org>2007-02-26 08:15:56 +0000
commit01ee9020b350e550aa823f81efbe79cba3e5cb58 (patch)
tree2804c9d36c181a4e54a05d8bca1ea7ca7d284525
parent22aa654f0bcb07fd20f8c8543209224495888a8e (diff)
downloadFreeBSD-src-01ee9020b350e550aa823f81efbe79cba3e5cb58.zip
FreeBSD-src-01ee9020b350e550aa823f81efbe79cba3e5cb58.tar.gz
Update the dump program to save extended attributes. Update
the restore program to restore all dumped extended attributes. If the restore is running as root, it will always be able to restore all extended attributes. If it is not running as root, it makes a best effort to set them. Using the -v command line flag or the `verbose' command in interactive mode will display all the extended attributes being set on files (and at the end on directories) that are being restored. It will note any extended attributes that could not be set. The extended attributes are placed on the dump image immediately following each file's data. Older versions of restore can work with the newer dump images. Old versions of restore will correctly restore the file data and then (silently) skip over the extended attribute data and proceed to the next file. This resolves PR 93085 which will be closed once the code has been MFC'ed. Note that this code will not compile until these header files have been updated: <protocols/dumprestore.h> and <sys/extattr.h>. PR: bin/93085 Comments from: Poul-Henning Kamp and Robert Watson MFC after: 3 weeks
-rw-r--r--include/protocols/dumprestore.h3
-rw-r--r--sbin/dump/dump.h2
-rw-r--r--sbin/dump/traverse.c184
-rw-r--r--sbin/restore/dirs.c66
-rw-r--r--sbin/restore/extern.h4
-rw-r--r--sbin/restore/restore.h1
-rw-r--r--sbin/restore/tape.c303
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;
OpenPOWER on IntegriCloud