Subject: sysfs-iattr: add sysfs_setattr

From: Maneesh Soni <[email-masked]>
Date: Tue, 31 May 2005 10:39:14 +0530

o This adds ->i_op->setattr VFS method for sysfs inodes. The changed
  attribues are saved in the persistent sysfs_dirent structure as a pointer
  to struct iattr. The struct iattr is allocated only for those sysfs_dirent's
  for which default attributes are getting changed. Thanks to Jon Smirl for
  this suggestion.

Signed-off-by: Maneesh Soni <[email-masked]>
Signed-off-by: Greg Kroah-Hartman <[email-masked]>

---
 fs/sysfs/dir.c        |    1 
 fs/sysfs/inode.c      |   65 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/sysfs/sysfs.h      |    2 +
 include/linux/sysfs.h |    1 
 4 files changed, 69 insertions(+)

--- gregkh-2.6.orig/include/linux/sysfs.h	2005-06-01 09:43:24.000000000 -0700
+++ gregkh-2.6/include/linux/sysfs.h	2005-06-01 13:33:18.000000000 -0700
@@ -73,6 +73,7 @@
 	int			s_type;
 	umode_t			s_mode;
 	struct dentry		* s_dentry;
+	struct iattr		* s_iattr;
 };
 
 #define SYSFS_ROOT		0x0001
--- gregkh-2.6.orig/fs/sysfs/inode.c	2005-05-27 22:50:37.000000000 -0700
+++ gregkh-2.6/fs/sysfs/inode.c	2005-06-01 13:33:18.000000000 -0700
@@ -26,6 +26,71 @@
 	.capabilities	= BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
 };
 
+static struct inode_operations sysfs_inode_operations ={
+	.setattr	= sysfs_setattr,
+};
+
+int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
+{
+	struct inode * inode = dentry->d_inode;
+	struct sysfs_dirent * sd = dentry->d_fsdata;
+	struct iattr * sd_iattr;
+	unsigned int ia_valid = iattr->ia_valid;
+	int error;
+
+	if (!sd)
+		return -EINVAL;
+
+	sd_iattr = sd->s_iattr;
+
+	error = inode_change_ok(inode, iattr);
+	if (error)
+		return error;
+
+	error = inode_setattr(inode, iattr);
+	if (error)
+		return error;
+
+	if (!sd_iattr) {
+		/* setting attributes for the first time, allocate now */
+		sd_iattr = kmalloc(sizeof(struct iattr), GFP_KERNEL);
+		if (!sd_iattr)
+			return -ENOMEM;
+		/* assign default attributes */
+		memset(sd_iattr, 0, sizeof(struct iattr));
+		sd_iattr->ia_mode = sd->s_mode;
+		sd_iattr->ia_uid = 0;
+		sd_iattr->ia_gid = 0;
+		sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME;
+		sd->s_iattr = sd_iattr;
+	}
+
+	/* attributes were changed atleast once in past */
+
+	if (ia_valid & ATTR_UID)
+		sd_iattr->ia_uid = iattr->ia_uid;
+	if (ia_valid & ATTR_GID)
+		sd_iattr->ia_gid = iattr->ia_gid;
+	if (ia_valid & ATTR_ATIME)
+		sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime,
+						inode->i_sb->s_time_gran);
+	if (ia_valid & ATTR_MTIME)
+		sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime,
+						inode->i_sb->s_time_gran);
+	if (ia_valid & ATTR_CTIME)
+		sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime,
+						inode->i_sb->s_time_gran);
+	if (ia_valid & ATTR_MODE) {
+		umode_t mode = iattr->ia_mode;
+
+		if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+			mode &= ~S_ISGID;
+		sd_iattr->ia_mode = mode;
+	}
+
+	return error;
+}
+
 struct inode * sysfs_new_inode(mode_t mode)
 {
 	struct inode * inode = new_inode(sysfs_sb);
--- gregkh-2.6.orig/fs/sysfs/dir.c	2005-06-01 13:33:14.000000000 -0700
+++ gregkh-2.6/fs/sysfs/dir.c	2005-06-01 13:33:18.000000000 -0700
@@ -233,6 +233,7 @@
 
 struct inode_operations sysfs_dir_inode_operations = {
 	.lookup		= sysfs_lookup,
+	.setattr	= sysfs_setattr,
 };
 
 static void remove_dir(struct dentry * d)
--- gregkh-2.6.orig/fs/sysfs/sysfs.h	2005-03-01 23:38:19.000000000 -0800
+++ gregkh-2.6/fs/sysfs/sysfs.h	2005-06-01 13:33:18.000000000 -0700
@@ -17,6 +17,7 @@
 
 extern const unsigned char * sysfs_get_name(struct sysfs_dirent *sd);
 extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent);
+extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
 
 extern struct rw_semaphore sysfs_rename_sem;
 extern struct super_block * sysfs_sb;
@@ -75,6 +76,7 @@
 		kobject_put(sl->target_kobj);
 		kfree(sl);
 	}
+	kfree(sd->s_iattr);
 	kmem_cache_free(sysfs_dir_cachep, sd);
 }