| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| |
| #include "libxfs.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 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; |
| } |
| |
| char * |
| setup_proto( |
| char *fname) |
| { |
| char *buf = NULL; |
| static char dflt[] = "d--755 0 0 $"; |
| int fd; |
| long size; |
| |
| if (!fname) |
| return dflt; |
| 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; |
| } |
| |
| 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); |
| return buf; |
| |
| 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); |
| 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); |
| 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, |
| char **pp, |
| int proto_slashes_are_spaces) |
| { |
| slashes_are_spaces = proto_slashes_are_spaces; |
| parseproto(mp, NULL, fsx, pp, NULL); |
| } |
| |
| /* 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; |
| } |