[patch] add flags to stat()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all.
This is my first serious attempt at kernel hacking, so here goes. I was reading in LWN concerning the idea of adding flags to the stat system call, so that applications could request specific items they wanted as opposed to having to deal with the whole stat struct. Here is a patch that does just that. It ads a new parameter to the stat family including stat, stat64, and stat (and all their variants) to contain flags, and defines a flag for each entry in the stat structure. These flags are passed down the chain through the various vfs functions, and finally to the individual filesystem. This is where it hits a snag: each filesystem has to implement the actual processing of the flags on its own. However, it still seems to run fine even with these changes, so backwards compatibility doesn't appear to be a problem. If anyone knows some sources to read of a more efficient way to implement something like this, please let me know. All the kernel interfaces I have seen that use stat have been modified to pass 0 (give me everything) for now; we can optimize them later. Thoughts?

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 7b3b94d..73c60cf 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1008,7 +1008,7 @@ loop_get_status(struct loop_device *lo,

  	if (lo->lo_state != Lo_bound)
  		return -ENXIO;
-	error = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat);
+	error = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat, 0);
  	if (error)
  		return error;
  	memset(info, 0, sizeof(*info));
diff --git a/fs/stat.c b/fs/stat.c
index 3a44dcf..a2cdba8 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -37,7 +37,7 @@ void generic_fillattr(struct inode *inod

  EXPORT_SYMBOL(generic_fillattr);

-int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat, unsigned int flags)
  {
  	struct inode *inode = dentry->d_inode;
  	int retval;
@@ -47,10 +47,10 @@ int vfs_getattr(struct vfsmount *mnt, st
  		return retval;

  	if (inode->i_op->getattr)
-		return inode->i_op->getattr(mnt, dentry, stat);
+		return inode->i_op->getattr(mnt, dentry, stat, flags);

  	generic_fillattr(inode, stat);
-	if (!stat->blksize) {
+	if (!stat->blksize && flags & S_GETBLKSIZE) {
  		struct super_block *s = inode->i_sb;
  		unsigned blocks;
  		blocks = (stat->size+s->s_blocksize-1) >> s->s_blocksize_bits;
@@ -62,53 +62,53 @@ int vfs_getattr(struct vfsmount *mnt, st

  EXPORT_SYMBOL(vfs_getattr);

-int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat)
+int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat, unsigned int flags)
  {
  	struct nameidata nd;
  	int error;

  	error = __user_walk_fd(dfd, name, LOOKUP_FOLLOW, &nd);
  	if (!error) {
-		error = vfs_getattr(nd.mnt, nd.dentry, stat);
+		error = vfs_getattr(nd.mnt, nd.dentry, stat, flags);
  		path_release(&nd);
  	}
  	return error;
  }

-int vfs_stat(char __user *name, struct kstat *stat)
+int vfs_stat(char __user *name, struct kstat *stat, unsigned int flags)
  {
-	return vfs_stat_fd(AT_FDCWD, name, stat);
+	return vfs_stat_fd(AT_FDCWD, name, stat, flags);
  }

  EXPORT_SYMBOL(vfs_stat);

-int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat)
+int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat, unsigned int flags)
  {
  	struct nameidata nd;
  	int error;

  	error = __user_walk_fd(dfd, name, 0, &nd);
  	if (!error) {
-		error = vfs_getattr(nd.mnt, nd.dentry, stat);
+		error = vfs_getattr(nd.mnt, nd.dentry, stat, flags);
  		path_release(&nd);
  	}
  	return error;
  }

-int vfs_lstat(char __user *name, struct kstat *stat)
+int vfs_lstat(char __user *name, struct kstat *stat, unsigned int flags)
  {
-	return vfs_lstat_fd(AT_FDCWD, name, stat);
+	return vfs_lstat_fd(AT_FDCWD, name, stat, flags);
  }

  EXPORT_SYMBOL(vfs_lstat);

-int vfs_fstat(unsigned int fd, struct kstat *stat)
+int vfs_fstat(unsigned int fd, struct kstat *stat, unsigned int flags)
  {
  	struct file *f = fget(fd);
  	int error = -EBADF;

  	if (f) {
-		error = vfs_getattr(f->f_vfsmnt, f->f_dentry, stat);
+		error = vfs_getattr(f->f_vfsmnt, f->f_dentry, stat, flags);
  		fput(f);
  	}
  	return error;
@@ -160,7 +160,7 @@ #endif
  asmlinkage long sys_stat(char __user * filename, struct __old_kernel_stat __user * statbuf)
  {
  	struct kstat stat;
-	int error = vfs_stat_fd(AT_FDCWD, filename, &stat);
+	int error = vfs_stat_fd(AT_FDCWD, filename, &stat, 0);

  	if (!error)
  		error = cp_old_stat(&stat, statbuf);
@@ -170,7 +170,7 @@ asmlinkage long sys_stat(char __user * f
  asmlinkage long sys_lstat(char __user * filename, struct __old_kernel_stat __user * statbuf)
  {
  	struct kstat stat;
-	int error = vfs_lstat_fd(AT_FDCWD, filename, &stat);
+	int error = vfs_lstat_fd(AT_FDCWD, filename, &stat, 0);

  	if (!error)
  		error = cp_old_stat(&stat, statbuf);
@@ -180,7 +180,7 @@ asmlinkage long sys_lstat(char __user *
  asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat __user * statbuf)
  {
  	struct kstat stat;
-	int error = vfs_fstat(fd, &stat);
+	int error = vfs_fstat(fd, &stat, 0);

  	if (!error)
  		error = cp_old_stat(&stat, statbuf);
@@ -238,10 +238,13 @@ #endif
  	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
  }

-asmlinkage long sys_newstat(char __user *filename, struct stat __user *statbuf)
+asmlinkage long sys_newstat(char __user *filename, struct stat __user *statbuf, unsigned int flags)
  {
  	struct kstat stat;
-	int error = vfs_stat_fd(AT_FDCWD, filename, &stat);
+	int error;
+        if(!flags) /*The want everything, so let's flip on all the flags.*/
+                --flags;
+	error = vfs_stat_fd(AT_FDCWD, filename, &stat, flags);

  	if (!error)
  		error = cp_new_stat(&stat, statbuf);
@@ -249,10 +252,13 @@ asmlinkage long sys_newstat(char __user
  	return error;
  }

-asmlinkage long sys_newlstat(char __user *filename, struct stat __user *statbuf)
+asmlinkage long sys_newlstat(char __user *filename, struct stat __user *statbuf, unsigned int flags)
  {
  	struct kstat stat;
-	int error = vfs_lstat_fd(AT_FDCWD, filename, &stat);
+	int error;
+        if(!flags) /*The want everything, so let's flip on all the flags.*/
+                --flags;
+	 error = vfs_lstat_fd(AT_FDCWD, filename, &stat, flags);

  	if (!error)
  		error = cp_new_stat(&stat, statbuf);
@@ -262,16 +268,17 @@ asmlinkage long sys_newlstat(char __user

  #if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT)
  asmlinkage long sys_newfstatat(int dfd, char __user *filename,
-				struct stat __user *statbuf, int flag)
+				struct stat __user *statbuf, int flag, unsigned int statflags)
  {
-	struct kstat stat;
  	int error = -EINVAL;
-
+	struct kstat stat;
+        if(!statflags) /*The want everything, so let's flip on all the flags.*/
+                --statflags;
  	if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
  		goto out;

  	if (flag & AT_SYMLINK_NOFOLLOW)
-		error = vfs_lstat_fd(dfd, filename, &stat);
+		error = vfs_lstat_fd(dfd, filename, &stat, statflags);
  	else
  		error = vfs_stat_fd(dfd, filename, &stat);

@@ -283,10 +290,13 @@ out:
  }
  #endif

-asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf)
+asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf, unsigned int statflags)
  {
  	struct kstat stat;
-	int error = vfs_fstat(fd, &stat);
+	int error;
+        if(!statflags) /*The want everything, so let's flip on all the flags.*/
+                --statflags;
+	error = vfs_fstat(fd, &stat, statflags);

  	if (!error)
  		error = cp_new_stat(&stat, statbuf);
@@ -365,30 +375,38 @@ #endif
  	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
  }

-asmlinkage long sys_stat64(char __user * filename, struct stat64 __user * statbuf)
+asmlinkage long sys_stat64(char __user * filename, struct stat64 __user * statbuf, unsigned int flags)
  {
  	struct kstat stat;
-	int error = vfs_stat(filename, &stat);
+	int error;
+        if(!flags) /*The want everything, so let's flip on all the flags.*/
+                --flags;
+	error = vfs_stat(filename, &stat, flags);

  	if (!error)
  		error = cp_new_stat64(&stat, statbuf);

  	return error;
  }
-asmlinkage long sys_lstat64(char __user * filename, struct stat64 __user * statbuf)
+asmlinkage long sys_lstat64(char __user * filename, struct stat64 __user * statbuf, unsigned int flags)
  {
-	struct kstat stat;
-	int error = vfs_lstat(filename, &stat);
-
+ struct kstat stat; + int error;
+       if(!flags) /*The want everything, so let's flip on all the flags.*/
+                --flags;
+	error = vfs_lstat(filename, &stat, flags);
  	if (!error)
  		error = cp_new_stat64(&stat, statbuf);

  	return error;
  }
-asmlinkage long sys_fstat64(unsigned long fd, struct stat64 __user * statbuf)
+asmlinkage long sys_fstat64(unsigned long fd, struct stat64 __user * statbuf, unsigned int flags)
  {
  	struct kstat stat;
-	int error = vfs_fstat(fd, &stat);
+	int error;
+        if(!flags) /*The want everything, so let's flip on all the flags.*/
+                --flags;
+	error = vfs_fstat(fd, &stat, flags);

  	if (!error)
  		error = cp_new_stat64(&stat, statbuf);
@@ -397,18 +415,20 @@ asmlinkage long sys_fstat64(unsigned lon
  }

  asmlinkage long sys_fstatat64(int dfd, char __user *filename,
-			       struct stat64 __user *statbuf, int flag)
+			       struct stat64 __user *statbuf, int flag, unsigned int statflags)
  {
  	struct kstat stat;
+        if(!statflags) /*The want everything, so let's flip on all the flags.*/
+                --statflags;
  	int error = -EINVAL;

  	if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
  		goto out;

  	if (flag & AT_SYMLINK_NOFOLLOW)
-		error = vfs_lstat_fd(dfd, filename, &stat);
+		error = vfs_lstat_fd(dfd, filename, &stat, statflags);
  	else
-		error = vfs_stat_fd(dfd, filename, &stat);
+		error = vfs_stat_fd(dfd, filename, &stat, statflags);

  	if (!error)
  		error = cp_new_stat64(&stat, statbuf);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 2561020..252dc1a 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -40,6 +40,7 @@ struct files_stat_struct {
  extern struct files_stat_struct files_stat;
  extern int get_max_files(void);

+
  struct inodes_stat_t {
  	int nr_inodes;
  	int nr_unused;
@@ -1099,7 +1100,7 @@ struct inode_operations {
  	void (*truncate) (struct inode *);
  	int (*permission) (struct inode *, int, struct nameidata *);
  	int (*setattr) (struct dentry *, struct iattr *);
-	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
+	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *, unsigned int flags);
  	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
  	ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
  	ssize_t (*listxattr) (struct dentry *, char *, size_t);
@@ -1764,7 +1765,7 @@ extern int page_symlink(struct inode *in
  extern struct inode_operations page_symlink_inode_operations;
  extern int generic_readlink(struct dentry *, char __user *, int);
  extern void generic_fillattr(struct inode *, struct kstat *);
-extern int vfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+extern int vfs_getattr(struct vfsmount *, struct dentry *, struct kstat *, unsigned int);
  void inode_add_bytes(struct inode *inode, loff_t bytes);
  void inode_sub_bytes(struct inode *inode, loff_t bytes);
  loff_t inode_get_bytes(struct inode *inode);
@@ -1772,11 +1773,11 @@ void inode_set_bytes(struct inode *inode

  extern int vfs_readdir(struct file *, filldir_t, void *);

-extern int vfs_stat(char __user *, struct kstat *);
-extern int vfs_lstat(char __user *, struct kstat *);
-extern int vfs_stat_fd(int dfd, char __user *, struct kstat *);
-extern int vfs_lstat_fd(int dfd, char __user *, struct kstat *);
-extern int vfs_fstat(unsigned int, struct kstat *);
+extern int vfs_stat(char __user *, struct kstat *, unsigned int);
+extern int vfs_lstat(char __user *, struct kstat *, unsigned int);
+extern int vfs_stat_fd(int dfd, char __user *, struct kstat *, unsigned int);
+extern int vfs_lstat_fd(int dfd, char __user *, struct kstat *, unsigned int);
+extern int vfs_fstat(unsigned int, struct kstat *, unsigned int);

  extern int vfs_ioctl(struct file *, unsigned int, unsigned int, unsigned long);

diff --git a/include/linux/stat.h b/include/linux/stat.h
index 8669291..7f607e2 100644
--- a/include/linux/stat.h
+++ b/include/linux/stat.h
@@ -72,6 +72,21 @@ struct kstat {
  	unsigned long long	blocks;
  };

+/* Flags for the construction of a stat structure as opposed to getting the whole thing. */
+#define S_GETDEV 0x001
+#define S_GETINODE 0x002
+#define S_GETMODE 0x004
+#define S_GETNLINK 0x008
+#define S_GETUID 0x010
+#define S_GETGID 0x020
+#define S_GETRDEV 0x040
+#define S_GETSIZE 0x080
+#define S_GETBLKSIZE 0x100
+#define S_GETBLOCKS 0x100
+#define S_GETATIME 0x200
+#define S_GETMTIME 0x400
+#define S_GETCTIME 0x800
+
  #endif

  #endif
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 008f04c..f3a993c 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -225,17 +225,17 @@ asmlinkage long sys_lstat(char __user *f
  asmlinkage long sys_fstat(unsigned int fd,
  			struct __old_kernel_stat __user *statbuf);
  asmlinkage long sys_newstat(char __user *filename,
-				struct stat __user *statbuf);
+				struct stat __user *statbuf, unsigned int flags);
  asmlinkage long sys_newlstat(char __user *filename,
-				struct stat __user *statbuf);
-asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf);
+				struct stat __user *statbuf, unsigned int flags);
+asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf, unsigned int flags);
  asmlinkage long sys_ustat(unsigned dev, struct ustat __user *ubuf);
  #if BITS_PER_LONG == 32
  asmlinkage long sys_stat64(char __user *filename,
-				struct stat64 __user *statbuf);
-asmlinkage long sys_fstat64(unsigned long fd, struct stat64 __user *statbuf);
+				struct stat64 __user *statbuf, unsigned int flags);
+asmlinkage long sys_fstat64(unsigned long fd, struct stat64 __user *statbuf, unsigned int flags);
  asmlinkage long sys_lstat64(char __user *filename,
-				struct stat64 __user *statbuf);
+				struct stat64 __user *statbuf, unsigned int flags);
  asmlinkage long sys_truncate64(const char __user *path, loff_t length);
  asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length);
  #endif
@@ -568,7 +568,7 @@ asmlinkage long sys_openat(int dfd, cons
  asmlinkage long sys_newfstatat(int dfd, char __user *filename,
  			       struct stat __user *statbuf, int flag);
  asmlinkage long sys_fstatat64(int dfd, char __user *filename,
-			       struct stat64 __user *statbuf, int flag);
+			       struct stat64 __user *statbuf, int flag, unsigned int statflags);
  asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf,
  			       int bufsiz);
  asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename,
diff --git a/init/do_mounts.h b/init/do_mounts.h
index e7f2e7f..e65b6fa 100644
--- a/init/do_mounts.h
+++ b/init/do_mounts.h
@@ -24,7 +24,7 @@ #if BITS_PER_LONG == 32
  static inline u32 bstat(char *name)
  {
  	struct stat64 stat;
-	if (sys_stat64(name, &stat) != 0)
+	if (sys_stat64(name, &stat, 0) != 0)
  		return 0;
  	if (!S_ISBLK(stat.st_mode))
  		return 0;
@@ -36,7 +36,7 @@ #else
  static inline u32 bstat(char *name)
  {
  	struct stat stat;
-	if (sys_newstat(name, &stat) != 0)
+	if (sys_newstat(name, &stat, 0) != 0)
  		return 0;
  	if (!S_ISBLK(stat.st_mode))
  		return 0;
diff --git a/init/initramfs.c b/init/initramfs.c
index d28c109..4b12040 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -245,7 +245,7 @@ static void __init clean_path(char *path
  {
  	struct stat st;

-	if (!sys_newlstat(path, &st) && (st.st_mode^mode) & S_IFMT) {
+	if (!sys_newlstat(path, &st, 0) && (st.st_mode^mode) & S_IFMT) {
  		if (S_ISDIR(st.st_mode))
  			sys_rmdir(path);
  		else
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux