Re: any FS with tree-based quota system?

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

 



On Monday February 13, [email protected] wrote:
> I'm looking for a file system with a tree-based quota system.  XFS on IRIX 
> has projects, but that functionality didn't get ported over to Linux that 
> I can see.
> 
> I'm building a closed-box system to serve web pages.  I expect to have 20k 
> to 30k different user trees.  User's will not have direct access to the 
> box and will not be assigned a uid/gid.  Every tree will be owned by the 
> same uid/gid.
> 
> I would like to be able to apply a quota to a particular tree, and have 
> every file and directory in the path of that tree count toward that tree's 
> quota usage.  I can prevent hard links across trees.
> 
> I noticed that Neil Brown wrote some patches fairly early on in the 2.4 
> cycle to do tree-based quota by UID.  The last patch-set I found was 
> against 2.4.14 (http://cgi.cse.unsw.edu.au/~neilb/patches/linux/2.4.14/) 
> from late 2001, and did not come with patches to quota-tools.

Following is my tree-quota patch updated to 2.6.14.3.  However it
doesn't do exactly what you claim to want.
You still need to assign a uid to each user (the kernel needs some
number to use as an index into the quotas file).  But only the
top-level directory of each tree needs to be owned by the uid.  Files
beneath the top can be owned by anyone.

I can dig-up a patch for quota-utils if you want to proceed with
this.

If you are using a more recent kernel and get a conflict in fs.h,
you'll just need to #define ATTR_TID to be 16384, because 8192 was
taken by ATTR_FILE.

NeilBrown




Signed-off-by: Neil Brown <[email protected]>

### Diffstat output
 ./fs/attr.c                |   16 ++++++++++++
 ./fs/dquot.c               |   20 +++++++++++++++
 ./fs/ext2/ialloc.c         |    1 
 ./fs/ext2/inode.c          |    3 ++
 ./fs/ext2/super.c          |    1 
 ./fs/ext3/ialloc.c         |    1 
 ./fs/ext3/inode.c          |    5 +++
 ./fs/ext3/super.c          |   45 +++++++++++++++++++++++++++++-----
 ./fs/namei.c               |   36 ++++++++++++++++++++++++++-
 ./fs/quota_v1.c            |    3 +-
 ./fs/stat.c                |    1 
 ./include/linux/ext2_fs.h  |    4 +--
 ./include/linux/ext3_fs.h  |    2 +
 ./include/linux/fs.h       |    3 ++
 ./include/linux/quota.h    |   16 +++++++++---
 ./include/linux/quotaops.h |   58 +++++++++++++++++++++++++++++++++++++++++++++
 ./include/linux/stat.h     |    1 
 17 files changed, 200 insertions(+), 16 deletions(-)

diff ./fs/attr.c~current~ ./fs/attr.c
--- ./fs/attr.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/attr.c	2005-12-07 11:28:43.000000000 +1100
@@ -87,6 +87,8 @@ int inode_setattr(struct inode * inode, 
 		inode->i_uid = attr->ia_uid;
 	if (ia_valid & ATTR_GID)
 		inode->i_gid = attr->ia_gid;
+	if (ia_valid & ATTR_TID)
+		inode->i_tid = attr->ia_tid;
 	if (ia_valid & ATTR_ATIME)
 		inode->i_atime = timespec_trunc(attr->ia_atime,
 						inode->i_sb->s_time_gran);
@@ -154,6 +156,19 @@ int notify_change(struct dentry * dentry
 	if (ia_valid & ATTR_SIZE)
 		down_write(&dentry->d_inode->i_alloc_sem);
 
+	if (!(ia_valid & ATTR_TID)
+	    && (ia_valid & ATTR_UID)
+	    && !treequota_parent_uid_ok(inode, dentry->d_parent->d_inode,
+					attr->ia_uid)) {
+		if (dentry == dentry->d_parent)
+			attr->ia_tid = attr->ia_uid;
+		else
+			attr->ia_tid = treequota_tid(dentry->d_parent->d_inode,
+						     attr->ia_uid);
+		ia_valid |= ATTR_TID;
+		attr->ia_valid = ia_valid;
+	}
+
 	if (inode->i_op && inode->i_op->setattr) {
 		error = security_inode_setattr(dentry, attr);
 		if (!error)
@@ -164,6 +179,7 @@ int notify_change(struct dentry * dentry
 			error = security_inode_setattr(dentry, attr);
 		if (!error) {
 			if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
+			    (ia_valid & ATTR_TID && attr->ia_tid != inode->i_tid) ||
 			    (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
 				error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0;
 			if (!error)

diff ./fs/dquot.c~current~ ./fs/dquot.c
--- ./fs/dquot.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/dquot.c	2005-12-07 11:29:26.000000000 +1100
@@ -784,6 +784,8 @@ static inline int need_print_warning(str
 			return current->fsuid == dquot->dq_id;
 		case GRPQUOTA:
 			return in_group_p(dquot->dq_id);
+		case TREEQUOTA:
+			return current->fsuid == dquot->dq_id;
 	}
 	return 0;
 }
@@ -955,6 +957,9 @@ int dquot_initialize(struct inode *inode
 				case GRPQUOTA:
 					id = inode->i_gid;
 					break;
+				case TREEQUOTA:
+					id = inode->i_tid;
+					break;
 			}
 			inode->i_dquot[cnt] = dqget(inode->i_sb, id, cnt);
 		}
@@ -1167,6 +1172,8 @@ int dquot_transfer(struct inode *inode, 
 	struct dquot *transfer_to[MAXQUOTAS];
 	int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
 	    chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
+	int chtreeid = (iattr->ia_valid & ATTR_TID) && inode->i_tid != iattr->ia_tid;
+
 	char warntype[MAXQUOTAS];
 
 	/* First test before acquiring semaphore - solves deadlocks when we
@@ -1199,6 +1206,11 @@ int dquot_transfer(struct inode *inode, 
 					continue;
 				transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_gid, cnt);
 				break;
+			case TREEQUOTA:
+				if (!chtreeid)
+					continue;
+				transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_tid, cnt);
+				break;
 		}
 	}
 	spin_lock(&dq_data_lock);
@@ -1208,6 +1220,8 @@ int dquot_transfer(struct inode *inode, 
 		if (transfer_to[cnt] == NODQUOT)
 			continue;
 		transfer_from[cnt] = inode->i_dquot[cnt];
+		if (iattr->ia_valid & ATTR_FORCE)
+			continue;			/* don't check, just do */
 		if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA ||
 		    check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA)
 			goto warn_put_all;
@@ -1297,6 +1311,9 @@ static inline void set_enable_flags(stru
 		case GRPQUOTA:
 			dqopt->flags |= DQUOT_GRP_ENABLED;
 			break;
+		case TREEQUOTA:
+			dqopt->flags |= DQUOT_TREE_ENABLED;
+			break;
 	}
 }
 
@@ -1309,6 +1326,9 @@ static inline void reset_enable_flags(st
 		case GRPQUOTA:
 			dqopt->flags &= ~DQUOT_GRP_ENABLED;
 			break;
+		case TREEQUOTA:
+			dqopt->flags &= ~DQUOT_TREE_ENABLED;
+			break;
 	}
 }
 

diff ./fs/ext2/ialloc.c~current~ ./fs/ext2/ialloc.c
--- ./fs/ext2/ialloc.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/ext2/ialloc.c	2005-12-07 11:28:43.000000000 +1100
@@ -564,6 +564,7 @@ got:
 	sb->s_dirt = 1;
 	mark_buffer_dirty(bh2);
 	inode->i_uid = current->fsuid;
+	inode->i_tid = treequota_tid(dir, inode->i_uid);
 	if (test_opt (sb, GRPID))
 		inode->i_gid = dir->i_gid;
 	else if (dir->i_mode & S_ISGID) {

diff ./fs/ext2/inode.c~current~ ./fs/ext2/inode.c
--- ./fs/ext2/inode.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/ext2/inode.c	2005-12-07 11:28:43.000000000 +1100
@@ -1079,6 +1079,7 @@ void ext2_read_inode (struct inode * ino
 	inode->i_mode = le16_to_cpu(raw_inode->i_mode);
 	inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
 	inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
+	inode->i_tid = (uid_t)le32_to_cpu(raw_inode->i_e2_tid);
 	if (!(test_opt (inode->i_sb, NO_UID32))) {
 		inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
 		inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
@@ -1216,6 +1217,7 @@ static int ext2_update_inode(struct inod
 		raw_inode->i_uid_high = 0;
 		raw_inode->i_gid_high = 0;
 	}
+	raw_inode->i_e2_tid = cpu_to_le32(inode->i_tid);
 	raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
 	raw_inode->i_size = cpu_to_le32(inode->i_size);
 	raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
@@ -1302,6 +1304,7 @@ int ext2_setattr(struct dentry *dentry, 
 	if (error)
 		return error;
 	if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) ||
+	    (iattr->ia_valid & ATTR_TID && iattr->ia_tid != inode->i_tid) ||
 	    (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) {
 		error = DQUOT_TRANSFER(inode, iattr) ? -EDQUOT : 0;
 		if (error)

diff ./fs/ext2/super.c~current~ ./fs/ext2/super.c
--- ./fs/ext2/super.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/ext2/super.c	2005-12-07 11:28:44.000000000 +1100
@@ -315,6 +315,7 @@ static match_table_t tokens = {
 	{Opt_xip, "xip"},
 	{Opt_grpquota, "grpquota"},
 	{Opt_ignore, "noquota"},
+	{Opt_ignore, "treequota"},
 	{Opt_quota, "quota"},
 	{Opt_usrquota, "usrquota"},
 	{Opt_err, NULL}

diff ./fs/ext3/ialloc.c~current~ ./fs/ext3/ialloc.c
--- ./fs/ext3/ialloc.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/ext3/ialloc.c	2005-12-07 11:28:43.000000000 +1100
@@ -545,6 +545,7 @@ got:
 	sb->s_dirt = 1;
 
 	inode->i_uid = current->fsuid;
+	inode->i_tid = treequota_tid(dir, inode->i_uid);
 	if (test_opt (sb, GRPID))
 		inode->i_gid = dir->i_gid;
 	else if (dir->i_mode & S_ISGID) {

diff ./fs/ext3/inode.c~current~ ./fs/ext3/inode.c
--- ./fs/ext3/inode.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/ext3/inode.c	2005-12-07 11:28:43.000000000 +1100
@@ -2446,6 +2446,7 @@ void ext3_read_inode(struct inode * inod
 	inode->i_mode = le16_to_cpu(raw_inode->i_mode);
 	inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
 	inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
+	inode->i_tid = (uid_t)le32_to_cpu(raw_inode->i_e3_tid);
 	if(!(test_opt (inode->i_sb, NO_UID32))) {
 		inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
 		inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
@@ -2608,6 +2609,7 @@ static int ext3_do_update_inode(handle_t
 		raw_inode->i_uid_high = 0;
 		raw_inode->i_gid_high = 0;
 	}
+	raw_inode->i_e3_tid = cpu_to_le32(inode->i_tid);
 	raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
 	raw_inode->i_size = cpu_to_le32(ei->i_disksize);
 	raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
@@ -2760,6 +2762,7 @@ int ext3_setattr(struct dentry *dentry, 
 		return error;
 
 	if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
+	    (ia_valid & ATTR_TID && attr->ia_tid != inode->i_tid) ||
 		(ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
 		handle_t *handle;
 
@@ -2782,6 +2785,8 @@ int ext3_setattr(struct dentry *dentry, 
 			inode->i_uid = attr->ia_uid;
 		if (attr->ia_valid & ATTR_GID)
 			inode->i_gid = attr->ia_gid;
+		if (attr->ia_valid & ATTR_TID)
+			inode->i_tid = attr->ia_tid;
 		error = ext3_mark_inode_dirty(handle, inode);
 		ext3_journal_stop(handle);
 	}

diff ./fs/ext3/super.c~current~ ./fs/ext3/super.c
--- ./fs/ext3/super.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/ext3/super.c	2005-12-07 15:32:13.000000000 +1100
@@ -533,19 +533,25 @@ static int ext3_show_options(struct seq_
 	if (sbi->s_qf_names[GRPQUOTA])
 		seq_printf(seq, ",grpjquota=%s", sbi->s_qf_names[GRPQUOTA]);
 
+	if (sbi->s_qf_names[TREEQUOTA])
+		seq_printf(seq, ",treejquota=%s", sbi->s_qf_names[TREEQUOTA]);
+
 	if (sbi->s_mount_opt & EXT3_MOUNT_USRQUOTA)
 		seq_puts(seq, ",usrquota");
 
 	if (sbi->s_mount_opt & EXT3_MOUNT_GRPQUOTA)
 		seq_puts(seq, ",grpquota");
+
+	if (sbi->s_mount_opt & EXT3_MOUNT_TREEQUOTA)
+		seq_puts(seq, ",treequota");
 #endif
 
 	return 0;
 }
 
 #ifdef CONFIG_QUOTA
-#define QTYPE2NAME(t) ((t)==USRQUOTA?"user":"group")
-#define QTYPE2MOPT(on, t) ((t)==USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
+#define QTYPE2NAME(t) ((t)==USRQUOTA?"user":(t)==GRPQUOTA?"group":"tree")
+#define QTYPE2MOPT(on, t) ((t)==USRQUOTA?((on)##USRJQUOTA):(t)==GRPQUOTA?((on)##GRPJQUOTA):((on)##TREEJQUOTA))
 
 static int ext3_dquot_initialize(struct inode *inode, int type);
 static int ext3_dquot_drop(struct inode *inode);
@@ -622,10 +628,11 @@ enum {
 	Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh,
 	Opt_commit, Opt_journal_update, Opt_journal_inum,
 	Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
-	Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
+	Opt_usrjquota, Opt_grpjquota, Opt_treejquota,
+	Opt_offusrjquota, Opt_offgrpjquota, Opt_offtreejquota,
 	Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota,
 	Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota,
-	Opt_grpquota
+	Opt_grpquota, Opt_treequota
 };
 
 static match_table_t tokens = {
@@ -667,10 +674,13 @@ static match_table_t tokens = {
 	{Opt_usrjquota, "usrjquota=%s"},
 	{Opt_offgrpjquota, "grpjquota="},
 	{Opt_grpjquota, "grpjquota=%s"},
+	{Opt_offtreejquota, "treejquota="},
+	{Opt_treejquota, "treejquota=%s"},
 	{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
 	{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
 	{Opt_grpquota, "grpquota"},
 	{Opt_noquota, "noquota"},
+	{Opt_treequota, "treequota"},
 	{Opt_quota, "quota"},
 	{Opt_usrquota, "usrquota"},
 	{Opt_barrier, "barrier=%u"},
@@ -875,6 +885,9 @@ static int parse_options (char * options
 			}
 			break;
 #ifdef CONFIG_QUOTA
+		case Opt_treejquota:
+			qtype = TREEQUOTA;
+			goto set_qf_name;
 		case Opt_usrjquota:
 			qtype = USRQUOTA;
 			goto set_qf_name;
@@ -913,6 +926,9 @@ set_qf_name:
 			}
 			set_opt(sbi->s_mount_opt, QUOTA);
 			break;
+		case Opt_offtreejquota:
+			qtype = TREEQUOTA;
+			goto clear_qf_name;
 		case Opt_offusrjquota:
 			qtype = USRQUOTA;
 			goto clear_qf_name;
@@ -946,6 +962,10 @@ clear_qf_name:
 			set_opt(sbi->s_mount_opt, QUOTA);
 			set_opt(sbi->s_mount_opt, GRPQUOTA);
 			break;
+		case Opt_treequota:
+			set_opt(sbi->s_mount_opt, QUOTA);
+			set_opt(sbi->s_mount_opt, TREEQUOTA);
+			break;
 		case Opt_noquota:
 			if (sb_any_quota_enabled(sb)) {
 				printk(KERN_ERR "EXT3-fs: Cannot change quota "
@@ -960,10 +980,13 @@ clear_qf_name:
 		case Opt_quota:
 		case Opt_usrquota:
 		case Opt_grpquota:
+		case Opt_treequota:
 		case Opt_usrjquota:
 		case Opt_grpjquota:
+		case Opt_treejquota:
 		case Opt_offusrjquota:
 		case Opt_offgrpjquota:
+		case Opt_offtreejquota:
 		case Opt_jqfmt_vfsold:
 		case Opt_jqfmt_vfsv0:
 			printk(KERN_ERR
@@ -1007,7 +1030,8 @@ clear_qf_name:
 		}
 	}
 #ifdef CONFIG_QUOTA
-	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] ||
+	    sbi->s_qf_names[TREEQUOTA]) {
 		if ((sbi->s_mount_opt & EXT3_MOUNT_USRQUOTA) &&
 		     sbi->s_qf_names[USRQUOTA])
 			clear_opt(sbi->s_mount_opt, USRQUOTA);
@@ -1016,6 +1040,11 @@ clear_qf_name:
 		     sbi->s_qf_names[GRPQUOTA])
 			clear_opt(sbi->s_mount_opt, GRPQUOTA);
 
+		if ((sbi->s_mount_opt & EXT3_MOUNT_TREEQUOTA) &&
+		     sbi->s_qf_names[TREEQUOTA])
+			clear_opt(sbi->s_mount_opt, TREEQUOTA);
+
+/* FIXME */
 		if ((sbi->s_qf_names[USRQUOTA] &&
 				(sbi->s_mount_opt & EXT3_MOUNT_GRPQUOTA)) ||
 		    (sbi->s_qf_names[GRPQUOTA] &&
@@ -2453,7 +2482,8 @@ static int ext3_mark_dquot_dirty(struct 
 {
 	/* Are we journalling quotas? */
 	if (EXT3_SB(dquot->dq_sb)->s_qf_names[USRQUOTA] ||
-	    EXT3_SB(dquot->dq_sb)->s_qf_names[GRPQUOTA]) {
+	    EXT3_SB(dquot->dq_sb)->s_qf_names[GRPQUOTA] ||
+	    EXT3_SB(dquot->dq_sb)->s_qf_names[TREEQUOTA]) {
 		dquot_mark_dquot_dirty(dquot);
 		return ext3_write_dquot(dquot);
 	} else {
@@ -2500,7 +2530,8 @@ static int ext3_quota_on(struct super_bl
 		return -EINVAL;
 	/* Not journalling quota? */
 	if (!EXT3_SB(sb)->s_qf_names[USRQUOTA] &&
-	    !EXT3_SB(sb)->s_qf_names[GRPQUOTA])
+	    !EXT3_SB(sb)->s_qf_names[GRPQUOTA] &&
+	    !EXT3_SB(sb)->s_qf_names[TREEQUOTA])
 		return vfs_quota_on(sb, type, format_id, path);
 	err = path_lookup(path, LOOKUP_FOLLOW, &nd);
 	if (err)

diff ./fs/namei.c~current~ ./fs/namei.c
--- ./fs/namei.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/namei.c	2005-12-07 11:30:13.000000000 +1100
@@ -808,6 +808,8 @@ static fastcall int __link_path_walk(con
 		if (err)
 			break;
 
+		treequota_check(next.dentry);
+
 		err = -ENOENT;
 		inode = next.dentry->d_inode;
 		if (!inode)
@@ -861,6 +863,9 @@ last_component:
 		err = do_lookup(nd, &this, &next);
 		if (err)
 			break;
+
+		treequota_check(next.dentry);
+
 		inode = next.dentry->d_inode;
 		if ((lookup_flags & LOOKUP_FOLLOW)
 		    && inode && inode->i_op && inode->i_op->follow_link) {
@@ -1092,6 +1097,7 @@ static struct dentry * __lookup_hash(str
 		else
 			dput(new);
 	}
+	treequota_check(dentry);
 out:
 	return dentry;
 }
@@ -1994,6 +2000,8 @@ int vfs_link(struct dentry *old_dentry, 
 	if (dir->i_sb != inode->i_sb)
 		return -EXDEV;
 
+	if (!treequota_parent_ok(inode, dir))
+		return -EXDEV;
 	/*
 	 * A link to an append-only or immutable file cannot be created.
 	 */
@@ -2100,6 +2108,7 @@ static int vfs_rename_dir(struct inode *
 {
 	int error = 0;
 	struct inode *target;
+	struct iattr attr;
 
 	/*
 	 * If we are going to change the parent - check write permissions,
@@ -2111,6 +2120,11 @@ static int vfs_rename_dir(struct inode *
 			return error;
 	}
 
+	if (!treequota_parent_ok(old_dentry->d_inode, new_dir)
+	    && !capable(CAP_CHOWN))
+		return -EXDEV;
+
+
 	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
 	if (error)
 		return error;
@@ -2120,8 +2134,14 @@ static int vfs_rename_dir(struct inode *
 		down(&target->i_sem);
 		dentry_unhash(new_dentry);
 	}
+	attr.ia_valid = ATTR_TID;
+	attr.ia_tid = treequota_tid(new_dir, old_dentry->d_inode->i_uid);
+
 	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
 		error = -EBUSY;
+	else if (!treequota_parent_ok(old_dentry->d_inode, new_dir)
+		 && (error = notify_change(old_dentry, &attr)))
+		;
 	else 
 		error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
 	if (target) {
@@ -2153,8 +2173,20 @@ static int vfs_rename_other(struct inode
 		down(&target->i_sem);
 	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
 		error = -EBUSY;
-	else
-		error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	else {
+		error = 0;
+		if (!treequota_parent_ok(old_dentry->d_inode, new_dir)) {
+			struct iattr attr;
+			if (old_dentry->d_inode->i_nlink > 1)
+				return -EXDEV;
+			attr.ia_valid = ATTR_TID;
+			attr.ia_tid = treequota_tid(new_dir,
+					    old_dentry->d_inode->i_uid);
+			error = notify_change(old_dentry, &attr);
+		}
+		if (!error)
+			error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+	}
 	if (!error) {
 		/* The following d_move() should become unconditional */
 		if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME))

diff ./fs/quota_v1.c~current~ ./fs/quota_v1.c
--- ./fs/quota_v1.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/quota_v1.c	2005-12-07 11:28:43.000000000 +1100
@@ -91,7 +91,8 @@ out:
 /* Magics of new quota format */
 #define V2_INITQMAGICS {\
 	0xd9c01f11,     /* USRQUOTA */\
-	0xd9c01927      /* GRPQUOTA */\
+	0xd9c01927,     /* GRPQUOTA */\
+	0xd9c01987      /* TREEQUOTA - a lie */\
 }
 
 /* Header of new quota format */

diff ./fs/stat.c~current~ ./fs/stat.c
--- ./fs/stat.c~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./fs/stat.c	2005-12-07 11:28:43.000000000 +1100
@@ -28,6 +28,7 @@ void generic_fillattr(struct inode *inod
 	stat->uid = inode->i_uid;
 	stat->gid = inode->i_gid;
 	stat->rdev = inode->i_rdev;
+	stat->tid = inode->i_tid;
 	stat->atime = inode->i_atime;
 	stat->mtime = inode->i_mtime;
 	stat->ctime = inode->i_ctime;

diff ./include/linux/ext2_fs.h~current~ ./include/linux/ext2_fs.h
--- ./include/linux/ext2_fs.h~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./include/linux/ext2_fs.h	2005-12-07 11:28:43.000000000 +1100
@@ -243,7 +243,7 @@ struct ext2_inode {
 			__u16	i_pad1;
 			__le16	l_i_uid_high;	/* these 2 fields    */
 			__le16	l_i_gid_high;	/* were reserved2[0] */
-			__u32	l_i_reserved2;
+			__u32	l_i_tid;	/* tree-id for quotas, no longer l_i_reserved2 */
 		} linux2;
 		struct {
 			__u8	h_i_frag;	/* Fragment number */
@@ -272,7 +272,7 @@ struct ext2_inode {
 #define i_gid_low	i_gid
 #define i_uid_high	osd2.linux2.l_i_uid_high
 #define i_gid_high	osd2.linux2.l_i_gid_high
-#define i_reserved2	osd2.linux2.l_i_reserved2
+#define i_e2_tid	osd2.linux2.l_i_tid
 #endif
 
 #ifdef	__hurd__

diff ./include/linux/ext3_fs.h~current~ ./include/linux/ext3_fs.h
--- ./include/linux/ext3_fs.h~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./include/linux/ext3_fs.h	2005-12-07 15:51:58.000000000 +1100
@@ -322,6 +322,7 @@ struct ext3_inode {
 #define i_uid_high	osd2.linux2.l_i_uid_high
 #define i_gid_high	osd2.linux2.l_i_gid_high
 #define i_reserved2	osd2.linux2.l_i_reserved2
+#define	i_e3_tid	i_reserved2
 
 #elif defined(__GNU__)
 
@@ -375,6 +376,7 @@ struct ext3_inode {
 #define EXT3_MOUNT_QUOTA		0x80000 /* Some quota option set */
 #define EXT3_MOUNT_USRQUOTA		0x100000 /* "old" user quota */
 #define EXT3_MOUNT_GRPQUOTA		0x200000 /* "old" group quota */
+#define EXT3_MOUNT_TREEQUOTA		0x400000 /* "old" tree quota */
 
 /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */
 #ifndef _LINUX_EXT2_FS_H

diff ./include/linux/fs.h~current~ ./include/linux/fs.h
--- ./include/linux/fs.h~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./include/linux/fs.h	2005-12-07 11:55:46.000000000 +1100
@@ -264,6 +264,7 @@ typedef void (dio_iodone_t)(struct kiocb
 #define ATTR_ATTR_FLAG	1024
 #define ATTR_KILL_SUID	2048
 #define ATTR_KILL_SGID	4096
+#define ATTR_TID	8192
 
 /*
  * This is the Inode Attributes structure, used for notify_change().  It
@@ -283,6 +284,7 @@ struct iattr {
 	struct timespec	ia_atime;
 	struct timespec	ia_mtime;
 	struct timespec	ia_ctime;
+	uid_t		ia_tid;
 };
 
 /*
@@ -430,6 +432,7 @@ struct inode {
 	unsigned int		i_nlink;
 	uid_t			i_uid;
 	gid_t			i_gid;
+	uid_t			i_tid;	/* tree-id for quotas */
 	dev_t			i_rdev;
 	loff_t			i_size;
 	struct timespec		i_atime;

diff ./include/linux/quota.h~current~ ./include/linux/quota.h
--- ./include/linux/quota.h~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./include/linux/quota.h	2005-12-07 11:28:43.000000000 +1100
@@ -56,9 +56,10 @@ extern spinlock_t dq_data_lock;
 #define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
 #define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
 
-#define MAXQUOTAS 2
+#define MAXQUOTAS 3
 #define USRQUOTA  0		/* element used for user quotas */
 #define GRPQUOTA  1		/* element used for group quotas */
+#define TREEQUOTA 2		/* element used for tree quotas */
 
 /*
  * Definitions for the default names of the quotas files.
@@ -66,6 +67,7 @@ extern spinlock_t dq_data_lock;
 #define INITQFNAMES { \
 	"user",    /* USRQUOTA */ \
 	"group",   /* GRPQUOTA */ \
+	"tree",    /* TREEQUOTA */ \
 	"undefined", \
 };
 
@@ -282,6 +284,7 @@ struct quota_format_type {
 
 #define DQUOT_USR_ENABLED	0x01		/* User diskquotas enabled */
 #define DQUOT_GRP_ENABLED	0x02		/* Group diskquotas enabled */
+#define DQUOT_TREE_ENABLED	0x04		/* Tree diskquotas enabled */
 
 struct quota_info {
 	unsigned int flags;			/* Flags for diskquotas on this device */
@@ -299,11 +302,16 @@ int mark_dquot_dirty(struct dquot *dquot
 
 #define dquot_dirty(dquot) test_bit(DQ_MOD_B, &(dquot)->dq_flags)
 
-#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \
-	(sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED))
+#define sb_has_quota_enabled(sb, type)	\
+	((type)==USRQUOTA ? \
+	   (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : \
+	     ((type)==GRPQUOTA ? \
+		(sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED) : \
+	      (sb_dqopt(sb)->flags & DQUOT_TREE_ENABLED)))
 
 #define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \
-				  sb_has_quota_enabled(sb, GRPQUOTA))
+				  sb_has_quota_enabled(sb, GRPQUOTA) | \
+				  sb_has_quota_enabled(sb, TREEQUOTA))
 
 int register_quota_format(struct quota_format_type *fmt);
 void unregister_quota_format(struct quota_format_type *fmt);

diff ./include/linux/quotaops.h~current~ ./include/linux/quotaops.h
--- ./include/linux/quotaops.h~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./include/linux/quotaops.h	2005-12-07 11:31:27.000000000 +1100
@@ -183,6 +183,59 @@ static __inline__ int DQUOT_OFF(struct s
 	return ret;
 }
 
+
+static __inline__ int treequota_parent_uid_ok(const struct inode *inode, const struct inode *dir, const uid_t uid)
+{
+	if ( !(inode->i_sb->s_dquot.flags & DQUOT_TREE_ENABLED))
+		return 1;
+	if ((dir->i_tid && dir != inode)
+	    ? (inode->i_tid ==   dir->i_tid)
+	    : (inode->i_tid ==   uid))
+		return 1;
+	return 0;
+}
+
+static __inline__ int treequota_parent_ok(const struct inode *inode, const struct inode *dir)
+{
+	return treequota_parent_uid_ok(inode,dir, inode->i_uid);
+}
+
+static __inline__ int treequota_tid(const struct inode *dir, const uid_t uid)
+{
+	if (!(dir->i_sb->s_dquot.flags & DQUOT_TREE_ENABLED))
+		return 0;
+	return dir->i_tid
+		? dir->i_tid
+		: uid;
+}
+
+static __inline__ void treequota_check(struct dentry *dentry)
+{
+	struct inode *inode;
+	struct iattr attr;
+
+	if (!dentry || IS_ERR(dentry))
+		return;
+	inode = dentry->d_inode;
+	if (!inode || ! (inode->i_sb->s_dquot.flags & DQUOT_TREE_ENABLED))
+		return;
+	if (treequota_parent_ok(inode, dentry->d_parent->d_inode))
+		return;
+
+	attr.ia_valid = ATTR_FORCE | ATTR_TID;
+	attr.ia_tid = treequota_tid(dentry->d_parent->d_inode,
+				    inode->i_uid);
+	if (!S_ISDIR(inode->i_mode)
+	    && inode->i_nlink > 1) {
+		printk(KERN_WARNING "treequota: file with multiple links has wrong tree-id\n");
+		printk(KERN_WARNING "  dev=%x ino=%ld dino=%ld\n",
+		       inode->i_sb->s_dev, inode->i_ino,
+		       dentry->d_parent->d_inode->i_ino);
+		printk(KERN_WARNING "  basename=%s\n", dentry->d_name.name);
+	}
+	notify_change(dentry, &attr);
+}
+
 #else
 
 /*
@@ -235,6 +288,11 @@ extern __inline__ void DQUOT_FREE_SPACE(
 	mark_inode_dirty(inode);
 }	
 
+#define treequota_parent_uid_ok(inode,dir,uid) (1)
+#define treequota_parent_ok(inode,dir) (1)
+#define treequota_tid(inode,uid) (0)
+#define treequota_check(dentry) ((void)(0))
+
 #endif /* CONFIG_QUOTA */
 
 #define DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr)	DQUOT_PREALLOC_SPACE_NODIRTY(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)

diff ./include/linux/stat.h~current~ ./include/linux/stat.h
--- ./include/linux/stat.h~current~	2005-12-07 15:51:56.000000000 +1100
+++ ./include/linux/stat.h	2005-12-07 11:28:43.000000000 +1100
@@ -63,6 +63,7 @@ struct kstat {
 	unsigned int	nlink;
 	uid_t		uid;
 	gid_t		gid;
+	uid_t		tid;
 	dev_t		rdev;
 	loff_t		size;
 	struct timespec  atime;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux