]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - io/open.c
xfs_io: factor out new get_last_inode() helper
[thirdparty/xfsprogs-dev.git] / io / open.c
index 21de76e4663a0fc6ddde91f55ab016a0013eace5..c8c82b773e102523d553d50c7430f7ca08fb7722 100644 (file)
--- a/io/open.c
+++ b/io/open.c
 /*
- * Copyright (c) 2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
  * published by the Free Software Foundation.
  *
- * This program is distributed in the hope that it would be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
  *
- * Further, this software is distributed without any warranty that it is
- * free of the rightful claim of any third person regarding infringement
- * or the like.  Any license provided herein, whether implied or
- * otherwise, applies only to this software file.  Patent licenses, if
- * any, provided herein do not apply to combinations of this program with
- * other software, or any other product whatsoever.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston MA 02111-1307, USA.
- *
- * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
- * Mountain View, CA  94043, or:
- *
- * http://www.sgi.com
- *
- * For further information regarding this notice, see:
- *
- * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#include <xfs/libxfs.h>
 #include "command.h"
+#include "input.h"
 #include "init.h"
+#include "io.h"
+#include "libxfs.h"
+
+#ifndef __O_TMPFILE
+#if defined __alpha__
+#define __O_TMPFILE    0100000000
+#elif defined(__hppa__)
+#define __O_TMPFILE     040000000
+#elif defined(__sparc__)
+#define __O_TMPFILE     0x2000000
+#else
+#define __O_TMPFILE     020000000
+#endif
+#endif /* __O_TMPFILE */
+
+#ifndef O_TMPFILE
+#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#endif
 
 static cmdinfo_t open_cmd;
 static cmdinfo_t stat_cmd;
-static cmdinfo_t setfl_cmd;
+static cmdinfo_t close_cmd;
 static cmdinfo_t statfs_cmd;
+static cmdinfo_t chproj_cmd;
+static cmdinfo_t lsproj_cmd;
 static cmdinfo_t extsize_cmd;
-static int stat_f(int, char **);
+static cmdinfo_t inode_cmd;
+static prid_t prid;
+static long extsize;
+
+off64_t
+filesize(void)
+{
+       struct stat64   st;
+
+       if (fstat64(file->fd, &st) < 0) {
+               perror("fstat64");
+               return -1;
+       }
+       return st.st_size;
+}
+
+static char *
+filetype(mode_t mode)
+{
+       switch (mode & S_IFMT) {
+       case S_IFSOCK:
+               return _("socket");
+       case S_IFDIR:
+               return _("directory");
+       case S_IFCHR:
+               return _("char device");
+       case S_IFBLK:
+               return _("block device");
+       case S_IFREG:
+               return _("regular file");
+       case S_IFLNK:
+               return _("symbolic link");
+       case S_IFIFO:
+               return _("fifo");
+       }
+       return NULL;
+}
+
+static int
+stat_f(
+       int             argc,
+       char            **argv)
+{
+       struct dioattr  dio;
+       struct fsxattr  fsx, fsxa;
+       struct stat64   st;
+       int             verbose = (argc == 2 && !strcmp(argv[1], "-v"));
+
+       printf(_("fd.path = \"%s\"\n"), file->name);
+       printf(_("fd.flags = %s,%s,%s%s%s%s%s\n"),
+               file->flags & IO_OSYNC ? _("sync") : _("non-sync"),
+               file->flags & IO_DIRECT ? _("direct") : _("non-direct"),
+               file->flags & IO_READONLY ? _("read-only") : _("read-write"),
+               file->flags & IO_REALTIME ? _(",real-time") : "",
+               file->flags & IO_APPEND ? _(",append-only") : "",
+               file->flags & IO_NONBLOCK ? _(",non-block") : "",
+               file->flags & IO_TMPFILE ? _(",tmpfile") : "");
+       if (fstat64(file->fd, &st) < 0) {
+               perror("fstat64");
+       } else {
+               printf(_("stat.ino = %lld\n"), (long long)st.st_ino);
+               printf(_("stat.type = %s\n"), filetype(st.st_mode));
+               printf(_("stat.size = %lld\n"), (long long)st.st_size);
+               printf(_("stat.blocks = %lld\n"), (long long)st.st_blocks);
+               if (verbose) {
+                       printf(_("stat.atime = %s"), ctime(&st.st_atime));
+                       printf(_("stat.mtime = %s"), ctime(&st.st_mtime));
+                       printf(_("stat.ctime = %s"), ctime(&st.st_ctime));
+               }
+       }
+       if (file->flags & IO_FOREIGN)
+               return 0;
+       if ((xfsctl(file->name, file->fd, FS_IOC_FSGETXATTR, &fsx)) < 0 ||
+           (xfsctl(file->name, file->fd, XFS_IOC_FSGETXATTRA, &fsxa)) < 0) {
+               perror("FS_IOC_FSGETXATTR");
+       } else {
+               printf(_("fsxattr.xflags = 0x%x "), fsx.fsx_xflags);
+               printxattr(fsx.fsx_xflags, verbose, 0, file->name, 1, 1);
+               printf(_("fsxattr.projid = %u\n"), fsx.fsx_projid);
+               printf(_("fsxattr.extsize = %u\n"), fsx.fsx_extsize);
+               printf(_("fsxattr.nextents = %u\n"), fsx.fsx_nextents);
+               printf(_("fsxattr.naextents = %u\n"), fsxa.fsx_nextents);
+       }
+       if ((xfsctl(file->name, file->fd, XFS_IOC_DIOINFO, &dio)) < 0) {
+               perror("XFS_IOC_DIOINFO");
+       } else {
+               printf(_("dioattr.mem = 0x%x\n"), dio.d_mem);
+               printf(_("dioattr.miniosz = %u\n"), dio.d_miniosz);
+               printf(_("dioattr.maxiosz = %u\n"), dio.d_maxiosz);
+       }
+       return 0;
+}
 
 int
 openfile(
        char            *path,
-       int             aflag,
-       int             cflag,
-       int             dflag,
-       int             rflag,
-       int             sflag,
-       int             tflag,
-       int             xflag)
+       xfs_fsop_geom_t *geom,
+       int             flags,
+       mode_t          mode)
 {
        int             fd;
        int             oflags;
 
-       oflags = (rflag ? O_RDONLY : O_RDWR);
-       if (aflag)
+       oflags = flags & IO_READONLY ? O_RDONLY : O_RDWR;
+       if (flags & IO_APPEND)
                oflags |= O_APPEND;
-       if (cflag)
+       if (flags & IO_CREAT)
                oflags |= O_CREAT;
-       if (dflag)
+       if (flags & IO_DIRECT)
                oflags |= O_DIRECT;
-       if (sflag)
+       if (flags & IO_OSYNC)
                oflags |= O_SYNC;
-       if (tflag)
+       if (flags & IO_TRUNC)
                oflags |= O_TRUNC;
+       if (flags & IO_NONBLOCK)
+               oflags |= O_NONBLOCK;
+       if (flags & IO_TMPFILE)
+               oflags |= O_TMPFILE;
 
-       fd = open(path, oflags, 0644);
+       fd = open(path, oflags, mode);
        if (fd < 0) {
-               perror(path);
-               return -1;
+               if (errno == EISDIR &&
+                   ((oflags & (O_RDWR|O_TMPFILE)) == O_RDWR)) {
+                       /* make it as if we asked for O_RDONLY & try again */
+                       oflags &= ~O_RDWR;
+                       oflags |= O_RDONLY;
+                       flags |= IO_READONLY;
+                       fd = open(path, oflags, mode);
+                       if (fd < 0) {
+                               perror(path);
+                               return -1;
+                       }
+               } else {
+                       perror(path);
+                       return -1;
+               }
        }
-       if (!platform_test_xfs_fd(fd)) {
-               fprintf(stderr, _("%s: specified file "
-                       "[\"%s\"] is not on an XFS filesystem\n"),
-                       progname, fname);
+
+       if (!geom || !platform_test_xfs_fd(fd))
+               return fd;
+
+       if (xfsctl(path, fd, XFS_IOC_FSGEOMETRY, geom) < 0) {
+               perror("XFS_IOC_FSGEOMETRY");
                close(fd);
                return -1;
        }
 
-       if (!readonly && xflag) {       /* read/write and realtime */
+       if (!(flags & IO_READONLY) && (flags & IO_REALTIME)) {
                struct fsxattr  attr;
 
-               if (xfsctl(path, fd, XFS_IOC_FSGETXATTR, &attr) < 0) {
-                       perror("XFS_IOC_FSGETXATTR");
+               if (xfsctl(path, fd, FS_IOC_FSGETXATTR, &attr) < 0) {
+                       perror("FS_IOC_FSGETXATTR");
                        close(fd);
                        return -1;
                }
-               if (!(attr.fsx_xflags & XFS_XFLAG_REALTIME)) {
-                       attr.fsx_xflags |= XFS_XFLAG_REALTIME;
-                       if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &attr) < 0) {
-                               perror("XFS_IOC_FSSETXATTR");
+               if (!(attr.fsx_xflags & FS_XFLAG_REALTIME)) {
+                       attr.fsx_xflags |= FS_XFLAG_REALTIME;
+                       if (xfsctl(path, fd, FS_IOC_FSSETXATTR, &attr) < 0) {
+                               perror("FS_IOC_FSSETXATTR");
                                close(fd);
                                return -1;
                        }
@@ -100,10 +212,39 @@ openfile(
        return fd;
 }
 
-static int
-usage(void)
+int
+addfile(
+       char            *name,
+       int             fd,
+       xfs_fsop_geom_t *geometry,
+       int             flags)
 {
-       printf("%s %s\n", open_cmd.name, open_cmd.oneline);
+       char            *filename;
+
+       filename = strdup(name);
+       if (!filename) {
+               perror("strdup");
+               close(fd);
+               return -1;
+       }
+
+       /* Extend the table of currently open files */
+       filetable = (fileio_t *)realloc(filetable,      /* growing */
+                                       ++filecount * sizeof(fileio_t));
+       if (!filetable) {
+               perror("realloc");
+               filecount = 0;
+               free(filename);
+               close(fd);
+               return -1;
+       }
+
+       /* Finally, make this the new active open file */
+       file = &filetable[filecount - 1];
+       file->fd = fd;
+       file->flags = flags;
+       file->name = filename;
+       file->geom = *geometry;
        return 0;
 }
 
@@ -112,23 +253,27 @@ open_help(void)
 {
        printf(_(
 "\n"
-" opens a new file in the requested mode, after closing the current file\n"
+" opens a new file in the requested mode\n"
 "\n"
 " Example:\n"
-" 'open -d /tmp/data' - opens data file read-write for direct IO\n"
+" 'open -cd /tmp/data' - creates/opens data file read-write for direct IO\n"
 "\n"
 " Opens a file for subsequent use by all of the other xfs_io commands.\n"
 " With no arguments, open uses the stat command to show the current file.\n"
 " -a -- open with the O_APPEND flag (append-only mode)\n"
-" -c -- open with O_CREAT (create the file if it doesn't exist)\n"
 " -d -- open with O_DIRECT (non-buffered IO, note alignment constraints)\n"
+" -f -- open with O_CREAT (create the file if it doesn't exist)\n"
+" -m -- permissions to use in case a new file is created (default 0600)\n"
+" -n -- open with O_NONBLOCK\n"
 " -r -- open with O_RDONLY, the default is O_RDWR\n"
 " -s -- open with O_SYNC\n"
 " -t -- open with O_TRUNC (truncate the file to zero length if it exists)\n"
-" -x -- mark the file as a realtime XFS file immediately after opening it\n"
-" Note1: read/write direct IO requests must be blocksize aligned.\n"
+" -R -- mark the file as a realtime XFS file immediately after opening it\n"
+" -T -- open with O_TMPFILE (create a file not visible in the namespace)\n"
+" Note1: usually read/write direct IO requests must be blocksize aligned;\n"
+"        some kernels, however, allow sectorsize alignment for direct IO.\n"
 " Note2: the bmap for non-regular files can be obtained provided the file\n"
-" was opened appropriately (in particular, must be opened read-only).\n"
+"        was opened correctly (in particular, must be opened read-only).\n"
 "\n"));
 }
 
@@ -137,218 +282,417 @@ open_f(
        int             argc,
        char            **argv)
 {
-       int             aflag = 0;
-       int             cflag = 0;
-       int             dflag = 0;
-       int             rflag = 0;
-       int             sflag = 0;
-       int             tflag = 0;
-       int             xflag = 0;
-       char            *filename;
-       int             fd;
-       int             c;
-
-       if (argc == 1)
-               return stat_f(argc, argv);
+       int             c, fd, flags = 0;
+       char            *sp;
+       mode_t          mode = 0600;
+       xfs_fsop_geom_t geometry = { 0 };
+
+       if (argc == 1) {
+               if (file)
+                       return stat_f(argc, argv);
+               fprintf(stderr, _("no files are open, try 'help open'\n"));
+               return 0;
+       }
 
-       while ((c = getopt(argc, argv, "acdrstx")) != EOF) {
+       while ((c = getopt(argc, argv, "FRTacdfm:nrstx")) != EOF) {
                switch (c) {
+               case 'F':
+                       /* Ignored / deprecated now, handled automatically */
+                       break;
                case 'a':
-                       aflag = 1;
+                       flags |= IO_APPEND;
                        break;
                case 'c':
-                       cflag = 1;
+               case 'f':
+                       flags |= IO_CREAT;
                        break;
                case 'd':
-                       dflag = 1;
+                       flags |= IO_DIRECT;
+                       break;
+               case 'm':
+                       mode = strtoul(optarg, &sp, 0);
+                       if (!sp || sp == optarg) {
+                               printf(_("non-numeric mode -- %s\n"), optarg);
+                               return 0;
+                       }
+                       break;
+               case 'n':
+                       flags |= IO_NONBLOCK;
                        break;
                case 'r':
-                       rflag = 1;
+                       flags |= IO_READONLY;
                        break;
                case 's':
-                       sflag = 1;
+                       flags |= IO_OSYNC;
                        break;
                case 't':
-                       tflag = 1;
+                       flags |= IO_TRUNC;
                        break;
-               case 'x':
-                       xflag = 1;
+               case 'R':
+               case 'x':       /* backwards compatibility */
+                       flags |= IO_REALTIME;
+                       break;
+               case 'T':
+                       flags |= IO_TMPFILE;
                        break;
                default:
-                       return usage();
+                       return command_usage(&open_cmd);
                }
        }
 
        if (optind != argc - 1)
-               return usage();
+               return command_usage(&open_cmd);
+
+       if ((flags & (IO_READONLY|IO_TMPFILE)) == (IO_READONLY|IO_TMPFILE)) {
+               fprintf(stderr, _("-T and -r options are incompatible\n"));
+               return -1;
+       }
 
-       fd = openfile(argv[optind],
-                     aflag, cflag, dflag, rflag, sflag, tflag, xflag);
+       fd = openfile(argv[optind], &geometry, flags, mode);
        if (fd < 0)
                return 0;
 
-       filename = strdup(argv[optind]);
-       if (!filename) {
-               perror("strdup");
-               close(fd);
-               return 0;
-       }
+       if (!platform_test_xfs_fd(fd))
+               flags |= IO_FOREIGN;
 
-       /*
-        * All OK, proceed to make this the new global open file
-        */
-       osync = sflag;
-       trunc = tflag;
-       append = aflag;
-       directio = dflag;
-       readonly = rflag;
-       realtime = xflag;
-       if (fname) {
-               close(fdesc);
-               free(fname);
-       }
-       fname = filename;
-       fdesc = fd;
+       addfile(argv[optind], fd, &geometry, flags);
        return 0;
 }
 
-off64_t
-filesize(void)
+static int
+close_f(
+       int             argc,
+       char            **argv)
 {
-       struct stat64   st;
+       size_t          length;
+       unsigned int    offset;
 
-       if (fstat64(fdesc, &st) < 0) {
-               perror("fstat64");
-               return -1;
+       if (close(file->fd) < 0) {
+               perror("close");
+               return 0;
        }
-       return st.st_size;
+       free(file->name);
+
+       /* Shuffle the file table entries down over the removed entry */
+       offset = file - &filetable[0];
+       length = filecount * sizeof(fileio_t);
+       length -= (offset + 1) * sizeof(fileio_t);
+       if (length)
+               memmove(file, file + 1, length);
+
+       /* Resize the memory allocated for the table, possibly freeing */
+       if (--filecount) {
+               filetable = (fileio_t *)realloc(filetable,      /* shrinking */
+                                       filecount * sizeof(fileio_t));
+               if (offset == filecount)
+                       offset--;
+               file = filetable + offset;
+       } else {
+               free(filetable);
+               file = filetable = NULL;
+       }
+       filelist_f();
+       return 0;
 }
 
-static char *
-filetype(mode_t mode)
+static void
+lsproj_help(void)
 {
-       switch (mode & S_IFMT) {
-       case S_IFSOCK:
-               return _("socket");
-       case S_IFDIR:
-               return _("directory");
-       case S_IFCHR:
-               return _("char device");
-       case S_IFBLK:
-               return _("block device");
-       case S_IFREG:
-               return _("regular file");
-       case S_IFLNK:
-               return _("symbolic link");
-       case S_IFIFO:
-               return _("fifo");
+       printf(_(
+"\n"
+" displays the project identifier associated with the current path\n"
+"\n"
+" Options:\n"
+" -R -- recursively descend (useful when current path is a directory)\n"
+" -D -- recursively descend, but only list projects on directories\n"
+"\n"));
+}
+
+static int
+lsproj_callback(
+       const char              *path,
+       const struct stat       *stat,
+       int                     status,
+       struct FTW              *data)
+{
+       prid_t                  projid;
+       int                     fd;
+
+       if (recurse_dir && !S_ISDIR(stat->st_mode))
+               return 0;
+
+       if ((fd = open(path, O_RDONLY)) == -1) {
+               fprintf(stderr, _("%s: cannot open %s: %s\n"),
+                       progname, path, strerror(errno));
+       } else {
+               if (getprojid(path, fd, &projid) == 0)
+                       printf("[%u] %s\n", (unsigned int)projid, path);
+               close(fd);
        }
-       return NULL;
+       return 0;
 }
 
 static int
-stat_f(
+lsproj_f(
        int             argc,
        char            **argv)
 {
-       struct fsxattr  fsx;
-       struct stat64   st;
-       char            fullname[PATH_MAX + 1];
-
-       printf(_("fd.path = \"%s\"\n"),
-               realpath(fname, fullname) ? fullname : fname);
-       printf(_("fd.flags = %s,%s,%s%s%s\n"),
-               osync ? _("sync") : _("non-sync"),
-               directio ? _("direct") : _("non-direct"),
-               readonly ? _("read-only") : _("read-write"),
-               realtime ? _(",real-time") : "",
-               append ? _(",append-only") : "");
-       if (fstat64(fdesc, &st) < 0) {
-               perror("fstat64");
-       } else {
-               printf(_("stat.ino = %lld\n"), (long long)st.st_ino);
-               printf(_("stat.type = %s\n"), filetype(st.st_mode));
-               printf(_("stat.size = %lld\n"), (long long)st.st_size);
-               printf(_("stat.blocks = %lld\n"), (long long)st.st_blocks);
-               if (argc == 2 && !strcmp(argv[1], "-v")) {
-                       printf(_("stat.atime = %s"), ctime(&st.st_atime));
-                       printf(_("stat.mtime = %s"), ctime(&st.st_mtime));
-                       printf(_("stat.ctime = %s"), ctime(&st.st_ctime));
+       prid_t          projid;
+       int             c;
+
+       recurse_all = recurse_dir = 0;
+       while ((c = getopt(argc, argv, "DR")) != EOF) {
+               switch (c) {
+               case 'D':
+                       recurse_all = 0;
+                       recurse_dir = 1;
+                       break;
+               case 'R':
+                       recurse_all = 1;
+                       recurse_dir = 0;
+                       break;
+               default:
+                       return command_usage(&lsproj_cmd);
                }
        }
-       if ((xfsctl(fname, fdesc, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
-               perror("xfsctl(XFS_IOC_FSGETXATTR)");
-       } else {
-               printf(_("xattr.xflags = 0x%x\n"), fsx.fsx_xflags);
-               printf(_("xattr.extsize = %u\n"), fsx.fsx_extsize);
-               printf(_("xattr.nextents = %u\n"), fsx.fsx_nextents);
-       }
+
+       if (argc != optind)
+               return command_usage(&lsproj_cmd);
+
+       if (recurse_all || recurse_dir)
+               nftw(file->name, lsproj_callback,
+                       100, FTW_PHYS | FTW_MOUNT | FTW_DEPTH);
+       else if (getprojid(file->name, file->fd, &projid) < 0)
+               perror("getprojid");
+       else
+               printf(_("projid = %u\n"), (unsigned int)projid);
        return 0;
 }
 
+static void
+chproj_help(void)
+{
+       printf(_(
+"\n"
+" modifies the project identifier associated with the current path\n"
+"\n"
+" -R -- recursively descend (useful when current path is a directory)\n"
+" -D -- recursively descend, only modifying projects on directories\n"
+"\n"));
+}
+
 static int
-setfl_f(
-       int                     argc,
-       char                    **argv)
+chproj_callback(
+       const char              *path,
+       const struct stat       *stat,
+       int                     status,
+       struct FTW              *data)
 {
-       int                     c, flags;
+       int                     fd;
 
-       flags = fcntl(fdesc, F_GETFL, 0);
-       if (flags < 0) {
-               perror("fcntl(F_GETFL)");
+       if (recurse_dir && !S_ISDIR(stat->st_mode))
                return 0;
+
+       if ((fd = open(path, O_RDONLY)) == -1) {
+               fprintf(stderr, _("%s: cannot open %s: %s\n"),
+                       progname, path, strerror(errno));
+       } else {
+               if (setprojid(path, fd, prid) < 0)
+                       perror("setprojid");
+               close(fd);
        }
+       return 0;
+}
 
-       while ((c = getopt(argc, argv, "ad")) != EOF) {
+static int
+chproj_f(
+       int             argc,
+       char            **argv)
+{
+       int             c;
+
+       recurse_all = recurse_dir = 0;
+       while ((c = getopt(argc, argv, "DR")) != EOF) {
                switch (c) {
-               case 'a':
-                       if (flags & O_APPEND)
-                               flags |= O_APPEND;
-                       else
-                               flags &= ~O_APPEND;
+               case 'D':
+                       recurse_all = 0;
+                       recurse_dir = 1;
                        break;
-               case 'd':
-                       if (flags & O_DIRECT)
-                               flags |= O_DIRECT;
-                       else
-                               flags &= ~O_DIRECT;
+               case 'R':
+                       recurse_all = 1;
+                       recurse_dir = 0;
                        break;
                default:
-                       printf(_("invalid setfl argument -- '%c'\n"), c);
-                       return 0;
+                       return command_usage(&chproj_cmd);
                }
        }
 
-       if (fcntl(fdesc, F_SETFL, flags)  < 0)
-               perror("fcntl(F_SETFL)");
+       if (argc != optind + 1)
+               return command_usage(&chproj_cmd);
+
+       prid = prid_from_string(argv[optind]);
+       if (prid == -1) {
+               printf(_("invalid project ID -- %s\n"), argv[optind]);
+               return 0;
+       }
 
+       if (recurse_all || recurse_dir)
+               nftw(file->name, chproj_callback,
+                       100, FTW_PHYS | FTW_MOUNT | FTW_DEPTH);
+       else if (setprojid(file->name, file->fd, prid) < 0)
+               perror("setprojid");
        return 0;
 }
 
+static void
+extsize_help(void)
+{
+       printf(_(
+"\n"
+" report or modify preferred extent size (in bytes) for the current path\n"
+"\n"
+" -R -- recursively descend (useful when current path is a directory)\n"
+" -D -- recursively descend, only modifying extsize on directories\n"
+"\n"));
+}
+
 static int
-extsize_f(
-       int                     argc,
-       char                    **argv)
+get_extsize(const char *path, int fd)
 {
-       struct fsxattr          fsx;
-       unsigned int            extsize;
-       char                    *sp;
+       struct fsxattr  fsx;
 
-       extsize = strtoul(argv[1], &sp, 0);
-       if (!sp || sp == argv[1]) {
-               printf(_("non-numeric extsize argument -- %s\n"), argv[1]);
+       if ((xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
+               printf("%s: FS_IOC_FSGETXATTR %s: %s\n",
+                       progname, path, strerror(errno));
                return 0;
        }
-       if ((xfsctl(fname, fdesc, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
-               perror("xfsctl(XFS_IOC_FSGETXATTR)");
+       printf("[%u] %s\n", fsx.fsx_extsize, path);
+       return 0;
+}
+
+static int
+set_extsize(const char *path, int fd, long extsz)
+{
+       struct fsxattr  fsx;
+       struct stat64   stat;
+
+       if (fstat64(fd, &stat) < 0) {
+               perror("fstat64");
+               return 0;
+       }
+       if ((xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
+               printf("%s: FS_IOC_FSGETXATTR %s: %s\n",
+                       progname, path, strerror(errno));
                return 0;
        }
-       fsx.fsx_extsize = extsize;
-       if ((xfsctl(fname, fdesc, XFS_IOC_FSSETXATTR, &fsx)) < 0) {
-               perror("xfsctl(XFS_IOC_FSSETXATTR)");
+
+       if (S_ISREG(stat.st_mode)) {
+               fsx.fsx_xflags |= FS_XFLAG_EXTSIZE;
+       } else if (S_ISDIR(stat.st_mode)) {
+               fsx.fsx_xflags |= FS_XFLAG_EXTSZINHERIT;
+       } else {
+               printf(_("invalid target file type - file %s\n"), path);
+               return 0;
+       }
+       fsx.fsx_extsize = extsz;
+
+       if ((xfsctl(path, fd, FS_IOC_FSSETXATTR, &fsx)) < 0) {
+               printf("%s: FS_IOC_FSSETXATTR %s: %s\n",
+                       progname, path, strerror(errno));
+               return 0;
+       }
+
+       return 0;
+}
+
+static int
+get_extsize_callback(
+       const char              *path,
+       const struct stat       *stat,
+       int                     status,
+       struct FTW              *data)
+{
+       int                     fd;
+
+       if (recurse_dir && !S_ISDIR(stat->st_mode))
+               return 0;
+
+       if ((fd = open(path, O_RDONLY)) == -1) {
+               fprintf(stderr, _("%s: cannot open %s: %s\n"),
+                       progname, path, strerror(errno));
+       } else {
+               get_extsize(path, fd);
+               close(fd);
+       }
+       return 0;
+}
+
+static int
+set_extsize_callback(
+       const char              *path,
+       const struct stat       *stat,
+       int                     status,
+       struct FTW              *data)
+{
+       int                     fd;
+
+       if (recurse_dir && !S_ISDIR(stat->st_mode))
                return 0;
+
+       if ((fd = open(path, O_RDONLY)) == -1) {
+               fprintf(stderr, _("%s: cannot open %s: %s\n"),
+                       progname, path, strerror(errno));
+       } else {
+               set_extsize(path, fd, extsize);
+               close(fd);
+       }
+       return 0;
+}
+
+static int
+extsize_f(
+       int             argc,
+       char            **argv)
+{
+       size_t                  blocksize, sectsize;
+       int                     c;
+
+       recurse_all = recurse_dir = 0;
+       init_cvtnum(&blocksize, &sectsize);
+       while ((c = getopt(argc, argv, "DR")) != EOF) {
+               switch (c) {
+               case 'D':
+                       recurse_all = 0;
+                       recurse_dir = 1;
+                       break;
+               case 'R':
+                       recurse_all = 1;
+                       recurse_dir = 0;
+                       break;
+               default:
+                       return command_usage(&extsize_cmd);
+               }
        }
 
+       if (optind < argc) {
+               extsize = (long)cvtnum(blocksize, sectsize, argv[optind]);
+               if (extsize < 0) {
+                       printf(_("non-numeric extsize argument -- %s\n"),
+                               argv[optind]);
+                       return 0;
+               }
+       } else {
+               extsize = -1;
+       }
+
+       if (recurse_all || recurse_dir)
+               nftw(file->name, (extsize >= 0) ?
+                       set_extsize_callback : get_extsize_callback,
+                       100, FTW_PHYS | FTW_MOUNT | FTW_DEPTH);
+       else if (extsize >= 0)
+               set_extsize(file->name, file->fd, extsize);
+       else
+               get_extsize(file->name, file->fd);
        return 0;
 }
 
@@ -357,23 +701,28 @@ statfs_f(
        int                     argc,
        char                    **argv)
 {
-       struct xfs_fsop_geom_v1 fsgeo;
+       struct xfs_fsop_counts  fscounts;
+       struct xfs_fsop_geom    fsgeo;
        struct statfs           st;
-       char                    fullname[PATH_MAX + 1];
 
-       printf(_("fd.path = \"%s\"\n"),
-               realpath(fname, fullname) ? fullname : fname);
-       if (platform_fstatfs(fdesc, &st) < 0) {
+       printf(_("fd.path = \"%s\"\n"), file->name);
+       if (platform_fstatfs(file->fd, &st) < 0) {
                perror("fstatfs");
        } else {
                printf(_("statfs.f_bsize = %lld\n"), (long long) st.f_bsize);
                printf(_("statfs.f_blocks = %lld\n"), (long long) st.f_blocks);
-#if !defined(__sgi__)
+#if defined(__sgi__)
+               printf(_("statfs.f_frsize = %lld\n"), (long long) st.f_frsize);
+#else
                printf(_("statfs.f_bavail = %lld\n"), (long long) st.f_bavail);
 #endif
+               printf(_("statfs.f_files = %lld\n"), (long long) st.f_files);
+               printf(_("statfs.f_ffree = %lld\n"), (long long) st.f_ffree);
        }
-       if ((xfsctl(fname, fdesc, XFS_IOC_FSGEOMETRY_V1, &fsgeo)) < 0) {
-               perror("xfsctl(XFS_IOC_FSGEOMETRY_V1)");
+       if (file->flags & IO_FOREIGN)
+               return 0;
+       if ((xfsctl(file->name, file->fd, XFS_IOC_FSGEOMETRY_V1, &fsgeo)) < 0) {
+               perror("XFS_IOC_FSGEOMETRY_V1");
        } else {
                printf(_("geom.bsize = %u\n"), fsgeo.blocksize);
                printf(_("geom.agcount = %u\n"), fsgeo.agcount);
@@ -388,51 +737,256 @@ statfs_f(
                printf(_("geom.sunit = %u\n"), fsgeo.sunit);
                printf(_("geom.swidth = %u\n"), fsgeo.swidth);
        }
+       if ((xfsctl(file->name, file->fd, XFS_IOC_FSCOUNTS, &fscounts)) < 0) {
+               perror("XFS_IOC_FSCOUNTS");
+       } else {
+               printf(_("counts.freedata = %llu\n"),
+                       (unsigned long long) fscounts.freedata);
+               printf(_("counts.freertx = %llu\n"),
+                       (unsigned long long) fscounts.freertx);
+               printf(_("counts.freeino = %llu\n"),
+                       (unsigned long long) fscounts.freeino);
+               printf(_("counts.allocino = %llu\n"),
+                       (unsigned long long) fscounts.allocino);
+       }
+       return 0;
+}
+
+static void
+inode_help(void)
+{
+       printf(_(
+"\n"
+"Query physical information about an inode"
+"\n"
+" Default:     -- Return 1 if any inode number greater than 32 bits exists in\n"
+"                 the filesystem, or 0 if none exist\n"
+" num          -- Return inode number [num] if in use, or 0 if not in use\n"
+" -n num       -- Return the next used inode after [num]\n"
+" -v           -- Verbose mode - display returned inode number's size in bits\n"
+"\n"));
+}
+
+static __u64
+get_last_inode(void)
+{
+       __u64                   lastip = 0;
+       __u64                   lastgrp = 0;
+       __s32                   ocount = 0;
+       __u64                   last_ino;
+       struct xfs_inogrp       igroup[1024];
+       struct xfs_fsop_bulkreq bulkreq;
+
+       bulkreq.lastip = &lastip;
+       bulkreq.ubuffer = &igroup;
+       bulkreq.icount = sizeof(igroup) / sizeof(struct xfs_inogrp);
+       bulkreq.ocount = &ocount;
+
+       for (;;) {
+               if (xfsctl(file->name, file->fd, XFS_IOC_FSINUMBERS,
+                               &bulkreq)) {
+                       perror("XFS_IOC_FSINUMBERS");
+                       return 0;
+               }
+
+               /* Did we reach the last inode? */
+               if (ocount == 0)
+                       break;
+
+               /* last inode in igroup table */
+               lastgrp = ocount;
+       }
+
+       lastgrp--;
+
+       /* The last inode number in use */
+       last_ino = igroup[lastgrp].xi_startino +
+                 libxfs_highbit64(igroup[lastgrp].xi_allocmask);
+
+       return last_ino;
+}
+
+static int
+inode_f(
+         int                   argc,
+         char                  **argv)
+{
+       __s32                   count = 0;
+       __u64                   lastino = 0;
+       __u64                   userino = 0;
+       char                    *p;
+       int                     c;
+       int                     verbose = 0;
+       int                     ret_next = 0;
+       int                     cmd = 0;
+       struct xfs_fsop_bulkreq bulkreq;
+       struct xfs_bstat        bstat;
+
+       while ((c = getopt(argc, argv, "nv")) != EOF) {
+               switch (c) {
+               case 'v':
+                       verbose = 1;
+                       break;
+               case 'n':
+                       ret_next = 1;
+                       break;
+               default:
+                       return command_usage(&inode_cmd);
+               }
+       }
+
+       /*
+        * Inode number can be passed with or without extra arguments, so we
+        * should handle inode numbers passed by user out of getopt()
+        */
+       if (optind < argc) {
+
+               if (ret_next) {
+                       cmd = XFS_IOC_FSBULKSTAT;
+               } else {
+                       if ((argc > 2) && !verbose)
+                               return command_usage(&inode_cmd);
+                       else
+                               cmd = XFS_IOC_FSBULKSTAT_SINGLE;
+               }
+
+               userino = strtoull(argv[optind], &p, 10);
+               if ((*p != '\0')) {
+                       printf(_("[num] must be a numeric value\n"));
+                       exitcode = 1;
+                       return 0;
+               }
+
+               bulkreq.lastip = &userino;
+               bulkreq.icount = 1;
+               bulkreq.ubuffer = &bstat;
+               bulkreq.ocount = &count;
+
+               if (xfsctl(file->name, file->fd, cmd, &bulkreq)) {
+                       if (errno == EINVAL) {
+                               if (!ret_next)
+                                       printf("0\n");
+                       } else {
+                               perror("xfsctl");
+                       }
+                       exitcode = 1;
+                       return 0;
+               }
+
+               if (ret_next)
+                       userino = bstat.bs_ino;
+
+               if (verbose)
+                       printf("%llu:%d\n",
+                              userino,
+                              userino > XFS_MAXINUMBER_32 ? 64 : 32);
+               else
+                       /* Inode in use */
+                       printf("%llu\n", userino);
+               return 0;
+
+       /* -n option must not be used stand alone */
+       } else if (ret_next) {
+               return command_usage(&inode_cmd);
+       }
+
+       /* We are finding last inode in use */
+       lastino = get_last_inode();
+       if (!lastino) {
+               exitcode = 1;
+               return 0;
+       }
+
+       if (verbose)
+               printf("%llu:%d\n", lastino,
+                       lastino > XFS_MAXINUMBER_32 ? 64 : 32);
+       else
+               printf("%d\n", lastino > XFS_MAXINUMBER_32 ? 1 : 0);
+
        return 0;
 }
 
 void
 open_init(void)
 {
-       open_cmd.name = _("open");
-       open_cmd.altname = _("o");
+       open_cmd.name = "open";
+       open_cmd.altname = "o";
        open_cmd.cfunc = open_f;
        open_cmd.argmin = 0;
        open_cmd.argmax = -1;
-       open_cmd.args = _("[-acdrstx] [path]");
-       open_cmd.oneline =
-               _("close the current file, open file specified by path");
+       open_cmd.flags = CMD_NOMAP_OK | CMD_NOFILE_OK | CMD_FOREIGN_OK;
+       open_cmd.args = _("[-acdrstxT] [path]");
+       open_cmd.oneline = _("open the file specified by path");
        open_cmd.help = open_help;
 
-       stat_cmd.name = _("stat");
+       stat_cmd.name = "stat";
        stat_cmd.cfunc = stat_f;
        stat_cmd.argmin = 0;
        stat_cmd.argmax = 1;
+       stat_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
        stat_cmd.args = _("[-v]");
-       stat_cmd.oneline =
-               _("statistics on the currently open file");
+       stat_cmd.oneline = _("statistics on the currently open file");
 
-       setfl_cmd.name = _("setfl");
-       setfl_cmd.cfunc = setfl_f;
-       setfl_cmd.args = _("[-adx]");
-       setfl_cmd.oneline =
-               _("set/clear append/direct flags on the open file");
+       close_cmd.name = "close";
+       close_cmd.altname = "c";
+       close_cmd.cfunc = close_f;
+       close_cmd.argmin = 0;
+       close_cmd.argmax = 0;
+       close_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
+       close_cmd.oneline = _("close the current open file");
 
-       statfs_cmd.name = _("statfs");
+       statfs_cmd.name = "statfs";
        statfs_cmd.cfunc = statfs_f;
+       statfs_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
        statfs_cmd.oneline =
                _("statistics on the filesystem of the currently open file");
 
-       extsize_cmd.name = _("extsize");
+       chproj_cmd.name = "chproj";
+       chproj_cmd.cfunc = chproj_f;
+       chproj_cmd.args = _("[-D | -R] projid");
+       chproj_cmd.argmin = 1;
+       chproj_cmd.argmax = -1;
+       chproj_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
+       chproj_cmd.oneline =
+               _("change project identifier on the currently open file");
+       chproj_cmd.help = chproj_help;
+
+       lsproj_cmd.name = "lsproj";
+       lsproj_cmd.cfunc = lsproj_f;
+       lsproj_cmd.args = _("[-D | -R]");
+       lsproj_cmd.argmin = 0;
+       lsproj_cmd.argmax = -1;
+       lsproj_cmd.flags = CMD_NOMAP_OK;
+       lsproj_cmd.oneline =
+               _("list project identifier set on the currently open file");
+       lsproj_cmd.help = lsproj_help;
+
+       extsize_cmd.name = "extsize";
        extsize_cmd.cfunc = extsize_f;
-       extsize_cmd.argmin = 1;
-       extsize_cmd.argmax = 1;
+       extsize_cmd.args = _("[-D | -R] [extsize]");
+       extsize_cmd.argmin = 0;
+       extsize_cmd.argmax = -1;
+       extsize_cmd.flags = CMD_NOMAP_OK;
        extsize_cmd.oneline =
-               _("set prefered extent size (in bytes) for the open file");
+               _("get/set preferred extent size (in bytes) for the open file");
+       extsize_cmd.help = extsize_help;
+
+       inode_cmd.name = "inode";
+       inode_cmd.cfunc = inode_f;
+       inode_cmd.args = _("[-nv] [num]");
+       inode_cmd.argmin = 0;
+       inode_cmd.argmax = 3;
+       inode_cmd.flags = CMD_NOMAP_OK;
+       inode_cmd.oneline =
+               _("Query inode number usage in the filesystem");
+       inode_cmd.help = inode_help;
 
        add_command(&open_cmd);
        add_command(&stat_cmd);
-       add_command(&setfl_cmd);
+       add_command(&close_cmd);
        add_command(&statfs_cmd);
+       add_command(&chproj_cmd);
+       add_command(&lsproj_cmd);
        add_command(&extsize_cmd);
+       add_command(&inode_cmd);
 }