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 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 #include #include +#include + +#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