[PATCH] Posix file attribute support on VFAT (take #2)

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

 



This is a take 2 of posix file attribute support on VFAT.

I made a couple of changes from previous revision, according
feedbacks on LKML.
 - clean up
 - restrict file attribute operations to prevent attribute lose at
   runtime.
 - added condition checking to special files like device files
   and symlink.

I did not yet implement short symlik optimization, sugessted at
<[email protected]> from
[email protected]

Main purpose of this patch is to build root file system with VFAT for
smallembedded device. FAT is widely used for embedded device to exchange
data, and also small embedded device has resource limitation. So it's
very handy that VFAT has capability to built root fs, even that has
restrictions.

Details are described within a patch. I think this feature still needs
improvemnts, however it is very helpful for most embedded developpers.

This patch is against 2.6.12 kernel.

-- 
Hiroyuki Machida		[email protected]		
SSW Dept. HENC, Sony Corp.
 This patch enables "posix_attr" option described as following;

		 vfat posix attr option "posix_attr" 

					Mon Aug 15 21:29:54 JST 2005
 
* FEATURES

 Following attributes/modes are supported in posix attributea mapping
 in VFAT.

   - FileType
 	This supports following special files and it's attributes;
 		symbolic link,	block device node,
 		char device node, fifo,	socket
 	Regular files/dirs also may have POSIX attributes.
   - DeviceFile
 	Major and minor number would be held at ctime
 	and both values are limited  to 255.
   - Owner's User ID/Group ID 
 	This can be used to distinguish root and others,
 	because this has just one bit width. 
 	Value of UID/GID for non-root user will be taken from uid/gid 
 	option on mounting. If nothing is specified, system uses 
	(u16)-1 as last resort. That means change-uid may affect on gid.
   - Permission for Group/Other (rwx)
 	Those modes will be kept in ctime_cs.
 	Also permission modes for "others" will be
 	same as "group", due to lack of fields.
	That means set-group-mode may affect on other-mode.
	On the other hand, set-other-mode has no affect to group-mode.
	
   - Permission for Owner (rwx)
 	These modes will be mapped to FAT attributes.
 	Just same as mapping under VFAT.
   - Others
 	no sticky, setgid nor setuid bits are not supported.
 
* ALGORITHM FOR MAPPING DECISION
   - Regular file/dir
 	To distinguish regular files/dirs, look if this fat dir 
 	entry doesn't have ATTR_SYS, first. If it doesn't have 
 	ATTR_SYS, then check if TYPE field (MSB 3bits) in ctime_cs 
 	is equal to 7. If so, this regular file/dir is created and/or 
 	modified under VFAT with "posix_attr". And posix attribute 
 	mapping can be take place. Otherwise, conventional VFAT 
 	attribute mapping is used.

   - Special file
 	To distinguish special files, look if this fat dir entry 
 	has ATTR_SYS, first. Also we need to check it not to have 
	ATT_EXT.
	If it has ATTR_SYS, then check 1st. LSB bit in ctime_cs, 
	referred as "special file flag".
 	If set,  this file is created under VFAT with "posix_attr". 
 	Look up TYPE field to decide special file type.

 	This special file detection method has some flaw to make
 	potential confusion. E.g. some system file created under
	dos/win may be treated as special file.  However in most case,
	user don't create system file under dos/win.
	To reduce possiblity of this confusion, system makes
	sure special files except symlink have size ZERO.
	For symlink, system checks it's size not to exceed page size 
	and PATH_MAX.

* FAT DIR ENTRY FIELDS
   - ctime_cs
 	    8bit byte
 	7 6 5 4 3 2 1 0
 	|===| | | | | |
 	TYPE  | | | | +- special file flag (valid if ATTR_SYS)
 	      | | | +--- User/Group ID(root or others)
 	      | | +----- !group X
 	      | +------- !group W
 	      +--------- !group R
 
 	  special file flag
 		Indicate this entry has posix attribute mapping.
 		This field is valid for fat dir entry, which 
 		have ATTR_SYS.
 
 	  special file TYPE
 		val	type on VFS(val)	Description
 		------------------------------------------------
 		0 	(place folder for backward compat)
 		1 	DT_LNK (10)		symbolic link
 		2	DT_BLK (6)		block dev
 		3	DT_CHR (4)		char dev
 		4	DT_FIFO (1)		fifo
 		5	DT_SOCK	(12)		socket
 	
 		7*)	(reserved for DT_REG/DT_DIR) 
 
 		*)Value 7 is reserved for regular file/dir (DT_REG/DT_DIR).
 		Normally ctime_cs would have 0-199 value to stand for 
 		up to 2sec. The value for DT_REG/DT_DIR is selected 
 		to be over this range to distinguish if file was created
 		under POSIX_ATTR or not.

   - attr
 	FAT attribute	(val)		mapped attribute
 	------------------------------------------------
 	ATTR_RO		0x01 		!owner W
 	ATTR_HIDDEN	0x02		!owner R
 	ATTR_SYS	0x04
 	ATTR_VOLUME	0x08
 	ATTR_DIR	0x10		DIR
 	ATTR_ARCH	0x20		!owner X

   - ctime
 		16bit word
 	f e d c b a 9 8 7 6 5 4 3 2 1 0
 	|=============| |-------------|
 	  major		  minor
 
* vfat-posix_attr.patch:

 fs/fat/file.c            |   52 ++++++++
 fs/fat/inode.c           |   27 ++++
 fs/vfat/namei.c          |  272 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/msdos_fs.h |  280 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 629 insertions(+), 2 deletions(-)

Signed-off-by: Hiroyuki Machida <[email protected]> for CELF

--- alp-2.6.12-p8.orig/fs/fat/file.c	2005-08-12 21:35:51.000000000 +0900
+++ alp-2.6.12-p8/fs/fat/file.c	2005-08-12 21:37:36.000000000 +0900
@@ -162,6 +162,8 @@
 	struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
 	struct inode *inode = dentry->d_inode;
 	int mask, error = 0;
+	int px_uid = sbi->options.fs_uid ? sbi->options.fs_uid : (u16)-1;
+	int px_gid = sbi->options.fs_gid ? sbi->options.fs_gid : (u16)-1;
 
 	lock_kernel();
 
@@ -173,12 +175,56 @@
 		}
 	}
 
+	if (sbi->options.posix_attr) {
+		if (attr->ia_valid & ATTR_UID) {
+			/* root can change uid to any value */
+			if (capable(CAP_CHOWN) && 
+			    (attr->ia_uid && attr->ia_uid != px_uid))
+				attr->ia_uid = px_uid;
+			/* change-uid affects gid */
+			attr->ia_valid |= ATTR_GID;
+			attr->ia_gid = attr->ia_uid ? px_gid : 0;
+		} else {
+			/* chown syscall sets both uid and gid */
+			if (attr->ia_valid & ATTR_GID) {
+				/* root can change gid to any value */
+				if (capable(CAP_CHOWN) && 
+				    (attr->ia_gid && attr->ia_gid != px_gid))
+					attr->ia_gid = px_gid;
+				/* change-gid affects uid */
+				attr->ia_valid |= ATTR_UID;
+				attr->ia_uid = attr->ia_gid ? px_uid : 0;
+			}
+		}
+		/* change-group-mode affects on others-mode */
+		if (attr->ia_valid & ATTR_MODE) { 
+			int others_mode = (attr->ia_mode & S_IRWXG) >> 3;
+			attr->ia_mode &=  ~S_IRWXO;
+			attr->ia_mode |=  others_mode;
+		}
+	}
+
 	error = inode_change_ok(inode, attr);
 	if (error) {
 		if (sbi->options.quiet)
 			error = 0;
 		goto out;
 	}
+
+	if (sbi->options.posix_attr) {
+		if (((attr->ia_valid & ATTR_UID) &&
+		       ((attr->ia_uid) && (attr->ia_uid != px_uid))) ||
+		    ((attr->ia_valid & ATTR_GID) &&
+		       ((attr->ia_gid) && (attr->ia_gid != px_gid))) ||
+		    ((attr->ia_valid & ATTR_MODE) &&
+		     (attr->ia_mode & ~VFAT_POSIX_ATTR_VALID_MODE)))
+			error = -EPERM;
+		if (!error)  
+			error = inode_setattr(inode, attr);
+		if (sbi->options.quiet)
+			error = 0;
+		goto out;
+	}
 	if (((attr->ia_valid & ATTR_UID) &&
 	     (attr->ia_uid != sbi->options.fs_uid)) ||
 	    ((attr->ia_valid & ATTR_GID) &&
@@ -306,3 +352,9 @@
 	.truncate	= fat_truncate,
 	.setattr	= fat_notify_change,
 };
+
+struct inode_operations fat_symlink_inode_operations = {
+       .readlink       = page_readlink,
+       .follow_link    = page_follow_link_light,
+       .setattr        = fat_notify_change,
+};
Index: alp-2.6.12-p8/fs/fat/inode.c
===================================================================
--- alp-2.6.12-p8.orig/fs/fat/inode.c	2005-08-12 21:35:51.000000000 +0900
+++ alp-2.6.12-p8/fs/fat/inode.c	2005-08-15 20:48:02.000000000 +0900
@@ -274,6 +274,10 @@
 		MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
 		inode->i_size = le32_to_cpu(de->size);
 		inode->i_op = &fat_file_inode_operations;
+		if (sbi->options.posix_attr
+			&& is_vfat_posix_symlink(inode, de)){
+				inode->i_op = &fat_symlink_inode_operations;
+		}
 		inode->i_fop = &fat_file_operations;
 		inode->i_mapping->a_ops = &fat_aops;
 		MSDOS_I(inode)->mmu_private = inode->i_size;
@@ -499,8 +503,11 @@
 	fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date);
 	if (sbi->options.isvfat) {
 		fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate);
-		raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 +
-			inode->i_ctime.tv_nsec / 10000000;
+		if (set_vfat_posix_attr(raw_entry, inode) == -1) {
+			raw_entry->ctime_cs 
+				= (inode->i_ctime.tv_sec & 1) * 100 +
+					inode->i_ctime.tv_nsec / 10000000;
+		}
 	}
 	spin_unlock(&sbi->inode_hash_lock);
 	mark_buffer_dirty(bh);
@@ -742,6 +749,8 @@
 			seq_puts(m, ",uni_xlate");
 		if (!opts->numtail)
 			seq_puts(m, ",nonumtail");
+		if (opts->posix_attr)
+			seq_puts(m, ",posix_attr");
 	}
 
 	return 0;
@@ -755,6 +764,7 @@
 	Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
 	Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
 	Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
+	Opt_posix_attr_no, Opt_posix_attr_yes,
 	Opt_obsolate, Opt_err,
 };
 
@@ -823,6 +833,13 @@
 	{Opt_nonumtail_yes, "nonumtail=yes"},
 	{Opt_nonumtail_yes, "nonumtail=true"},
 	{Opt_nonumtail_yes, "nonumtail"},
+	{Opt_posix_attr_no, "posix_attr=0"},		/* 0 or no or false */
+	{Opt_posix_attr_no, "posix_attr=no"},
+	{Opt_posix_attr_no, "posix_attr=false"},
+	{Opt_posix_attr_yes, "posix_attr=1"},	/* empty or 1 or yes or true */
+	{Opt_posix_attr_yes, "posix_attr=yes"},
+	{Opt_posix_attr_yes, "posix_attr=true"},
+	{Opt_posix_attr_yes, "posix_attr"},
 	{Opt_err, NULL}
 };
 
@@ -980,6 +997,12 @@
 		case Opt_nonumtail_yes:		/* empty or 1 or yes or true */
 			opts->numtail = 0;	/* negated option */
 			break;
+		case Opt_posix_attr_no:		/* 0 or no or false */
+			opts->posix_attr = 0;
+			break;
+		case Opt_posix_attr_yes:	/* empty or 1 or yes or true */
+			opts->posix_attr = 1;
+			break;
 
 		/* obsolete mount options */
 		case Opt_obsolate:
Index: alp-2.6.12-p8/fs/vfat/namei.c
===================================================================
--- alp-2.6.12-p8.orig/fs/vfat/namei.c	2005-08-12 21:35:51.000000000 +0900
+++ alp-2.6.12-p8/fs/vfat/namei.c	2005-08-15 21:29:05.354135836 +0900
@@ -24,6 +24,173 @@
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 #include <linux/namei.h>
+#include <linux/fs.h>
+
+#define	GET_INODE_FILE_TYPE(x)	(((x)->i_mode & S_IFMT) >> 12)
+#define	GET_INODE_USER_ID(x)	((x)->i_uid ? 1 : 0)
+#define	GET_INODE_DRV_MAJOR(x)	((x)->i_rdev >> MINORBITS)
+#define	GET_INODE_DRV_MINOR(x)	((x)->i_rdev & MINORMASK)
+
+static unsigned short  filetype_table[] = {
+	DT_REG, /* place folder for backward compat */
+	DT_LNK,
+	DT_BLK,
+	DT_CHR,
+	DT_FIFO,
+	DT_SOCK,
+	0
+};
+
+/**
+ * is_vfat_posix_symlink - check if posix file type from VFAT dir is symlink
+ *
+ * Returns
+ *	        0 ... is not symlink or don't have posix attributes
+ *	otherwise ... is symlink
+ */
+int is_vfat_posix_symlink(struct inode *inode, struct msdos_dir_entry *dentry)
+{
+	if ((dentry->attr & ATTR_SYS) && get_pxattr_specf(dentry)) {
+		int size = le16_to_cpu(dentry->size);
+		if ((size <= PATH_MAX) && (size <= PAGE_SIZE))
+			return filetype_table[get_pxattr_ftype(dentry)] == DT_LNK;
+	}
+	return 0;
+}
+
+/*
+ * get_vfat_posix_attr - Retrieve posix attributes from VFAT dir entry
+ *
+ * Returns
+ *	 0 ... posix_attr are get
+ *	-1 ... posix_attr are not get
+ */
+static 
+int get_vfat_posix_attr(struct inode *inode, struct msdos_dir_entry *dentry)
+{
+	int px_uid, px_gid;
+	struct msdos_sb_info *sbi;
+	int ftype;
+	int umode, gmode, omode;
+
+	if (!(inode && dentry) || IS_ERR(inode)) goto not_get;
+	sbi = MSDOS_SB(inode->i_sb);
+	if ((!sbi->options.posix_attr) || (dentry->attr == ATTR_EXT) || 
+	    (dentry->attr & ATTR_VOLUME)) goto not_get;
+
+	/* File type : 0xF000 : 12 */
+	ftype = -1;
+	if (!(dentry->attr & ATTR_SYS) || (dentry->attr & ATTR_DIR)) {
+		if (get_pxattr_regf(dentry))
+			ftype = (dentry->attr & ATTR_DIR) ? DT_DIR : DT_REG;
+	} else if (get_pxattr_specf(dentry)) {
+		int size = le16_to_cpu(dentry->size);
+		ftype = filetype_table[get_pxattr_ftype(dentry)]; 
+		if (ftype == DT_LNK) {
+			if ((size > PATH_MAX) || (size > PAGE_SIZE)) ftype = -1;
+		} else {
+			if (size) ftype = -1;
+		}
+	}
+	if (ftype == -1)
+		goto not_get;
+	inode->i_mode = ftype << 12;
+	inode->i_ctime = inode->i_mtime;
+
+	/* User  : 0x01C0) : 6 */
+	/* Group : 0x0038) : 3 */
+	/* Other : 0x0007 */
+	umode = (get_pxattr_ur(dentry) ? S_IRUSR : 0) |
+		(get_pxattr_uw(dentry) ? S_IWUSR : 0) |
+		(get_pxattr_ux(dentry) ? S_IXUSR : 0);
+	gmode = (get_pxattr_gr(dentry) ? S_IRGRP : 0) |
+		(get_pxattr_gw(dentry) ? S_IWGRP : 0) |
+		(get_pxattr_gx(dentry) ? S_IXGRP : 0);
+	omode = gmode >> 3;
+	inode->i_mode |= (umode | gmode | omode);
+
+	/* User & Group ID */
+	px_uid = sbi->options.fs_uid ? sbi->options.fs_uid : (u16)-1;
+	px_gid = sbi->options.fs_gid ? sbi->options.fs_gid : (u16)-1;
+	inode->i_uid = get_pxattr_uid(dentry) ?  px_uid : 0;
+	inode->i_gid = get_pxattr_uid(dentry) ?  px_gid : 0;
+
+	/* Special file */
+	if ((ftype==DT_BLK) || (ftype==DT_CHR)) {
+		inode->i_rdev = ((get_pxattr_major(dentry) << MINORBITS) | 
+				  get_pxattr_minor(dentry));
+		inode->i_mode &= ~S_IFMT;
+		inode->i_mode |= (ftype == DT_BLK) ? S_IFBLK : S_IFCHR;
+		init_special_inode(inode, inode->i_mode, inode->i_rdev);
+	} else if ((ftype==DT_FIFO) || (ftype==DT_SOCK)) {
+		inode->i_mode &= ~S_IFMT;
+		inode->i_mode |= (ftype == DT_FIFO) ? S_IFIFO : S_IFSOCK;
+		init_special_inode(inode, inode->i_mode, inode->i_rdev);
+	}
+	return 0;
+not_get:
+	return -1;
+
+}
+
+/**
+ * set_vfat_posix_attr - set posix attributes to VFAT dir entry
+ *
+ * Returns
+ *	 0 ... posix_attr are set
+ *	-1 ... posix_attr are not set
+ */
+int set_vfat_posix_attr(struct msdos_dir_entry *dentry, struct inode *inode)
+{
+	int     ftype;
+	int     iftype;
+	int	mode;
+
+	if (!(inode && dentry) || IS_ERR(inode)) goto not_set;
+	if (!MSDOS_SB(inode->i_sb)->options.posix_attr) goto not_set;
+
+	/* File type */
+	iftype = GET_INODE_FILE_TYPE(inode);
+	switch (iftype) {
+	case DT_DIR:
+		dentry->attr |= ATTR_DIR;
+		/* fall through */
+	case DT_REG:
+		set_pxattr_regf(dentry, 1);
+		break;
+	default:
+		for(ftype=0; filetype_table[ftype]; ftype++){
+			if (filetype_table[ftype] == iftype) 
+				break;
+		}
+		if (!filetype_table[ftype]) goto not_set;
+		/* mark posix attr for special file */
+		dentry->attr |= ATTR_SYS;
+		set_pxattr_specf(dentry, 1);
+		set_pxattr_ftype(dentry, ftype);
+		break;
+	}
+	/* Permissions for Owner */
+	mode = inode->i_mode;
+	set_pxattr_ur(dentry, mode & S_IRUSR);
+	set_pxattr_uw(dentry, mode & S_IWUSR);
+	set_pxattr_ux(dentry, mode & S_IXUSR);
+	/* Permissions for Group/Others */
+	set_pxattr_gr(dentry, mode & S_IRGRP);
+	set_pxattr_gw(dentry, mode & S_IWGRP);
+	set_pxattr_gx(dentry, mode & S_IXGRP);
+	/* User ID */
+	set_pxattr_uid(dentry, GET_INODE_USER_ID(inode));
+
+	/* Deivce number */
+	if ((iftype==DT_BLK) || (iftype==DT_CHR)) {
+		set_pxattr_major(dentry, GET_INODE_DRV_MAJOR(inode));
+		set_pxattr_minor(dentry, GET_INODE_DRV_MINOR(inode));
+	}
+	return 0;
+not_set:
+	return -1;
+}
 
 static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
@@ -721,6 +888,7 @@
 		goto error;
 	}
 	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+	(void) get_vfat_posix_attr(inode, sinfo.de);
 	brelse(sinfo.bh);
 	if (IS_ERR(inode)) {
 		unlock_kernel();
@@ -774,6 +942,10 @@
 	}
 	inode->i_version++;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
+	if (MSDOS_SB(sb)->options.posix_attr)  {
+		inode->i_mode =	mode & VFAT_POSIX_ATTR_VALID_MODE;
+		mark_inode_dirty(inode);
+	}
 	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
 
 	dentry->d_time = dentry->d_parent->d_inode->i_version;
@@ -868,6 +1040,10 @@
 	inode->i_version++;
 	inode->i_nlink = 2;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
+	if (MSDOS_SB(sb)->options.posix_attr) {
+		inode->i_mode = (S_IFDIR | (mode & VFAT_POSIX_ATTR_VALID_MODE));
+		mark_inode_dirty(inode);
+	}
 	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
 
 	dentry->d_time = dentry->d_parent->d_inode->i_version;
@@ -1023,6 +1199,100 @@
 	goto out;
 }
 
+static
+int vfat_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+	/* base : vfat_create() */
+	struct super_block *sb = dir->i_sb;
+	struct inode *inode = NULL;
+	struct fat_slot_info sinfo;
+	struct timespec ts;
+	int err;
+	int len;
+
+	if (!MSDOS_SB(sb)->options.posix_attr)
+		return -EOPNOTSUPP;
+
+	len = strlen (symname) + 1;
+	if ((len > PATH_MAX) || (len > PAGE_SIZE)) {
+		return -ENAMETOOLONG;
+	}
+
+	lock_kernel();
+
+	ts = CURRENT_TIME_SEC;
+	err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo);
+	if (err)
+		goto out;
+	dir->i_version++;
+
+	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+	brelse(sinfo.bh);
+	if (IS_ERR(inode)){
+		err = PTR_ERR(inode);
+		goto out;
+	}
+	inode->i_version++;
+	inode->i_mode = (S_IFLNK | 0777);
+	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
+	inode->i_op = &fat_symlink_inode_operations;
+	mark_inode_dirty(inode);
+
+	dentry->d_time = dentry->d_parent->d_inode->i_version;
+	d_instantiate(dentry,inode);
+
+	err = page_symlink(dentry->d_inode, symname, len);
+out:
+	unlock_kernel();
+	return err;
+}
+
+static
+int vfat_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+{
+	/* base : vfat_create() */
+	struct super_block *sb = dir->i_sb;
+	struct inode *inode = NULL;
+	struct fat_slot_info sinfo;
+	struct timespec ts;
+	int err;
+
+	if (!MSDOS_SB(sb)->options.posix_attr)
+		return -EOPNOTSUPP;
+
+	lock_kernel();
+
+	ts = CURRENT_TIME_SEC;
+	err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo);
+	if (err)
+		goto out;
+	dir->i_version++;
+
+	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+	brelse(sinfo.bh);
+	if (IS_ERR(inode)){
+		err = PTR_ERR(inode);
+		goto out;
+	}
+	inode->i_version++;
+
+	inode->i_mode =	mode & VFAT_POSIX_ATTR_VALID_MODE;
+	inode->i_rdev = rdev;
+
+	inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
+	init_special_inode(inode, mode, rdev);
+	mark_inode_dirty(inode);
+
+	dentry->d_time = dentry->d_parent->d_inode->i_version;
+	d_instantiate(dentry, inode);
+
+	err = 0;
+
+out:
+	unlock_kernel();
+	return err;
+}
+
 static struct inode_operations vfat_dir_inode_operations = {
 	.create		= vfat_create,
 	.lookup		= vfat_lookup,
@@ -1031,6 +1301,8 @@
 	.rmdir		= vfat_rmdir,
 	.rename		= vfat_rename,
 	.setattr	= fat_notify_change,
+	.symlink	= vfat_symlink,
+	.mknod		= vfat_mknod,
 };
 
 static int vfat_fill_super(struct super_block *sb, void *data, int silent)
Index: alp-2.6.12-p8/include/linux/msdos_fs.h
===================================================================
--- alp-2.6.12-p8.orig/include/linux/msdos_fs.h	2005-08-12 21:35:51.000000000 +0900
+++ alp-2.6.12-p8/include/linux/msdos_fs.h	2005-08-15 20:48:02.000000000 +0900
@@ -199,6 +199,7 @@
 		 sys_immutable:1, /* set = system files are immutable */
 		 dotsOK:1,        /* set = hidden and system files are named '.filename' */
 		 isvfat:1,        /* 0=no vfat long filename support, 1=vfat support */
+		 posix_attr:1,       /* 1= posix attribute mapping support */
 		 utf8:1,	  /* Use of UTF8 character set (Default) */
 		 unicode_xlate:1, /* create escape sequences for unhandled Unicode */
 		 numtail:1,       /* Does first alias have a numeric '~1' type tail? */
@@ -386,6 +387,7 @@
 			     unsigned int cmd, unsigned long arg);
 extern struct file_operations fat_file_operations;
 extern struct inode_operations fat_file_inode_operations;
+extern struct inode_operations fat_symlink_inode_operations;
 extern int fat_notify_change(struct dentry * dentry, struct iattr * attr);
 extern void fat_truncate(struct inode *inode);
 
@@ -407,6 +409,284 @@
 extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date);
 extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs);
 
+/*
+ * vfat posix attr option "posix_attr" stuffs
+ *
+ * FEATURES
+ *	
+ * Following attributes/modes are supported in posix attributea mapping
+ * in VFAT.
+ *
+ *   - FileType
+ * 	This supports following special files and it's attributes;
+ * 		symbolic link,	block device node,
+ * 		char device node, fifo,	socket
+ * 	Regular files/dirs also may have POSIX attributes.
+ *   - DeviceFile
+ * 	Major and minor number would be held at ctime
+ * 	and both values are limited  to 255.
+ *   - Owner's User ID/Group ID 
+ * 	This can be used to distinguish root and others,
+ * 	because this has just one bit width. 
+ * 	Value of UID/GID for non-root user will be taken from uid/gid 
+ * 	option on mounting. If nothing is specified, system uses 
+ *	(u16)-1 as last resort. That means change-uid may affect on gid.
+ *   - Permission for Group/Other (rwx)
+ * 	Those modes will be kept in ctime_cs.
+ * 	Also permission modes for "others" will be
+ * 	same as "group", due to lack of fields.
+ *	That means set-group-mode may affect on other-mode.
+ *	On the other hand, set-other-mode has no affect to group-mode.
+ *	
+ *   - Permission for Owner (rwx)
+ * 	These modes will be mapped to FAT attributes.
+ * 	Just same as mapping under VFAT.
+ *   - Others
+ * 	no sticky, setgid nor setuid bits are not supported.
+ * 
+ *ALGORITHM FOR MAPPING DECISION
+ *   - Regular file/dir
+ * 	To distinguish regular files/dirs, look if this fat dir 
+ * 	entry doesn't have ATTR_SYS, first. If it doesn't have 
+ * 	ATTR_SYS, then check if TYPE field (MSB 3bits) in ctime_cs 
+ * 	is equal to 7. If so, this regular file/dir is created and/or 
+ * 	modified under VFAT with "posix_attr". And posix attribute 
+ * 	mapping can be take place. Otherwise, conventional VFAT 
+ * 	attribute mapping is used.
+ *
+ *   - Special file
+ * 	To distinguish special files, look if this fat dir entry 
+ * 	has ATTR_SYS, first. Also we need to check it not to have 
+ *	ATT_EXT.
+ *	If it has ATTR_SYS, then check 1st. LSB bit in ctime_cs, 
+ *	referred as "special file flag".
+ * 	If set,  this file is created under VFAT with "posix_attr". 
+ * 	Look up TYPE field to decide special file type.
+ *
+ * 	This special file detection method has some flaw to make
+ * 	potential confusion. E.g. some system file created under
+ *	dos/win may be treated as special file.  However in most case,
+ *	user don't create system file under dos/win.
+ *	To reduce possiblity of this confusion, system makes
+ *	sure special files except symlink have size ZERO.
+ *	For symlink, system checks it's size not to exceed page size 
+ *	and PATH_MAX.
+ *
+ *FAT DIR ENTRY FIELDS
+ *
+ *   - ctime_cs
+ * 	    8bit byte
+ * 	7 6 5 4 3 2 1 0
+ * 	|===| | | | | |
+ * 	TYPE  | | | | +- special file flag (valid if ATTR_SYS)
+ * 	      | | | +--- User/Group ID(root or others)
+ * 	      | | +----- !group X
+ * 	      | +------- !group W
+ * 	      +--------- !group R
+ * 
+ * 	  special file flag
+ * 		Indicate this entry has posix attribute mapping.
+ * 		This field is valid for fat dir entry, which 
+ * 		have ATTR_SYS.
+ * 
+ * 	  special file TYPE
+ * 		val	type on VFS(val)	Description
+ * 		------------------------------------------------
+ * 		0 	(place folder for backward compat)
+ * 		1 	DT_LNK (10)		symbolic link
+ * 		2	DT_BLK (6)		block dev
+ * 		3	DT_CHR (4)		char dev
+ * 		4	DT_FIFO (1)		fifo
+ * 		5	DT_SOCK	(12)		socket
+ * 	
+ * 		7*)	(reserved for DT_REG/DT_DIR) 
+ * 
+ * 		*)Value 7 is reserved for regular file/dir (DT_REG/DT_DIR).
+ * 		Normally ctime_cs would have 0-199 value to stand for 
+ * 		up to 2sec. The value for DT_REG/DT_DIR is selected 
+ * 		to be over this range to distinguish if file was created
+ * 		under POSIX_ATTR or not.
+ *
+ *   - attr
+ * 	FAT attribute	(val)		mapped attribute
+ * 	------------------------------------------------
+ * 	ATTR_RO		0x01 		!owner W
+ * 	ATTR_HIDDEN	0x02		!owner R
+ * 	ATTR_SYS	0x04
+ * 	ATTR_VOLUME	0x08
+ * 	ATTR_DIR	0x10		DIR
+ * 	ATTR_ARCH	0x20		!owner X
+ *
+ *   - ctime
+ * 		16bit word
+ * 	f e d c b a 9 8 7 6 5 4 3 2 1 0
+ * 	|=============| |-------------|
+ * 	  major		  minor
+ * 
+ */
+
+#define VFAT_POSIX_ATTR_VALID_MODE	(S_IFMT|S_IRWXU|S_IRWXG|S_IRWXO)
+
+#define VFAT_CS_FMSK	0xe0
+#define VFAT_CS_FSFT	5
+#define VFAT_CS_FREG	0xe0
+
+#define VFAT_CS_SPCF	0x01
+#define VFAT_CS_UID	0x02
+#define VFAT_CS_NXGRP	0x04
+#define VFAT_CS_NWGRP	0x08
+#define VFAT_CS_NRGRP	0x10
+
+/* regular file/dir flag */
+static inline int get_pxattr_regf(struct msdos_dir_entry *de)
+{
+	return (de->ctime_cs & VFAT_CS_FMSK) == VFAT_CS_FREG;
+}
+static inline void set_pxattr_regf(struct msdos_dir_entry *de, int val)
+{
+	val = val ? VFAT_CS_FMSK : 0;
+	de->ctime_cs = (val | (de->ctime_cs & (~VFAT_CS_FMSK)));
+}
+
+/* file type */
+static inline int get_pxattr_ftype(struct msdos_dir_entry *de)
+{
+	return ((de->ctime_cs & VFAT_CS_FMSK) >> VFAT_CS_FSFT);
+}
+static inline void set_pxattr_ftype(struct msdos_dir_entry *de, int val)
+{
+	val = (val  << VFAT_CS_FSFT) & VFAT_CS_FMSK;
+	de->ctime_cs = (val | (de->ctime_cs & (~VFAT_CS_FMSK)));
+}
+
+/* special file flag */
+static inline int get_pxattr_specf(struct msdos_dir_entry *de)
+{
+	return de->ctime_cs & VFAT_CS_SPCF;
+}
+static inline void set_pxattr_specf(struct msdos_dir_entry *de, int val)
+{
+	val = val ? VFAT_CS_SPCF : 0;
+	de->ctime_cs = (val | (de->ctime_cs & (~VFAT_CS_SPCF)));
+}
+
+/* user r */
+static inline int get_pxattr_ur(struct msdos_dir_entry *de)
+{
+	return !(de->attr & ATTR_HIDDEN);
+}
+static inline void set_pxattr_ur(struct msdos_dir_entry *de, int val)
+{
+	val = val ? 0 : ATTR_HIDDEN;
+	de->attr = (val | (de->attr & ~ATTR_HIDDEN));
+}
+
+/* user w */
+static inline int get_pxattr_uw(struct msdos_dir_entry *de)
+{
+	return !(de->attr & ATTR_RO);
+}
+static inline void set_pxattr_uw(struct msdos_dir_entry *de, int val)
+{
+	val = val ? 0 : ATTR_RO;
+	de->attr = (val | (de->attr & ~ATTR_RO));
+}
+
+/* user x */
+static inline int get_pxattr_ux(struct msdos_dir_entry *de)
+{
+	return !(de->attr & ATTR_ARCH);
+}
+static inline void set_pxattr_ux(struct msdos_dir_entry *de, int val)
+{
+	val = val ? 0 : ATTR_ARCH;
+	de->attr = (val | (de->attr & ~ATTR_ARCH));
+}
+
+/* group r */
+static inline int get_pxattr_gr(struct msdos_dir_entry *de)
+{
+	return !(de->ctime_cs & VFAT_CS_NRGRP);
+}
+static inline void set_pxattr_gr(struct msdos_dir_entry *de, int val)
+{
+	val = val ? 0 : VFAT_CS_NRGRP;
+	de->ctime_cs = (val | (de->ctime_cs & (~VFAT_CS_NRGRP)));
+}
+
+/* group w */
+static inline int get_pxattr_gw(struct msdos_dir_entry *de)
+{
+	return !(de->ctime_cs & VFAT_CS_NWGRP);
+}
+static inline void set_pxattr_gw(struct msdos_dir_entry *de, int val)
+{
+	val = val ? 0 : VFAT_CS_NWGRP;
+	de->ctime_cs = (val | (de->ctime_cs & (~VFAT_CS_NWGRP)));
+}
+
+/* group x */
+static inline int get_pxattr_gx(struct msdos_dir_entry *de)
+{
+	return !(de->ctime_cs & VFAT_CS_NXGRP);
+}
+static inline void set_pxattr_gx(struct msdos_dir_entry *de, int val)
+{
+	val = val ? 0 : VFAT_CS_NXGRP;
+	de->ctime_cs = (val | (de->ctime_cs & (~VFAT_CS_NXGRP)));
+}
+
+/* user id */
+static inline int get_pxattr_uid(struct msdos_dir_entry *de)
+{
+	return (de->ctime_cs & VFAT_CS_UID) != 0;
+}
+static inline void set_pxattr_uid(struct msdos_dir_entry *de, int val)
+{
+	val = val ? VFAT_CS_UID : 0;
+	de->ctime_cs = (val | (de->ctime_cs & (~VFAT_CS_UID)));
+}
+
+/* driver major number */
+static inline int get_pxattr_major(struct msdos_dir_entry *de)
+{
+	return ((le16_to_cpu(de->ctime) & 0xff00) >> 8);
+}
+static inline void set_pxattr_major(struct msdos_dir_entry *de, int val)
+{
+	val = (val & 0xff) << 8;
+	de->ctime = cpu_to_le16((val | (le16_to_cpu(de->ctime) & 0x00ff)));
+}
+
+/* driver minor number */
+static inline int get_pxattr_minor(struct msdos_dir_entry *de)
+{
+	return le16_to_cpu(de->ctime) & 0xff;
+}
+static inline void set_pxattr_minor(struct msdos_dir_entry *de, int val)
+{
+	val &= 0xff;
+	de->ctime = cpu_to_le16(val | (le16_to_cpu(de->ctime) & 0xff00));
+}
+
+#ifdef CONFIG_VFAT_FS
+/* fs/vfat/namei.c */
+extern int is_vfat_posix_symlink(struct inode *, struct msdos_dir_entry *);
+extern int set_vfat_posix_attr(struct msdos_dir_entry *, struct inode *);
+#else /* CONFIG_VFAT_FS */
+static inline int 
+is_vfat_posix_symlink(struct inode *inode, struct msdos_dir_entry *de)
+{
+	return -1;
+}
+
+static inline int 
+set_vfat_posix_attr(struct msdos_dir_entry *de, struct inode *inode)
+{
+	return -1;
+}
+#endif /* CONFIG_VFAT_FS */
+
 #endif /* __KERNEL__ */
 
 #endif

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