linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] FAT extension filters
@ 2002-05-15  2:38 Malcolm Smith
  2002-05-15  3:18 ` Albert D. Cahalan
  0 siblings, 1 reply; 8+ messages in thread
From: Malcolm Smith @ 2002-05-15  2:38 UTC (permalink / raw)
  To: linux-kernel, chaffee

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 */


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] FAT extension filters
  2002-05-15  2:38 [RFC] FAT extension filters Malcolm Smith
@ 2002-05-15  3:18 ` Albert D. Cahalan
  2002-05-15 12:39   ` OGAWA Hirofumi
  2002-05-15 12:49   ` Xavier Bestel
  0 siblings, 2 replies; 8+ messages in thread
From: Albert D. Cahalan @ 2002-05-15  3:18 UTC (permalink / raw)
  To: Malcolm Smith; +Cc: linux-kernel, chaffee

> (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]]]]

Some ideas:

1. use this to express the existing showexec option
2. make this work for all non-UNIX filesystems
3. specify the data some other place

While you're hacking around FAT stuff, see about
using the timestamp of the volume name file for
the root directory. Remember that very old
filesystems won't have a volume name file, so
you might need to create one.

Also I found a bug in the vfat code. It doesn't
properly handle old (pre-vfat) files with names
that start with 0xE5; these are stored on disk
with 0x05 as the first character.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] FAT extension filters
  2002-05-15  3:18 ` Albert D. Cahalan
@ 2002-05-15 12:39   ` OGAWA Hirofumi
  2002-05-15 17:49     ` Albert D. Cahalan
  2002-05-15 12:49   ` Xavier Bestel
  1 sibling, 1 reply; 8+ messages in thread
From: OGAWA Hirofumi @ 2002-05-15 12:39 UTC (permalink / raw)
  To: Albert D. Cahalan; +Cc: Malcolm Smith, linux-kernel, chaffee

"Albert D. Cahalan" <acahalan@cs.uml.edu> writes:

> Also I found a bug in the vfat code. It doesn't
> properly handle old (pre-vfat) files with names
> that start with 0xE5; these are stored on disk
> with 0x05 as the first character.

Umm... why? 0xE5 is free entry mark.
-- 
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] FAT extension filters
  2002-05-15  3:18 ` Albert D. Cahalan
  2002-05-15 12:39   ` OGAWA Hirofumi
@ 2002-05-15 12:49   ` Xavier Bestel
  1 sibling, 0 replies; 8+ messages in thread
From: Xavier Bestel @ 2002-05-15 12:49 UTC (permalink / raw)
  To: Albert D. Cahalan; +Cc: Malcolm Smith, Linux Kernel Mailing List, chaffee

Le mer 15/05/2002 à 05:18, Albert D. Cahalan a écrit :
> Also I found a bug in the vfat code. It doesn't
> properly handle old (pre-vfat) files with names
> that start with 0xE5; these are stored on disk
> with 0x05 as the first character.

Isn't this a deleted file ?

	



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] FAT extension filters
  2002-05-15 12:39   ` OGAWA Hirofumi
@ 2002-05-15 17:49     ` Albert D. Cahalan
  2002-05-15 18:05       ` Mark Mielke
  2002-05-16 13:46       ` OGAWA Hirofumi
  0 siblings, 2 replies; 8+ messages in thread
From: Albert D. Cahalan @ 2002-05-15 17:49 UTC (permalink / raw)
  To: OGAWA Hirofumi; +Cc: Albert D. Cahalan, Malcolm Smith, linux-kernel, chaffee

OGAWA Hirofumi writes:
> "Albert D. Cahalan" <acahalan@cs.uml.edu> writes:

>> Also I found a bug in the vfat code. It doesn't
>> properly handle old (pre-vfat) files with names
>> that start with 0xE5; these are stored on disk
>> with 0x05 as the first character.
>
> Umm... why? 0xE5 is free entry mark.

That is exactly why! You can store a name that
starts with 0xE5 by substituting 0x05. You can
not store a file that starts with 0x05, because
it would appear to start with 0xE5 when you
read it back.

Using plain old MS-DOS, or Linux right before the
vfat code was merged, create a file with this name:

E5 44 05 44 E5 44 44 44   44 E5 05

On disk it gets stored as this:

05 44 05 44 E5 44 44 44   44 E5 05
^^

Now remount or reboot so you don't cheat by
accident. Do an "ls -l" and note that you
see the original filename. The 0xE5 is at
the beginning of the name, and the 0x05 in
the middle has not been mangled.

Using vfat is not supposed to make old msdos
files inaccessible. I discovered this problem
while trying to make a backup of my old FAT
partition. Even "ls -l" would fail.

I think the problem is in fs/fat/dir.c where
it does:

        for (i = 0; i < 8; i++) {
                /* see namei.c, msdos_format_name */
                if (de->name[i] == 0x05)
                        work[i] = 0xE5;
                else
                        work[i] = de->name[i];
        }

That should be:

        for (i = 0; i < 8; i++) work[i] = 0xE5;
         /* see namei.c, msdos_format_name */
        if (*work == 0x05) *work = 0xE5;

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] FAT extension filters
  2002-05-15 17:49     ` Albert D. Cahalan
@ 2002-05-15 18:05       ` Mark Mielke
  2002-05-15 18:42         ` Albert D. Cahalan
  2002-05-16 13:46       ` OGAWA Hirofumi
  1 sibling, 1 reply; 8+ messages in thread
From: Mark Mielke @ 2002-05-15 18:05 UTC (permalink / raw)
  To: Albert D. Cahalan; +Cc: OGAWA Hirofumi, Malcolm Smith, linux-kernel, chaffee

On Wed, May 15, 2002 at 01:49:46PM -0400, Albert D. Cahalan wrote:
> I think the problem is in fs/fat/dir.c where
> it does:
>         for (i = 0; i < 8; i++) {
>                 /* see namei.c, msdos_format_name */
>                 if (de->name[i] == 0x05)
>                         work[i] = 0xE5;
>                 else
>                         work[i] = de->name[i];
>         }
> That should be:
>         for (i = 0; i < 8; i++) work[i] = 0xE5;
>          /* see namei.c, msdos_format_name */
>         if (*work == 0x05) *work = 0xE5;

I assume that should be:

>         for (i = 0; i < 8; i++) work[i] = de->name[i];
>          /* see namei.c, msdos_format_name */
>         if (*work == 0x05) *work = 0xE5;

The comment from msdos/namei.c reads:

  /*  0xE5 is legal as a first character, but we must substitute 0x05     */
  /*  because 0xE5 marks deleted files.  Yes, DOS really does this.       */
  /*  It seems that Microsoft hacked DOS to support non-US characters     */
  /*  after the 0xE5 character was already in use to mark deleted files.  */

A question for the long-time kernel developers who are kind enough to
spend time on a question such as this:

Should the code:

>         for (i = 0; i < 8; i++) work[i] = de->name[i];

Be written as some sort of memcpy()? I would expect the inlined
version to do two 32-bit copies, or one 64-bit copy operation for
64-bit platforms for the above piece of code. Is much of the code
written as the above? Or is effort made to try and inline it
in some better architecture-specific way?

mark

-- 
mark@mielke.cc/markm@ncf.ca/markm@nortelnetworks.com __________________________
.  .  _  ._  . .   .__    .  . ._. .__ .   . . .__  | Neighbourhood Coder
|\/| |_| |_| |/    |_     |\/|  |  |_  |   |/  |_   | 
|  | | | | \ | \   |__ .  |  | .|. |__ |__ | \ |__  | Ottawa, Ontario, Canada

  One ring to rule them all, one ring to find them, one ring to bring them all
                       and in the darkness bind them...

                           http://mark.mielke.cc/


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] FAT extension filters
  2002-05-15 18:05       ` Mark Mielke
@ 2002-05-15 18:42         ` Albert D. Cahalan
  0 siblings, 0 replies; 8+ messages in thread
From: Albert D. Cahalan @ 2002-05-15 18:42 UTC (permalink / raw)
  To: Mark Mielke
  Cc: Albert D. Cahalan, OGAWA Hirofumi, Malcolm Smith, linux-kernel, chaffee

Mark Mielke writes:
> On Wed, May 15, 2002 at 01:49:46PM -0400, Albert D. Cahalan wrote:

>> I think the problem is in fs/fat/dir.c where
>> it does:
>>         for (i = 0; i < 8; i++) {
>>                 /* see namei.c, msdos_format_name */
>>                 if (de->name[i] == 0x05)
>>                         work[i] = 0xE5;
>>                 else
>>                         work[i] = de->name[i];
>>         }
>> That should be:
>>         for (i = 0; i < 8; i++) work[i] = 0xE5;
>>          /* see namei.c, msdos_format_name */
>>         if (*work == 0x05) *work = 0xE5;
>
> I assume that should be:
>
>>         for (i = 0; i < 8; i++) work[i] = de->name[i];
>>          /* see namei.c, msdos_format_name */
>>         if (*work == 0x05) *work = 0xE5;

Sure, and using memcpy() might be good too. I wasn't
paying much attention. Here:

        memcpy(work,de->name,8);
        /* see namei.c, msdos_format_name */
        if(*work == 0x05) *work = 0xE5;

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] FAT extension filters
  2002-05-15 17:49     ` Albert D. Cahalan
  2002-05-15 18:05       ` Mark Mielke
@ 2002-05-16 13:46       ` OGAWA Hirofumi
  1 sibling, 0 replies; 8+ messages in thread
From: OGAWA Hirofumi @ 2002-05-16 13:46 UTC (permalink / raw)
  To: Albert D. Cahalan; +Cc: Malcolm Smith, linux-kernel, chaffee

"Albert D. Cahalan" <acahalan@cs.uml.edu> writes:

> Using plain old MS-DOS, or Linux right before the
> vfat code was merged, create a file with this name:
> 
> E5 44 05 44 E5 44 44 44   44 E5 05
> 
> On disk it gets stored as this:
> 
> 05 44 05 44 E5 44 44 44   44 E5 05
> ^^
> 
> Now remount or reboot so you don't cheat by
> accident. Do an "ls -l" and note that you
> see the original filename. The 0xE5 is at
> the beginning of the name, and the 0x05 in
> the middle has not been mangled.

Ah, yes. Indeed.

I'll submit the following patch to Linus. Thanks.

diff -urN linux-bk/fs/fat/dir.c linux-2.5.14/fs/fat/dir.c
--- linux-bk/fs/fat/dir.c	Mon May 13 02:28:29 2002
+++ linux-2.5.14/fs/fat/dir.c	Thu May 16 22:39:32 2002
@@ -271,13 +271,10 @@
 				long_slots = 0;
 		}
 
-		for (i = 0; i < 8; i++) {
-			/* see namei.c, msdos_format_name */
-			if (de->name[i] == 0x05)
-				work[i] = 0xE5;
-			else
-				work[i] = de->name[i];
-		}
+		/* see namei.c, msdos_format_name */
+		memcpy(work, de->name, sizeof(de->name));
+		if (de->name[0] == 0x05)
+			work[0] = 0xE5;
 		for (i = 0, j = 0, last_u = 0; i < 8;) {
 			if (!work[i]) break;
 			chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
@@ -478,13 +475,10 @@
 		dotoffset = 1;
 	}
 
-	for (i = 0; i < 8; i++) {
-		/* see namei.c, msdos_format_name */
-		if (de->name[i] == 0x05)
-			work[i] = 0xE5;
-		else
-			work[i] = de->name[i];
-	}
+	/* see namei.c, msdos_format_name */
+	memcpy(work, de->name, sizeof(de->name));
+	if (de->name[0] == 0x05)
+		work[0] = 0xE5;
 	for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) {
 		if (!(c = work[i])) break;
 		chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
-- 
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2002-05-16 13:48 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-05-15  2:38 [RFC] FAT extension filters Malcolm Smith
2002-05-15  3:18 ` Albert D. Cahalan
2002-05-15 12:39   ` OGAWA Hirofumi
2002-05-15 17:49     ` Albert D. Cahalan
2002-05-15 18:05       ` Mark Mielke
2002-05-15 18:42         ` Albert D. Cahalan
2002-05-16 13:46       ` OGAWA Hirofumi
2002-05-15 12:49   ` Xavier Bestel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).