blob: 2b29240db95f7497b5fcd93a2b89a90c9218f061 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "libxfs.h"
#include <dirent.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <linux/xattr.h>
#include "libfrog/convert.h"
#include "proto.h"
/*
* Prototypes for internal functions.
*/
static char *getstr(char **pp);
static void fail(char *msg, int i);
static struct xfs_trans * getres(struct xfs_mount *mp, uint blocks);
static void rsvfile(xfs_mount_t *mp, xfs_inode_t *ip, long long len);
static int newregfile(char **pp, char **fname);
static void rtinit(xfs_mount_t *mp);
static off_t filesize(int fd);
static void populate_from_dir(struct xfs_mount *mp, struct fsxattr *fsxp,
char *source_dir);
static void walk_dir(struct xfs_mount *mp, struct xfs_inode *pip,
struct fsxattr *fsxp, char *path_buf, int path_len);
static int preserve_atime;
static int slashes_are_spaces;
/*
* Use this for block reservations needed for mkfs's conditions
* (basically no fragmentation).
*/
#define MKFS_BLOCKRES_INODE \
((uint)(M_IGEO(mp)->ialloc_blks + (M_IGEO(mp)->inobt_maxlevels - 1)))
#define MKFS_BLOCKRES(rb) \
((uint)(MKFS_BLOCKRES_INODE + XFS_DA_NODE_MAXDEPTH + \
(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1) + (rb)))
static long long
getnum(
const char *str,
unsigned int blksize,
unsigned int sectsize,
bool convert)
{
long long i;
char *sp;
if (convert)
return cvtnum(blksize, sectsize, str);
i = strtoll(str, &sp, 0);
if (i == 0 && sp == str)
return -1LL;
if (*sp != '\0')
return -1LL; /* trailing garbage */
return i;
}
struct proto_source
setup_proto(
char *fname)
{
struct proto_source result = {};
struct stat statbuf;
char *buf = NULL;
static char dflt[] = "d--755 0 0 $";
int fd;
long size;
/*
* If no prototype path is supplied, use the default protofile which
* creates only a root directory.
*/
if (!fname) {
result.type = PROTO_SRC_PROTOFILE;
result.data = dflt;
return result;
}
if ((fd = open(fname, O_RDONLY)) < 0 || (size = filesize(fd)) < 0) {
fprintf(stderr, _("%s: failed to open %s: %s\n"),
progname, fname, strerror(errno));
goto out_fail;
}
if (fstat(fd, &statbuf) < 0)
fail(_("invalid or unreadable source path"), errno);
/*
* Handle directory inputs.
*/
if (S_ISDIR(statbuf.st_mode)) {
close(fd);
result.type = PROTO_SRC_DIR;
result.data = fname;
return result;
}
/*
* Else this is a protofile, let's handle traditionally.
*/
buf = malloc(size + 1);
if (read(fd, buf, size) < size) {
fprintf(stderr, _("%s: read failed on %s: %s\n"),
progname, fname, strerror(errno));
goto out_fail;
}
if (buf[size - 1] != '\n') {
fprintf(stderr, _("%s: proto file %s premature EOF\n"),
progname, fname);
goto out_fail;
}
buf[size] = '\0';
/*
* Skip past the stuff there for compatibility, a string and 2 numbers.
*/
(void)getstr(&buf); /* boot image name */
(void)getnum(getstr(&buf), 0, 0, false); /* block count */
(void)getnum(getstr(&buf), 0, 0, false); /* inode count */
close(fd);
result.type = PROTO_SRC_PROTOFILE;
result.data = buf;
return result;
out_fail:
if (fd >= 0)
close(fd);
free(buf);
exit(1);
}
static void
fail(
char *msg,
int i)
{
fprintf(stderr, "%s: %s [%d - %s]\n", progname, msg, i, strerror(i));
exit(1);
}
void
res_failed(
int i)
{
fail(_("cannot reserve space"), i);
}
static struct xfs_trans *
getres(
struct xfs_mount *mp,
uint blocks)
{
struct xfs_trans *tp;
int i;
uint r;
for (i = 0, r = MKFS_BLOCKRES(blocks); r >= blocks; r--) {
i = -libxfs_trans_alloc_rollable(mp, r, &tp);
if (i == 0)
return tp;
}
res_failed(i);
/* NOTREACHED */
return NULL;
}
static char *
getstr(
char **pp)
{
char c;
char *p;
char *rval;
p = *pp;
while ((c = *p)) {
switch (c) {
case ' ':
case '\t':
case '\n':
p++;
continue;
case ':':
p++;
while (*p++ != '\n')
;
continue;
default:
rval = p;
while (c != ' ' && c != '\t' && c != '\n' && c != '\0')
c = *++p;
*p++ = '\0';
*pp = p;
return rval;
}
}
if (c != '\0') {
fprintf(stderr, _("%s: premature EOF in prototype file\n"),
progname);
exit(1);
}
return NULL;
}
/* Extract directory entry name from a protofile. */
static char *
getdirentname(
char **pp)
{
char *p = getstr(pp);
char *c = p;
if (!p)
return NULL;
if (!slashes_are_spaces)
return p;
/* Replace slash with space because slashes aren't allowed. */
while (*c) {
if (*c == '/')
*c = ' ';
c++;
}
return p;
}
static void
rsvfile(
xfs_mount_t *mp,
xfs_inode_t *ip,
long long llen)
{
int error;
xfs_trans_t *tp;
error = -libxfs_alloc_file_space(ip, 0, llen, XFS_BMAPI_PREALLOC);
if (error) {
fail(_("error reserving space for a file"), error);
exit(1);
}
/*
* update the inode timestamp, mode, and prealloc flag bits
*/
error = -libxfs_trans_alloc_rollable(mp, 0, &tp);
if (error)
fail(_("allocating transaction for a file"), error);
libxfs_trans_ijoin(tp, ip, 0);
VFS_I(ip)->i_mode &= ~S_ISUID;
/*
* Note that we don't have to worry about mandatory
* file locking being disabled here because we only
* clear the S_ISGID bit if the Group execute bit is
* on, but if it was on then mandatory locking wouldn't
* have been enabled.
*/
if (VFS_I(ip)->i_mode & S_IXGRP)
VFS_I(ip)->i_mode &= ~S_ISGID;
libxfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
ip->i_diflags |= XFS_DIFLAG_PREALLOC;
libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("committing space for a file failed"), error);
}
static void
writesymlink(
struct xfs_trans *tp,
struct xfs_inode *ip,
char *buf,
int len)
{
struct xfs_mount *mp = tp->t_mountp;
xfs_extlen_t nb = XFS_B_TO_FSB(mp, len);
int error;
error = -libxfs_symlink_write_target(tp, ip, ip->i_ino, buf, len, nb,
nb);
if (error) {
fprintf(stderr,
_("%s: error %d creating symlink to '%s'.\n"), progname, error, buf);
exit(1);
}
}
static void
writefile_range(
struct xfs_inode *ip,
const char *fname,
int fd,
off_t pos,
uint64_t len)
{
static char buf[131072];
int error;
if (XFS_IS_REALTIME_INODE(ip)) {
fprintf(stderr,
_("%s: creating realtime files from proto file not supported.\n"),
progname);
exit(1);
}
while (len > 0) {
ssize_t read_len;
read_len = pread(fd, buf, min(len, sizeof(buf)), pos);
if (read_len < 0) {
fprintf(stderr, _("%s: read failed on %s: %s\n"),
progname, fname, strerror(errno));
exit(1);
}
error = -libxfs_alloc_file_space(ip, pos, read_len, 0);
if (error)
fail(_("error allocating space for a file"), error);
error = -libxfs_file_write(ip, buf, pos, read_len);
if (error)
fail(_("error writing file"), error);
pos += read_len;
len -= read_len;
}
}
static void
writefile(
struct xfs_inode *ip,
const char *fname,
int fd)
{
struct xfs_trans *tp;
struct xfs_mount *mp = ip->i_mount;
struct stat statbuf;
off_t data_pos;
int error;
/* do not try to read from non-regular files */
error = fstat(fd, &statbuf);
if (error < 0)
fail(_("unable to stat file to copyin"), errno);
if (!S_ISREG(statbuf.st_mode))
return;
data_pos = lseek(fd, 0, SEEK_DATA);
while (data_pos >= 0) {
off_t hole_pos;
hole_pos = lseek(fd, data_pos, SEEK_HOLE);
if (hole_pos < 0) {
/* save error, break */
data_pos = hole_pos;
break;
}
if (hole_pos <= data_pos) {
/* shouldn't happen??? */
break;
}
writefile_range(ip, fname, fd, data_pos, hole_pos - data_pos);
data_pos = lseek(fd, hole_pos, SEEK_DATA);
}
if (data_pos < 0 && errno != ENXIO)
fail(_("error finding file data to import"), errno);
/* extend EOF only after writing all the file data */
error = -libxfs_trans_alloc_inode(ip, &M_RES(mp)->tr_ichange, 0, 0,
false, &tp);
if (error)
fail(_("error creating isize transaction"), error);
libxfs_trans_ijoin(tp, ip, 0);
ip->i_disk_size = statbuf.st_size;
libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("error committing isize transaction"), error);
}
static void
writeattr(
struct xfs_inode *ip,
const char *fname,
int fd,
const char *attrname,
char *valuebuf,
size_t valuelen)
{
struct xfs_da_args args = {
.dp = ip,
.geo = ip->i_mount->m_attr_geo,
.owner = ip->i_ino,
.whichfork = XFS_ATTR_FORK,
.op_flags = XFS_DA_OP_OKNOENT,
.value = valuebuf,
};
ssize_t ret;
int error;
ret = fgetxattr(fd, attrname, valuebuf, valuelen);
/*
* In case of filedescriptors with O_PATH, fgetxattr() will fail with
* EBADF.
* Let's try to fallback to lgetxattr() using input path.
*/
if (ret < 0 && errno == EBADF)
ret = lgetxattr(fname, attrname, valuebuf, valuelen);
if (ret < 0) {
if (errno == EOPNOTSUPP)
return;
fail(_("error collecting xattr value"), errno);
}
if (ret == 0)
return;
if (!strncmp(attrname, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) {
args.name = (unsigned char *)attrname + XATTR_TRUSTED_PREFIX_LEN;
args.attr_filter = LIBXFS_ATTR_ROOT;
} else if (!strncmp(attrname, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) {
args.name = (unsigned char *)attrname + XATTR_SECURITY_PREFIX_LEN;
args.attr_filter = LIBXFS_ATTR_SECURE;
} else if (!strncmp(attrname, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) {
args.name = (unsigned char *)attrname + XATTR_USER_PREFIX_LEN;
args.attr_filter = 0;
} else {
args.name = (unsigned char *)attrname;
args.attr_filter = 0;
}
args.namelen = strlen((char *)args.name);
args.valuelen = ret;
libxfs_attr_sethash(&args);
error = -libxfs_attr_set(&args, XFS_ATTRUPDATE_UPSERT, false);
if (error)
fail(_("setting xattr value"), error);
}
static void
writeattrs(
struct xfs_inode *ip,
const char *fname,
int fd)
{
char *namebuf, *p, *end;
char *valuebuf = NULL;
ssize_t ret;
namebuf = malloc(XATTR_LIST_MAX);
if (!namebuf)
fail(_("error allocating xattr name buffer"), errno);
ret = flistxattr(fd, namebuf, XATTR_LIST_MAX);
/*
* In case of filedescriptors with O_PATH, flistxattr() will fail with
* EBADF.
* Let's try to fallback to llistxattr() using input path.
*/
if (ret < 0 && errno == EBADF)
ret = llistxattr(fname, namebuf, XATTR_LIST_MAX);
if (ret < 0) {
if (errno == EOPNOTSUPP)
goto out_namebuf;
fail(_("error collecting xattr names"), errno);
}
p = namebuf;
end = namebuf + ret;
for (p = namebuf; p < end; p += strlen(p) + 1) {
if (!valuebuf) {
valuebuf = malloc(ATTR_MAX_VALUELEN);
if (!valuebuf)
fail(_("error allocating xattr value buffer"),
errno);
}
writeattr(ip, fname, fd, p, valuebuf, ATTR_MAX_VALUELEN);
}
free(valuebuf);
out_namebuf:
free(namebuf);
}
static int
newregfile(
char **pp,
char **fname)
{
int fd;
off_t size;
*fname = getstr(pp);
if ((fd = open(*fname, O_RDONLY)) < 0 || (size = filesize(fd)) < 0) {
fprintf(stderr, _("%s: cannot open %s: %s\n"),
progname, *fname, strerror(errno));
exit(1);
}
return fd;
}
static void
newdirent(
struct xfs_mount *mp,
struct xfs_trans *tp,
struct xfs_inode *pip,
struct xfs_name *name,
struct xfs_inode *ip,
struct xfs_parent_args *ppargs)
{
int error;
int rsv;
if (!libxfs_dir2_namecheck(name->name, name->len)) {
fprintf(stderr, _("%.*s: invalid directory entry name\n"),
name->len, name->name);
exit(1);
}
rsv = XFS_DIRENTER_SPACE_RES(mp, name->len);
error = -libxfs_dir_createname(tp, pip, name, ip->i_ino, rsv);
if (error)
fail(_("directory createname error"), error);
if (ppargs) {
error = -libxfs_parent_addname(tp, ppargs, pip, name, ip);
if (error)
fail(_("parent addname error"), error);
}
}
static void
newdirectory(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_inode_t *dp,
xfs_inode_t *pdp)
{
int error;
error = -libxfs_dir_init(tp, dp, pdp);
if (error)
fail(_("directory create error"), error);
}
static struct xfs_parent_args *
newpptr(
struct xfs_mount *mp)
{
struct xfs_parent_args *ret;
int error;
error = -libxfs_parent_start(mp, &ret);
if (error)
fail(_("initializing parent pointer"), error);
return ret;
}
struct cred {
uid_t cr_uid;
gid_t cr_gid;
};
static int
creatproto(
struct xfs_trans **tpp,
struct xfs_inode *dp,
mode_t mode,
xfs_dev_t rdev,
struct cred *cr,
struct fsxattr *fsx,
struct xfs_inode **ipp)
{
struct xfs_icreate_args args = {
.idmap = libxfs_nop_idmap,
.pip = dp,
.rdev = rdev,
.mode = mode,
};
struct xfs_inode *ip;
struct inode *inode;
xfs_ino_t ino;
int error;
/* Root directories cannot be linked to a parent. */
if (!dp)
args.flags |= XFS_ICREATE_UNLINKABLE;
/*
* Call the space management code to pick the on-disk inode to be
* allocated.
*/
error = -libxfs_dialloc(tpp, &args, &ino);
if (error)
return error;
error = -libxfs_icreate(*tpp, ino, &args, &ip);
if (error)
return error;
inode = VFS_I(ip);
i_uid_write(inode, cr->cr_uid);
i_gid_write(inode, cr->cr_gid);
/* If there is no parent dir, initialize the file from fsxattr data. */
if (dp == NULL) {
ip->i_projid = fsx->fsx_projid;
ip->i_extsize = fsx->fsx_extsize;
ip->i_diflags = xfs_flags2diflags(ip, fsx->fsx_xflags);
if (xfs_has_v3inodes(ip->i_mount)) {
ip->i_diflags2 = xfs_flags2diflags2(ip,
fsx->fsx_xflags);
ip->i_cowextsize = fsx->fsx_cowextsize;
}
/* xfsdump breaks if the root dir has a nonzero generation */
inode->i_generation = 0;
}
libxfs_trans_log_inode(*tpp, ip, XFS_ILOG_CORE);
*ipp = ip;
return 0;
}
/* Create a new metadata root directory. */
static int
create_metadir(
struct xfs_mount *mp)
{
struct xfs_inode *ip = NULL;
struct xfs_trans *tp;
int error;
struct xfs_icreate_args args = {
.mode = S_IFDIR,
.flags = XFS_ICREATE_UNLINKABLE,
};
xfs_ino_t ino;
if (!xfs_has_metadir(mp))
return 0;
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_create,
libxfs_create_space_res(mp, MAXNAMELEN), 0, 0, &tp);
if (error)
return error;
/*
* Create a new inode and set the sb pointer. The primary super is
* still marked inprogress, so we do not need to log the metadirino
* change ourselves.
*/
error = -libxfs_dialloc(&tp, &args, &ino);
if (error)
goto out_cancel;
error = -libxfs_icreate(tp, ino, &args, &ip);
if (error)
goto out_cancel;
mp->m_sb.sb_metadirino = ino;
/*
* Initialize the root directory. There are no ILOCKs in userspace
* so we do not need to drop it here.
*/
libxfs_metafile_set_iflag(tp, ip, XFS_METAFILE_DIR);
error = -libxfs_dir_init(tp, ip, ip);
if (error)
goto out_cancel;
error = -libxfs_trans_commit(tp);
if (error)
goto out_rele;
mp->m_metadirip = ip;
return 0;
out_cancel:
libxfs_trans_cancel(tp);
out_rele:
if (ip)
libxfs_irele(ip);
return error;
}
static void
parseproto(
xfs_mount_t *mp,
xfs_inode_t *pip,
struct fsxattr *fsxp,
char **pp,
char *name)
{
#define IF_REGULAR 0
#define IF_RESERVED 1
#define IF_BLOCK 2
#define IF_CHAR 3
#define IF_DIRECTORY 4
#define IF_SYMLINK 5
#define IF_FIFO 6
char *buf;
int error;
int flags;
int fmt;
int i;
xfs_inode_t *ip;
int fd = -1;
off_t len;
long long llen;
int majdev;
int mindev;
int mode;
char *mstr;
xfs_trans_t *tp;
int val;
int isroot = 0;
struct cred creds;
char *value;
char *fname = NULL;
struct xfs_name xname;
struct xfs_parent_args *ppargs = NULL;
memset(&creds, 0, sizeof(creds));
mstr = getstr(pp);
switch (mstr[0]) {
case '-':
fmt = IF_REGULAR;
break;
case 'r':
fmt = IF_RESERVED;
break;
case 'b':
fmt = IF_BLOCK;
break;
case 'c':
fmt = IF_CHAR;
break;
case 'd':
fmt = IF_DIRECTORY;
break;
case 'l':
fmt = IF_SYMLINK;
break;
case 'p':
fmt = IF_FIFO;
break;
default:
fprintf(stderr, _("%s: bad format string %s\n"),
progname, mstr);
exit(1);
}
mode = 0;
switch (mstr[1]) {
case '-':
break;
case 'u':
mode |= S_ISUID;
break;
default:
fprintf(stderr, _("%s: bad format string %s\n"),
progname, mstr);
exit(1);
}
switch (mstr[2]) {
case '-':
break;
case 'g':
mode |= S_ISGID;
break;
default:
fprintf(stderr, _("%s: bad format string %s\n"),
progname, mstr);
exit(1);
}
val = 0;
for (i = 3; i < 6; i++) {
if (mstr[i] < '0' || mstr[i] > '7') {
fprintf(stderr, _("%s: bad format string %s\n"),
progname, mstr);
exit(1);
}
val = val * 8 + mstr[i] - '0';
}
mode |= val;
creds.cr_uid = (int)getnum(getstr(pp), 0, 0, false);
creds.cr_gid = (int)getnum(getstr(pp), 0, 0, false);
xname.name = (unsigned char *)name;
xname.len = name ? strlen(name) : 0;
xname.type = 0;
flags = XFS_ILOG_CORE;
switch (fmt) {
case IF_REGULAR:
fd = newregfile(pp, &fname);
tp = getres(mp, 0);
ppargs = newpptr(mp);
error = creatproto(&tp, pip, mode | S_IFREG, 0, &creds, fsxp,
&ip);
if (error)
fail(_("Inode allocation failed"), error);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_REG_FILE;
newdirent(mp, tp, pip, &xname, ip, ppargs);
break;
case IF_RESERVED: /* pre-allocated space only */
value = getstr(pp);
llen = getnum(value, mp->m_sb.sb_blocksize,
mp->m_sb.sb_sectsize, true);
if (llen < 0) {
fprintf(stderr,
_("%s: Bad value %s for proto file %s\n"),
progname, value, name);
exit(1);
}
tp = getres(mp, XFS_B_TO_FSB(mp, llen));
ppargs = newpptr(mp);
error = creatproto(&tp, pip, mode | S_IFREG, 0, &creds, fsxp,
&ip);
if (error)
fail(_("Inode pre-allocation failed"), error);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_REG_FILE;
newdirent(mp, tp, pip, &xname, ip, ppargs);
libxfs_trans_log_inode(tp, ip, flags);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Space preallocation failed."), error);
libxfs_parent_finish(mp, ppargs);
rsvfile(mp, ip, llen);
libxfs_irele(ip);
return;
case IF_BLOCK:
tp = getres(mp, 0);
ppargs = newpptr(mp);
majdev = getnum(getstr(pp), 0, 0, false);
mindev = getnum(getstr(pp), 0, 0, false);
error = creatproto(&tp, pip, mode | S_IFBLK,
IRIX_MKDEV(majdev, mindev), &creds, fsxp, &ip);
if (error) {
fail(_("Inode allocation failed"), error);
}
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_BLKDEV;
newdirent(mp, tp, pip, &xname, ip, ppargs);
flags |= XFS_ILOG_DEV;
break;
case IF_CHAR:
tp = getres(mp, 0);
ppargs = newpptr(mp);
majdev = getnum(getstr(pp), 0, 0, false);
mindev = getnum(getstr(pp), 0, 0, false);
error = creatproto(&tp, pip, mode | S_IFCHR,
IRIX_MKDEV(majdev, mindev), &creds, fsxp, &ip);
if (error)
fail(_("Inode allocation failed"), error);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_CHRDEV;
newdirent(mp, tp, pip, &xname, ip, ppargs);
flags |= XFS_ILOG_DEV;
break;
case IF_FIFO:
tp = getres(mp, 0);
ppargs = newpptr(mp);
error = creatproto(&tp, pip, mode | S_IFIFO, 0, &creds, fsxp,
&ip);
if (error)
fail(_("Inode allocation failed"), error);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_FIFO;
newdirent(mp, tp, pip, &xname, ip, ppargs);
break;
case IF_SYMLINK:
buf = getstr(pp);
len = (int)strlen(buf);
tp = getres(mp, XFS_B_TO_FSB(mp, len));
ppargs = newpptr(mp);
error = creatproto(&tp, pip, mode | S_IFLNK, 0, &creds, fsxp,
&ip);
if (error)
fail(_("Inode allocation failed"), error);
writesymlink(tp, ip, buf, len);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_SYMLINK;
newdirent(mp, tp, pip, &xname, ip, ppargs);
break;
case IF_DIRECTORY:
tp = getres(mp, 0);
error = creatproto(&tp, pip, mode | S_IFDIR, 0, &creds, fsxp,
&ip);
if (error)
fail(_("Inode allocation failed"), error);
if (!pip) {
pip = ip;
mp->m_sb.sb_rootino = ip->i_ino;
libxfs_log_sb(tp);
isroot = 1;
} else {
ppargs = newpptr(mp);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_DIR;
newdirent(mp, tp, pip, &xname, ip, ppargs);
libxfs_bumplink(tp, pip);
libxfs_trans_log_inode(tp, pip, XFS_ILOG_CORE);
}
newdirectory(mp, tp, ip, pip);
libxfs_trans_log_inode(tp, ip, flags);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Directory inode allocation failed."), error);
libxfs_parent_finish(mp, ppargs);
/*
* RT initialization. Do this here to ensure that
* the RT inodes get placed after the root inode.
*/
if (isroot) {
error = create_metadir(mp);
if (error)
fail(
_("Creation of the metadata directory inode failed"),
error);
rtinit(mp);
}
tp = NULL;
for (;;) {
name = getdirentname(pp);
if (!name)
break;
if (strcmp(name, "$") == 0)
break;
parseproto(mp, ip, fsxp, pp, name);
}
libxfs_irele(ip);
return;
default:
ASSERT(0);
fail(_("Unknown format"), EINVAL);
}
libxfs_trans_log_inode(tp, ip, flags);
error = -libxfs_trans_commit(tp);
if (error) {
fail(_("Error encountered creating file from prototype file"),
error);
}
libxfs_parent_finish(mp, ppargs);
if (fmt == IF_REGULAR) {
writefile(ip, fname, fd);
writeattrs(ip, fname, fd);
close(fd);
}
libxfs_irele(ip);
}
void
parse_proto(
xfs_mount_t *mp,
struct fsxattr *fsx,
struct proto_source *protosource,
int proto_slashes_are_spaces,
int proto_preserve_atime)
{
preserve_atime = proto_preserve_atime;
slashes_are_spaces = proto_slashes_are_spaces;
/*
* In case of a file input, we will use the prototype file logic else
* we will fallback to populate from dir.
*/
switch(protosource->type) {
case PROTO_SRC_PROTOFILE:
parseproto(mp, NULL, fsx, &protosource->data, NULL);
break;
case PROTO_SRC_DIR:
populate_from_dir(mp, fsx, protosource->data);
break;
case PROTO_SRC_NONE:
fail(_("invalid or unreadable source path"), ENOENT);
}
}
/* Create a sb-rooted metadata file. */
static void
create_sb_metadata_file(
struct xfs_rtgroup *rtg,
enum xfs_rtg_inodes type,
int (*create)(struct xfs_rtgroup *rtg,
struct xfs_inode *ip,
struct xfs_trans *tp,
bool init))
{
struct xfs_mount *mp = rtg_mount(rtg);
struct xfs_icreate_args args = {
.mode = S_IFREG,
.flags = XFS_ICREATE_UNLINKABLE,
};
struct xfs_trans *tp;
struct xfs_inode *ip = NULL;
xfs_ino_t ino;
int error;
error = -libxfs_trans_alloc_rollable(mp, MKFS_BLOCKRES_INODE, &tp);
if (error)
res_failed(error);
error = -libxfs_dialloc(&tp, &args, &ino);
if (error)
goto fail;
error = -libxfs_icreate(tp, ino, &args, &ip);
if (error)
goto fail;
error = create(rtg, ip, tp, true);
if (error < 0)
error = -error;
if (error)
goto fail;
switch (type) {
case XFS_RTGI_BITMAP:
mp->m_sb.sb_rbmino = ip->i_ino;
break;
case XFS_RTGI_SUMMARY:
mp->m_sb.sb_rsumino = ip->i_ino;
break;
default:
error = EFSCORRUPTED;
goto fail;
}
libxfs_log_sb(tp);
error = -libxfs_trans_commit(tp);
if (error)
goto fail;
rtg->rtg_inodes[type] = ip;
return;
fail:
if (ip)
libxfs_irele(ip);
if (error)
fail(_("Realtime inode allocation failed"), error);
}
/*
* Free the whole realtime area using transactions. Each transaction may clear
* up to 32 rtbitmap blocks.
*/
static void
rtfreesp_init(
struct xfs_rtgroup *rtg)
{
struct xfs_mount *mp = rtg_mount(rtg);
struct xfs_trans *tp;
const xfs_rtxnum_t max_rtx = mp->m_rtx_per_rbmblock * 32;
xfs_rtxnum_t start_rtx = 0;
int error;
/*
* First zero the realtime bitmap and summary files.
*/
error = -libxfs_rtfile_initialize_blocks(rtg, XFS_RTGI_BITMAP, 0,
mp->m_sb.sb_rbmblocks, NULL);
if (error)
fail(_("Initialization of rtbitmap inode failed"), error);
error = -libxfs_rtfile_initialize_blocks(rtg, XFS_RTGI_SUMMARY, 0,
mp->m_rsumblocks, NULL);
if (error)
fail(_("Initialization of rtsummary inode failed"), error);
if (!mp->m_sb.sb_rbmblocks)
return;
/*
* Then free the blocks into the allocator, one bitmap block at a time.
*/
while (start_rtx < rtg->rtg_extents) {
xfs_rtxlen_t nr = min(rtg->rtg_extents - start_rtx, max_rtx);
/*
* The rt superblock, if present, must not be marked free.
* This may be the only rtx in the entire volume.
*/
if (xfs_has_rtsb(mp) && rtg_rgno(rtg) == 0 && start_rtx == 0) {
start_rtx++;
nr--;
if (start_rtx == rtg->rtg_extents)
break;
}
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate,
0, 0, 0, &tp);
if (error)
res_failed(error);
libxfs_trans_ijoin(tp, rtg_bitmap(rtg), 0);
error = -libxfs_rtfree_extent(tp, rtg, start_rtx, nr);
if (error) {
fprintf(stderr,
_("Error initializing the realtime free space near rgno %u rtx %lld-%lld (max %lld): %s\n"),
rtg_rgno(rtg),
(unsigned long long)start_rtx,
(unsigned long long)start_rtx + nr - 1,
(unsigned long long)rtg->rtg_extents,
strerror(error));
exit(1);
}
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Initialization of the realtime space failed"),
error);
start_rtx += nr;
}
}
static void
rtinit_nogroups(
struct xfs_mount *mp)
{
struct xfs_rtgroup *rtg = NULL;
while ((rtg = xfs_rtgroup_next(mp, rtg))) {
create_sb_metadata_file(rtg, XFS_RTGI_BITMAP,
libxfs_rtbitmap_create);
create_sb_metadata_file(rtg, XFS_RTGI_SUMMARY,
libxfs_rtsummary_create);
rtfreesp_init(rtg);
}
}
static int
init_rtrmap_for_rtsb(
struct xfs_rtgroup *rtg)
{
struct xfs_mount *mp = rtg_mount(rtg);
struct xfs_trans *tp;
int error;
error = -libxfs_trans_alloc_inode(rtg_rmap(rtg),
&M_RES(mp)->tr_itruncate, 0, 0, false, &tp);
if (error)
return error;
error = -libxfs_rtrmapbt_init_rtsb(mp, rtg, tp);
if (error) {
libxfs_trans_cancel(tp);
return error;
}
return -libxfs_trans_commit(tp);
}
static void
rtinit_groups(
struct xfs_mount *mp)
{
struct xfs_rtgroup *rtg = NULL;
unsigned int i;
int error;
error = -libxfs_rtginode_mkdir_parent(mp);
if (error)
fail(_("rtgroup directory allocation failed"), error);
while ((rtg = xfs_rtgroup_next(mp, rtg))) {
for (i = 0; i < XFS_RTGI_MAX; i++) {
error = -libxfs_rtginode_create(rtg, i, true);
if (error)
fail(_("rt group inode creation failed"),
error);
}
if (xfs_has_rtsb(mp) && xfs_has_rtrmapbt(mp) &&
rtg_rgno(rtg) == 0) {
error = init_rtrmap_for_rtsb(rtg);
if (error)
fail(_("rtrmap rtsb init failed"), error);
}
if (!xfs_has_zoned(mp))
rtfreesp_init(rtg);
}
}
/*
* Allocate the realtime bitmap and summary inodes, and fill in data if any.
*/
static void
rtinit(
struct xfs_mount *mp)
{
if (xfs_has_rtgroups(mp))
rtinit_groups(mp);
else
rtinit_nogroups(mp);
}
static off_t
filesize(
int fd)
{
struct stat stb;
if (fstat(fd, &stb) < 0)
return -1;
return stb.st_size;
}
/* Try to allow as many open directories as possible. */
static void
bump_max_fds(void)
{
struct rlimit rlim = {};
int ret;
ret = getrlimit(RLIMIT_NOFILE, &rlim);
if (ret)
return;
rlim.rlim_cur = rlim.rlim_max;
ret = setrlimit(RLIMIT_NOFILE, &rlim);
if (ret < 0)
fprintf(stderr, _("%s: could not bump fd limit: [ %d - %s]\n"),
progname, errno, strerror(errno));
}
static void
writefsxattrs(
struct xfs_inode *ip,
struct fsxattr *fsxp)
{
ip->i_projid = fsxp->fsx_projid;
ip->i_extsize = fsxp->fsx_extsize;
ip->i_diflags = xfs_flags2diflags(ip, fsxp->fsx_xflags);
if (xfs_has_v3inodes(ip->i_mount)) {
ip->i_diflags2 = xfs_flags2diflags2(ip, fsxp->fsx_xflags);
ip->i_cowextsize = fsxp->fsx_cowextsize;
}
}
static void
writetimestamps(
struct xfs_inode *ip,
struct stat *statbuf)
{
struct timespec64 ts;
/*
* Copy timestamps from source file to destination inode.
* Usually reproducible archives will delete or not register
* atime and ctime, for example:
* https://www.gnu.org/software/tar/manual/html_section/Reproducibility.html
* hence we will only copy mtime, and let ctime/crtime be set to
* current time.
* atime will be copied over if atime is true.
*/
ts.tv_sec = statbuf->st_mtim.tv_sec;
ts.tv_nsec = statbuf->st_mtim.tv_nsec;
inode_set_mtime_to_ts(VFS_I(ip), ts);
/*
* In case of atime option, we will copy the atime timestamp
* from source.
*/
if (preserve_atime) {
ts.tv_sec = statbuf->st_atim.tv_sec;
ts.tv_nsec = statbuf->st_atim.tv_nsec;
inode_set_atime_to_ts(VFS_I(ip), ts);
}
}
struct hardlink {
ino_t src_ino;
xfs_ino_t dst_ino;
};
struct hardlinks {
size_t count;
size_t size;
struct hardlink *entries;
};
/* Growth strategy for hardlink tracking array */
/* Double size for small arrays */
#define HARDLINK_DEFAULT_GROWTH_FACTOR 2
/* Grow by 25% for large arrays */
#define HARDLINK_LARGE_GROWTH_FACTOR 0.25
/* Threshold to switch growth strategies */
#define HARDLINK_THRESHOLD 1024
/* Initial allocation size */
#define HARDLINK_TRACKER_INITIAL_SIZE 4096
/*
* Keep track of source inodes that are from hardlinks so we can retrieve them
* when needed to setup in destination.
*/
static struct hardlinks hardlink_tracker = { 0 };
static void
init_hardlink_tracker(void)
{
hardlink_tracker.size = HARDLINK_TRACKER_INITIAL_SIZE;
hardlink_tracker.entries = calloc(
hardlink_tracker.size,
sizeof(struct hardlink));
if (!hardlink_tracker.entries)
fail(_("error allocating hardlinks tracking array"), errno);
}
static void
cleanup_hardlink_tracker(void)
{
free(hardlink_tracker.entries);
hardlink_tracker.entries = NULL;
hardlink_tracker.count = 0;
hardlink_tracker.size = 0;
}
static xfs_ino_t
get_hardlink_dst_inode(
xfs_ino_t i_ino)
{
for (size_t i = 0; i < hardlink_tracker.count; i++) {
if (hardlink_tracker.entries[i].src_ino == i_ino)
return hardlink_tracker.entries[i].dst_ino;
}
return 0;
}
static void
track_hardlink_inode(
ino_t src_ino,
xfs_ino_t dst_ino)
{
if (hardlink_tracker.count >= hardlink_tracker.size) {
/*
* double for smaller capacity.
* instead grow by 25% steps for larger capacities.
*/
const size_t old_size = hardlink_tracker.size;
size_t new_size = old_size * HARDLINK_DEFAULT_GROWTH_FACTOR;
if (old_size > HARDLINK_THRESHOLD)
new_size = old_size + (old_size * HARDLINK_LARGE_GROWTH_FACTOR);
struct hardlink *resized_array = reallocarray(
hardlink_tracker.entries,
new_size,
sizeof(struct hardlink));
if (!resized_array)
fail(_("error enlarging hardlinks tracking array"), errno);
memset(&resized_array[old_size], 0,
(new_size - old_size) * sizeof(struct hardlink));
hardlink_tracker.entries = resized_array;
hardlink_tracker.size = new_size;
}
hardlink_tracker.entries[hardlink_tracker.count].src_ino = src_ino;
hardlink_tracker.entries[hardlink_tracker.count].dst_ino = dst_ino;
hardlink_tracker.count++;
}
/*
* This function will first check in our tracker if the input hardlink has
* already been stored, if not report false so create_nondir_inode() can continue
* handling the inode as regularly, and later save the source inode in our
* buffer for future consumption.
*/
static bool
handle_hardlink(
struct xfs_mount *mp,
struct xfs_inode *pip,
struct xfs_name xname,
struct stat file_stat)
{
int error;
xfs_ino_t dst_ino;
struct xfs_inode *ip;
struct xfs_trans *tp;
struct xfs_parent_args *ppargs = NULL;
tp = getres(mp, 0);
ppargs = newpptr(mp);
dst_ino = get_hardlink_dst_inode(file_stat.st_ino);
/*
* We didn't find the hardlink inode, this means it's the first time
* we see it, report error so create_nondir_inode() can continue handling the
* inode as a regular file type, and later save the source inode in our
* buffer for future consumption.
*/
if (dst_ino == 0)
return false;
error = -libxfs_iget(mp, NULL, dst_ino, 0, &ip);
if (error)
fail(_("failed to get inode"), error);
/*
* In case the inode was already in our tracker we need to setup the
* hardlink and skip file copy.
*/
libxfs_trans_ijoin(tp, pip, 0);
libxfs_trans_ijoin(tp, ip, 0);
newdirent(mp, tp, pip, &xname, ip, ppargs);
/*
* Increment the link count
*/
libxfs_bumplink(tp, ip);
libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Error encountered creating file from prototype file"), error);
libxfs_parent_finish(mp, ppargs);
libxfs_irele(ip);
return true;
}
static void
create_directory_inode(
struct xfs_mount *mp,
struct xfs_inode *pip,
struct fsxattr *fsxp,
int mode,
struct cred creds,
struct xfs_name xname,
int flags,
struct stat file_stat,
int fd,
char *entryname,
char *path_buf,
int path_len)
{
int error;
struct xfs_inode *ip;
struct xfs_trans *tp;
struct xfs_parent_args *ppargs = NULL;
tp = getres(mp, 0);
ppargs = newpptr(mp);
error = creatproto(&tp, pip, mode, 0, &creds, fsxp, &ip);
if (error)
fail(_("Inode allocation failed"), error);
libxfs_trans_ijoin(tp, pip, 0);
newdirent(mp, tp, pip, &xname, ip, ppargs);
libxfs_bumplink(tp, pip);
libxfs_trans_log_inode(tp, pip, XFS_ILOG_CORE);
newdirectory(mp, tp, ip, pip);
/*
* Copy over timestamps.
*/
writetimestamps(ip, &file_stat);
libxfs_trans_log_inode(tp, ip, flags);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Directory inode allocation failed."), error);
libxfs_parent_finish(mp, ppargs);
tp = NULL;
/*
* Copy over attributes.
*/
writeattrs(ip, entryname, fd);
writefsxattrs(ip, fsxp);
close(fd);
walk_dir(mp, ip, fsxp, path_buf, path_len);
libxfs_irele(ip);
}
static void
create_nondir_inode(
struct xfs_mount *mp,
struct xfs_inode *pip,
struct fsxattr *fsxp,
int mode,
struct cred creds,
struct xfs_name xname,
int flags,
struct stat file_stat,
xfs_dev_t rdev,
int fd,
char *src_fname)
{
char link_target[XFS_SYMLINK_MAXLEN];
int error;
ssize_t link_len = 0;
struct xfs_inode *ip;
struct xfs_trans *tp;
struct xfs_parent_args *ppargs = NULL;
/*
* If handle_hardlink() returns true it means the hardlink has been
* correctly found and set, so we don't need to do anything else.
*/
if (file_stat.st_nlink > 1 && handle_hardlink(mp, pip, xname, file_stat)) {
close(fd);
return;
}
/*
* If instead we have an error it means the hardlink was not registered,
* so we proceed to treat it like a regular file, and save it to our
* tracker later.
*/
tp = getres(mp, 0);
/*
* In case of symlinks, we need to handle things a little differently.
* We need to read out our link target and act accordingly.
*/
if (xname.type == XFS_DIR3_FT_SYMLINK) {
link_len = readlink(src_fname, link_target, XFS_SYMLINK_MAXLEN);
if (link_len < 0)
fail(_("could not resolve symlink"), errno);
if (link_len >= PATH_MAX)
fail(_("symlink target too long"), ENAMETOOLONG);
tp = getres(mp, XFS_B_TO_FSB(mp, link_len));
}
ppargs = newpptr(mp);
error = creatproto(&tp, pip, mode, rdev, &creds, fsxp, &ip);
if (error)
fail(_("Inode allocation failed"), error);
/*
* In case of symlinks, we now write it down, for other file types
* this is handled later before cleanup.
*/
if (xname.type == XFS_DIR3_FT_SYMLINK)
writesymlink(tp, ip, link_target, link_len);
libxfs_trans_ijoin(tp, pip, 0);
newdirent(mp, tp, pip, &xname, ip, ppargs);
/*
* Copy over timestamps.
*/
writetimestamps(ip, &file_stat);
libxfs_trans_log_inode(tp, ip, flags);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Error encountered creating non dir inode"), error);
libxfs_parent_finish(mp, ppargs);
/*
* Copy over file content, attributes, extended attributes and
* timestamps.
*/
if (fd >= 0) {
/* We need to writefile only when not dealing with a symlink. */
if (xname.type != XFS_DIR3_FT_SYMLINK)
writefile(ip, src_fname, fd);
writeattrs(ip, src_fname, fd);
close(fd);
}
/*
* We do fsxattr also for file types where we don't have an fd,
* for example FIFOs.
*/
writefsxattrs(ip, fsxp);
/*
* If we're here it means this is the first time we're encountering an
* hardlink, so we need to store it.
*/
if (file_stat.st_nlink > 1)
track_hardlink_inode(file_stat.st_ino, ip->i_ino);
libxfs_irele(ip);
}
static void
handle_direntry(
struct xfs_mount *mp,
struct xfs_inode *pip,
struct fsxattr *fsxp,
char *path_buf,
int path_len,
struct dirent *entry)
{
char *fname = "";
int flags;
int majdev;
int mindev;
int mode;
int pathfd,fd = -1;
int rdev = 0;
struct stat file_stat;
struct xfs_name xname;
pathfd = open(path_buf, O_NOFOLLOW | O_PATH);
if (pathfd < 0){
fprintf(stderr, _("%s: cannot open %s: %s\n"), progname,
path_buf, strerror(errno));
exit(1);
}
/*
* Symlinks and sockets will need to be opened with O_PATH to work, so
* we handle this special case.
*/
fd = openat(pathfd, entry->d_name, O_NOFOLLOW | O_PATH);
if(fd < 0) {
fprintf(stderr, _("%s: cannot open %s: %s\n"), progname,
path_buf, strerror(errno));
exit(1);
}
if (fstat(fd, &file_stat) < 0) {
fprintf(stderr, _("%s: cannot stat '%s': %s (errno=%d)\n"),
progname, path_buf, strerror(errno), errno);
exit(1);
}
/* Ensure we're within the limits of PATH_MAX. */
size_t avail = PATH_MAX - path_len;
size_t wrote = snprintf(path_buf + path_len, avail, "/%s", entry->d_name);
if (wrote > avail)
fail(path_buf, ENAMETOOLONG);
/*
* Regular files instead need to be reopened with broader flags so we
* check if that's the case and reopen those.
*/
if (!S_ISSOCK(file_stat.st_mode) &&
!S_ISLNK(file_stat.st_mode) &&
!S_ISFIFO(file_stat.st_mode)) {
close(fd);
/*
* Try to open the source file noatime to avoid a flood of
* writes to the source fs, but we can fall back to plain
* readonly mode if we don't have enough permission.
*/
fd = openat(pathfd, entry->d_name,
O_NOFOLLOW | O_RDONLY | O_NOATIME);
if (fd < 0)
fd = openat(pathfd, entry->d_name,
O_NOFOLLOW | O_RDONLY);
if(fd < 0) {
fprintf(stderr, _("%s: cannot open %s: %s\n"), progname,
path_buf, strerror(errno));
exit(1);
}
}
struct cred creds = {
.cr_uid = file_stat.st_uid,
.cr_gid = file_stat.st_gid,
};
xname.name = (unsigned char *)entry->d_name;
xname.len = strlen(entry->d_name);
xname.type = 0;
mode = file_stat.st_mode;
flags = XFS_ILOG_CORE;
switch (file_stat.st_mode & S_IFMT) {
case S_IFDIR:
xname.type = XFS_DIR3_FT_DIR;
create_directory_inode(mp, pip, fsxp, mode, creds, xname, flags,
file_stat, fd, entry->d_name, path_buf,
path_len + strlen(entry->d_name) + 1);
goto out;
case S_IFREG:
xname.type = XFS_DIR3_FT_REG_FILE;
fname = entry->d_name;
break;
case S_IFCHR:
flags |= XFS_ILOG_DEV;
xname.type = XFS_DIR3_FT_CHRDEV;
majdev = major(file_stat.st_rdev);
mindev = minor(file_stat.st_rdev);
rdev = IRIX_MKDEV(majdev, mindev);
fname = entry->d_name;
break;
case S_IFBLK:
flags |= XFS_ILOG_DEV;
xname.type = XFS_DIR3_FT_BLKDEV;
majdev = major(file_stat.st_rdev);
mindev = minor(file_stat.st_rdev);
rdev = IRIX_MKDEV(majdev, mindev);
fname = entry->d_name;
break;
case S_IFLNK:
/*
* Being a symlink we opened the filedescriptor with O_PATH
* this will make flistxattr() and fgetxattr() fail with EBADF,
* so we will need to fallback to llistxattr() and lgetxattr(),
* this will need the full path to the original file, not just
* the entry name.
*/
xname.type = XFS_DIR3_FT_SYMLINK;
fname = path_buf;
break;
case S_IFIFO:
/*
* Being a fifo we opened the filedescriptor with O_PATH
* this will make flistxattr() and fgetxattr() fail with EBADF,
* so we will need to fallback to llistxattr() and lgetxattr(),
* this will need the full path to the original file, not just
* the entry name.
*/
xname.type = XFS_DIR3_FT_FIFO;
fname = path_buf;
break;
case S_IFSOCK:
/*
* Being a socket we opened the filedescriptor with O_PATH
* this will make flistxattr() and fgetxattr() fail with EBADF,
* so we will need to fallback to llistxattr() and lgetxattr(),
* this will need the full path to the original file, not just
* the entry name.
*/
xname.type = XFS_DIR3_FT_SOCK;
fname = path_buf;
break;
default:
break;
}
create_nondir_inode(mp, pip, fsxp, mode, creds, xname, flags, file_stat,
rdev, fd, fname);
out:
/* Reset path_buf to original */
path_buf[path_len] = '\0';
}
/*
* Walk_dir will recursively list files and directories and populate the
* mountpoint *mp with them using handle_direntry().
*/
static void
walk_dir(
struct xfs_mount *mp,
struct xfs_inode *pip,
struct fsxattr *fsxp,
char *path_buf,
int path_len)
{
DIR *dir;
struct dirent *entry;
/*
* Open input directory and iterate over all entries in it.
* when another directory is found, we will recursively call walk_dir.
*/
if ((dir = opendir(path_buf)) == NULL) {
fprintf(stderr, _("%s: cannot open input dir: %s [%d - %s]\n"),
progname, path_buf, errno, strerror(errno));
exit(1);
}
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0)
continue;
handle_direntry(mp, pip, fsxp, path_buf, path_len, entry);
}
closedir(dir);
}
static void
populate_from_dir(
struct xfs_mount *mp,
struct fsxattr *fsxp,
char *cur_path)
{
int error;
int mode;
int fd = -1;
char path_buf[PATH_MAX];
struct stat file_stat;
struct xfs_inode *ip;
struct xfs_trans *tp;
/*
* Initialize path_buf cur_path, strip trailing slashes they're
* automatically added when walking the dir.
*/
if (strlen(cur_path) > 1 && cur_path[strlen(cur_path)-1] == '/')
cur_path[strlen(cur_path)-1] = '\0';
if (snprintf(path_buf, PATH_MAX, "%s", cur_path) >= PATH_MAX)
fail(_("path name too long"), ENAMETOOLONG);
if (lstat(path_buf, &file_stat) < 0) {
fprintf(stderr, _("%s: cannot stat '%s': %s (errno=%d)\n"),
progname, path_buf, strerror(errno), errno);
exit(1);
}
fd = open(path_buf, O_NOFOLLOW | O_RDONLY | O_NOATIME);
if (fd < 0) {
fprintf(stderr, _("%s: cannot open %s: %s\n"),
progname, path_buf, strerror(errno));
exit(1);
}
/*
* We first ensure we have the root inode.
*/
struct cred creds = {
.cr_uid = file_stat.st_uid,
.cr_gid = file_stat.st_gid,
};
mode = file_stat.st_mode;
tp = getres(mp, 0);
error = creatproto(&tp, NULL, mode | S_IFDIR, 0, &creds, fsxp, &ip);
if (error)
fail(_("Inode allocation failed"), error);
mp->m_sb.sb_rootino = ip->i_ino;
libxfs_log_sb(tp);
newdirectory(mp, tp, ip, ip);
libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Inode allocation failed"), error);
libxfs_parent_finish(mp, NULL);
/*
* Copy over attributes.
*/
writeattrs(ip, path_buf, fd);
writefsxattrs(ip, fsxp);
close(fd);
/*
* RT initialization. Do this here to ensure that the RT inodes get
* placed after the root inode.
*/
error = create_metadir(mp);
if (error)
fail(_("Creation of the metadata directory inode failed"), error);
rtinit(mp);
/*
* By nature of walk_dir() we could be opening a great number of fds
* for deeply nested directory trees. try to bump max fds limit.
*/
bump_max_fds();
/*
* Initialize the hardlinks tracker.
*/
init_hardlink_tracker();
/*
* Now that we have a root inode, let's walk the input dir and populate
* the partition.
*/
walk_dir(mp, ip, fsxp, path_buf, strlen(cur_path));
/*
* Cleanup hardlinks tracker.
*/
cleanup_hardlink_tracker();
/*
* We free up our root inode only when we finished populating the root
* filesystem.
*/
libxfs_irele(ip);
}