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
This commit is contained in:
Kirk McKusick 2007-02-26 08:15:56 +00:00
parent fdece2b8f7
commit 772ad651bf
7 changed files with 517 additions and 48 deletions

View File

@ -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 */

View File

@ -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);

View File

@ -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,9 +700,124 @@ 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);
}
/*
* Dump a map to the tape.
*/

View File

@ -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;
@ -409,6 +411,17 @@ flushent(void)
dirloc = 0;
}
/*
* 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.
@ -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);
}
ep->e_flags &= ~NEW;
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);
}
}
(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;

View File

@ -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);

View File

@ -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;

View File

@ -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);
@ -689,6 +718,185 @@ extractfile(char *name)
/* NOTREACHED */
}
/*
* 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
*/
@ -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 @@ getfile(void (*fill)(char *, long), void (*skip)(char *, long))
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 @@ getfile(void (*fill)(char *, long), void (*skip)(char *, long))
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,11 +1021,54 @@ getfile(void (*fill)(char *, long), void (*skip)(char *, long))
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.
*/
@ -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;