[RFC] FAT extension filters

Malcolm Smith (msmith@operamail.com)
Wed, 15 May 2002 12:38:08 +1000


Gordon/All,

(Sorry if I'm doing stupid things - I'm a newbie. Send me a private
email and I'll fix them.)

This is a patch that adds an extra mount option for msdos/vfat
partitions, which allows you to specify a specific umask/uid/gid for
files with a particular extension. Supports multiple filters using
linked list. Note that this does not provide security on an inherently
insecure fs.

Use -o filter=ext[:[umask][:[uid][:[gid]]]]

This patch is for kernel 2.5.15
- Malcolm

diff -Nur linus-2.5/fs/fat/inode.c linux/fs/fat/inode.c
--- linus-2.5/fs/fat/inode.c Tue May 14 21:38:20 2002
+++ linux/fs/fat/inode.c Tue May 14 22:49:29 2002
@@ -169,6 +169,53 @@
unlock_kernel();
}

+/*
+ * Deletes all the elements from the linked list of filters.
+ */
+void fat_clear_filter(struct fat_filter_data *filter)
+{
+ struct fat_filter_data *this,*next;
+ this=filter;
+ while (this) {
+ next=this->next;
+ kfree(this);
+ this=next;
+ }
+}
+
+/*
+ * Loads a filter option into the filters linked list.
+ */
+void fat_load_filter(struct fat_filter_data *filter, char *value)
+{
+ char *tmp_pointer;
+ tmp_pointer = strchr(value, ':');
+ if (tmp_pointer) *tmp_pointer = '\0';
+ strncpy(filter->extension,value,3);
+ filter->next = NULL;
+ filter->mask_umask = 0;
+ filter->mask_uid = 0;
+ filter->mask_gid = 0;
+ if (tmp_pointer) {
+ if (tmp_pointer[1] != ':') {
+ filter->filter_umask = simple_strtoul(tmp_pointer + 1,
NULL, 8);
+ filter->mask_umask = 1;
+ }
+ tmp_pointer = strchr(tmp_pointer + 1, ':');
+ if (tmp_pointer) {
+ if (tmp_pointer[1] != ':') {
+ filter->filter_uid = simple_strtoul(tmp_pointer + 1,
NULL, 0);
+ filter->mask_uid = 1;
+ }
+ tmp_pointer = strchr(tmp_pointer + 1, ':');
+ if (tmp_pointer) {
+ filter->filter_gid = simple_strtoul(tmp_pointer + 1,
NULL, 0);
+ filter->mask_gid = 1;
+ }
+ }
+ }
+}
+
void fat_put_super(struct super_block *sb)
{
struct msdos_sb_info *sbi = MSDOS_SB(sb);
@@ -196,6 +243,10 @@
kfree(sbi->options.iocharset);
sbi->options.iocharset = NULL;
}
+ if (sbi->options.filter) {
+ fat_clear_filter(sbi->options.filter);
+ sbi->options.filter = NULL;
+ }
sb->u.generic_sbp = NULL;
kfree(sbi);
}
@@ -220,6 +271,7 @@
opts->shortname = 0;
opts->utf8 = 0;
opts->iocharset = NULL;
+ opts->filter = NULL;
*debug = 0;

if (!options)
@@ -256,6 +308,27 @@
opts->conversion = 'a';
else ret = 0;
}
+ else if (!strcmp(this_char,"filter") && value) {
+ struct fat_filter_data *cur_filter;
+ /* Allocates memory in the list for a filter */
+ if (opts->filter == NULL) {
+ opts->filter = kmalloc(
+ sizeof(struct fat_filter_data),
+ GFP_KERNEL);
+ cur_filter=opts->filter;
+ } else {
+ cur_filter = opts->filter;
+ while (cur_filter->next != NULL)
+ cur_filter = cur_filter->next;
+ cur_filter->next =
+ kmalloc(sizeof(struct
fat_filter_data),
+ GFP_KERNEL);
+ cur_filter = cur_filter->next;
+ }
+ /* Processes the filter option itself */
+ if (cur_filter)
+ fat_load_filter(cur_filter, value);
+ }
else if (!strcmp(this_char,"dots")) {
opts->dotsOK = 1;
}
@@ -920,6 +993,10 @@
unload_nls(sbi->nls_disk);
if (sbi->options.iocharset)
kfree(sbi->options.iocharset);
+ if (sbi->options.filter) {
+ fat_clear_filter(sbi->options.filter);
+ sbi->options.filter = NULL;
+ }
if (sbi->private_data)
kfree(sbi->private_data);
sbi->private_data = NULL;
@@ -1001,6 +1078,7 @@
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ struct fat_filter_data *filter;
int error;

MSDOS_I(inode)->i_location = 0;
@@ -1042,6 +1120,20 @@
!is_exec(de->ext))
? S_IRUGO|S_IWUGO : S_IRWXUGO)
& ~sbi->options.fs_umask) | S_IFREG;
+ /* Check if the inode's extension needs special
treatment */
+ filter = fat_is_filtered(de->ext, &sbi->options);
+ if (filter) {
+ if (filter->mask_uid)
+ inode->i_uid = filter->filter_uid;
+ if (filter->mask_gid)
+ inode->i_gid = filter->filter_gid;
+ if (filter->mask_umask)
+ inode->i_mode = MSDOS_MKMODE(de->attr,
+ ((sbi->options.showexec &&
+ !is_exec(de->ext))
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO)
+ & ~filter->filter_umask) | S_IFREG;
+ }
MSDOS_I(inode)->i_start = CF_LE_W(de->start);
if (sbi->fat_bits == 32) {
MSDOS_I(inode)->i_start |=
diff -Nur linus-2.5/fs/fat/misc.c linux/fs/fat/misc.c
--- linus-2.5/fs/fat/misc.c Tue May 14 21:37:24 2002
+++ linux/fs/fat/misc.c Tue May 14 22:36:53 2002
@@ -80,6 +80,19 @@
}
}

+/*
+ * fat_is_filtered returns nonzero if the file should be treated
specially.
+ */
+
+struct fat_filter_data *fat_is_filtered(char *extension,
+ struct fat_mount_options * opts)
+{
+ struct fat_filter_data *walk;
+ for (walk = opts->filter; walk; walk = walk->next)
+ if (!strncmp(extension,walk->extension,3)) return walk;
+ return NULL;
+}
+
void lock_fat(struct super_block *sb)
{
down(&(MSDOS_SB(sb)->fat_lock));
diff -Nur linus-2.5/include/linux/msdos_fs.h
linux/include/linux/msdos_fs.h
--- linus-2.5/include/linux/msdos_fs.h Tue May 14 21:45:55 2002
+++ linux/include/linux/msdos_fs.h Tue May 14 22:57:47 2002
@@ -299,6 +299,9 @@
/* fat/misc.c */
extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
extern int fat_is_binary(char conversion, char *extension);
+extern struct fat_filter_data *
+fat_is_filtered(char *extension,
+ struct fat_mount_options *opts);
extern void lock_fat(struct super_block *sb);
extern void unlock_fat(struct super_block *sb);
extern void fat_clusters_flush(struct super_block *sb);
diff -Nur linus-2.5/include/linux/msdos_fs_sb.h
linux/include/linux/msdos_fs_sb.h
--- linus-2.5/include/linux/msdos_fs_sb.h Thu May 2 23:06:24 2002

+++ linux/include/linux/msdos_fs_sb.h Tue May 14 22:54:32 2002
@@ -3,6 +3,20 @@
#include<linux/fat_cvf.h>

/*
+ * Specifies a filter to be applied to a specific file's extension.
+ */
+struct fat_filter_data {
+ char extension[4]; /* The extension to be filtered
*/
+ uid_t filter_uid; /* The uid of this filter */
+ gid_t filter_gid; /* The gid of this filter */
+ unsigned short filter_umask; /* The umask of this filter */
+ unsigned mask_umask:1, /* Is the umask applied? */
+ mask_uid:1, /* Is the uid applied? */
+ mask_gid:1; /* Is the gid applied? */
+ struct fat_filter_data *next; /* The next filter */
+};
+
+/*
* MS-DOS file system in-core superblock data
*/

@@ -12,6 +26,7 @@
unsigned short fs_umask;
unsigned short codepage; /* Codepage for shortname conversions
*/
char *iocharset; /* Charset used for filename
input/display */
+ struct fat_filter_data *filter; /* List of extensions to be
excised */
unsigned short shortname; /* flags for shortname display/create
rule */
unsigned char name_check; /* r = relaxed, n = normal, s = strict
*/
unsigned char conversion; /* b = binary, t = text, a = auto */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/