diff options
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/Kconfig | 32 | ||||
-rw-r--r-- | fs/fat/dir.c | 17 | ||||
-rw-r--r-- | fs/fat/namei_vfat.c | 113 |
3 files changed, 156 insertions, 6 deletions
diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig index 3ff1772f612e..ca5c78bda750 100644 --- a/fs/fat/Kconfig +++ b/fs/fat/Kconfig @@ -74,6 +74,27 @@ config VFAT_FS To compile this as a module, choose M here: the module will be called vfat. +config VFAT_FS_NO_DUALNAMES + bool "disable VFAT dual names support (patent workaround)" + depends on VFAT_FS + help + This option disables support for dual filenames on VFAT filesystems. + If this option is enabled then file creation will either put + a short (8.3) name or a long name on the file, but never both. + The field where a shortname would normally go is filled with + invalid characters such that it cannot be considered a valid + short filename. + + That means that long filenames created with this option + disabled will not be accessible at all to operating systems + that do not understand the FAT long filename extensions. + + Users considering disabling this option should consider the + implications of any patents that may exist on dual filenames + in VFAT. + + If unsure, say N + config FAT_DEFAULT_CODEPAGE int "Default codepage for FAT" depends on MSDOS_FS || VFAT_FS @@ -100,6 +121,17 @@ config FAT_DEFAULT_IOCHARSET Enable any character sets you need in File Systems/Native Language Support. +config VFAT_NO_CREATE_WITH_LONGNAMES + bool "Disable creating files with long names" + depends on VFAT_FS + default n + help + Set this to disable support for creating files or directories with + names longer than 8.3 (the original DOS maximum file name length) + e.g. naming a file FILE1234.TXT would be allowed but creating or + renaming a file to FILE12345.TXT or FILE1234.TEXT would not + be permitted. Reading files with long file names is still permitted. + config FAT_DEFAULT_UTF8 bool "Enable FAT UTF-8 option by default" depends on VFAT_FS diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 8e100c3bf72c..ca2321110a78 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -368,7 +368,8 @@ static int fat_parse_short(struct super_block *sb, dotoffset = 1; } - memcpy(work, de->name, sizeof(work)); + memcpy(work, de->name, sizeof(de->name)); + /* see namei.c, msdos_format_name */ if (work[0] == 0x05) work[0] = 0xE5; @@ -498,6 +499,13 @@ parse_record: goto end_of_dir; } + /* + * The FAT_NO_83NAME flag is used to mark files + * created with no 8.3 short name + */ + if (de->lcase & FAT_NO_83NAME) + goto compare_longname; + /* Never prepend '.' to hidden files here. * That is done only for msdos mounts (and only when * 'dotsOK=yes'); if we are executing here, it is in the @@ -511,6 +519,7 @@ parse_record: if (fat_name_match(sbi, name, name_len, bufname, len)) goto found; +compare_longname: if (nr_slots) { void *longname = unicode + FAT_MAX_UNI_CHARS; int size = PATH_MAX - FAT_MAX_UNI_SIZE; @@ -602,6 +611,8 @@ parse_record: if (de->attr != ATTR_EXT && IS_FREE(de->name)) goto record_end; } else { + if (de->lcase & FAT_NO_83NAME) + goto record_end; if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) goto record_end; } @@ -960,6 +971,10 @@ int fat_scan(struct inode *dir, const unsigned char *name, sinfo->bh = NULL; while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh, &sinfo->de) >= 0) { + /* skip files marked as having no 8.3 short name */ + if (sinfo->de->lcase & FAT_NO_83NAME) + continue; + if (!strncmp(sinfo->de->name, name, MSDOS_NAME)) { sinfo->slot_off -= sizeof(*sinfo->de); sinfo->nr_slots = 1; diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 9a5469120caa..60caf6e730ad 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -21,6 +21,7 @@ #include <linux/namei.h> #include <linux/kernel.h> #include <linux/iversion.h> +#include <linux/random.h> #include "fat.h" static inline unsigned long vfat_d_version(struct dentry *dentry) @@ -335,6 +336,17 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen; int is_shortname; struct shortname_info base_info, ext_info; + unsigned opts_shortname = opts->shortname; + +#ifdef CONFIG_VFAT_FS_NO_DUALNAMES + /* + * When we do not have dualnames, we want to maximise the + * chance that a file will be able to be represented with just + * a 8.3 entry. We can do that by using the WINNT case + * handling extensions to FAT. + */ + opts_shortname = VFAT_SFN_CREATE_WINNT; +#endif is_shortname = 1; INIT_SHORTNAME_INFO(&base_info); @@ -443,13 +455,22 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, memcpy(name_res, base, baselen); memcpy(name_res + 8, ext, extlen); *lcase = 0; + +#ifdef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES + if (is_shortname == 0) + return -ENAMETOOLONG; + if (!base_info.valid || !ext_info.valid) + return -EINVAL; + opts_shortname = VFAT_SFN_CREATE_WINNT; +#endif + if (is_shortname && base_info.valid && ext_info.valid) { if (vfat_find_form(dir, name_res) == 0) return -EEXIST; - if (opts->shortname & VFAT_SFN_CREATE_WIN95) { + if (opts_shortname & VFAT_SFN_CREATE_WIN95) { return (base_info.upper && ext_info.upper); - } else if (opts->shortname & VFAT_SFN_CREATE_WINNT) { + } else if (opts_shortname & VFAT_SFN_CREATE_WINNT) { if ((base_info.upper || base_info.lower) && (ext_info.upper || ext_info.lower)) { if (!base_info.upper && base_info.lower) @@ -575,6 +596,66 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, return 0; } +#ifdef CONFIG_VFAT_FS_NO_DUALNAMES +/* + * This function creates a dummy 8.3 entry which is as compatible as + * possible with existing FAT devices, while not being a valid + * filename under windows or Linux + */ +static void vfat_build_dummy_83_buffer(struct inode *dir, char *msdos_name, + int is_dir) +{ + /* + * These characters are all invalid in 8.3 names, plus have + * been shown to be harmless on all tested devices + */ + const char invalidchar[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0B, + 0x0C, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x22, 0x2a, + 0x3a, 0x3c, 0x3e, 0x3f, 0x5b, 0x5d, 0x7c }; + int i, tilde_pos, slash_pos; + u32 rand_num = prandom_u32(); + + /* We need a '~' in the prefix to make Win98 happy. */ + tilde_pos = rand_num % 8; + rand_num >>= 3; + + /* + * the '/' makes sure that even unpatched Linux systems can't + * get at files by the 8.3 entry. Don't put in a / in + * directories as it can cause problems with some + * photo frames + */ + if (is_dir) + slash_pos = -1; + else { + slash_pos = (tilde_pos + 1 + rand_num % 7) % 8; + rand_num >>= 3; + } + + /* + * fill in the first 8 bytes with invalid characters. Note + * that we need to be careful not to run out of randomness. We + * leave the 3 byte extension in place as some cheap MP3 + * players need them. + */ + for (i = 0; i < 8; i++) { + if (i == tilde_pos) + msdos_name[i] = '~'; + else if (i == slash_pos) + msdos_name[i] = '/'; + else { + msdos_name[i] = + invalidchar[rand_num % sizeof(invalidchar)]; + rand_num /= sizeof(invalidchar); + if (rand_num < sizeof(invalidchar)) + rand_num = prandom_u32(); + } + } +} +#endif + static int vfat_build_slots(struct inode *dir, const unsigned char *name, int len, int is_dir, int cluster, struct timespec *ts, @@ -582,15 +663,19 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, { struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); struct fat_mount_options *opts = &sbi->options; - struct msdos_dir_slot *ps; struct msdos_dir_entry *de; - unsigned char cksum, lcase; + unsigned char lcase; unsigned char msdos_name[MSDOS_NAME]; wchar_t *uname; __le16 time, date; u8 time_cs; - int err, ulen, usize, i; + int err, ulen, usize; +#ifndef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES + int i; + struct msdos_dir_slot *ps; + unsigned char cksum; loff_t offset; +#endif *nr_slots = 0; @@ -617,6 +702,16 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, goto shortname; } +#ifdef CONFIG_VFAT_FS_NO_DUALNAMES + printk_once(KERN_INFO + "VFAT: not creating 8.3 short filenames for long names\n"); + vfat_build_dummy_83_buffer(dir, msdos_name, is_dir); + lcase = FAT_NO_83NAME; +#endif + +#ifdef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES + de = (struct msdos_dir_entry *)slots; +#else /* build the entry of long file name */ cksum = fat_checksum(msdos_name); @@ -634,6 +729,7 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, } slots[0].id |= 0x40; de = (struct msdos_dir_entry *)ps; +#endif shortname: /* build the entry of 8.3 alias name */ @@ -1063,7 +1159,11 @@ static struct dentry *vfat_mount(struct file_system_type *fs_type, static struct file_system_type vfat_fs_type = { .owner = THIS_MODULE, +#ifdef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES + .name = "lfat", +#else .name = "vfat", +#endif .mount = vfat_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, @@ -1083,6 +1183,9 @@ static void __exit exit_vfat_fs(void) MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("VFAT filesystem support"); MODULE_AUTHOR("Gordon Chaffee"); +#ifdef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES +MODULE_ALIAS("lfat"); +#endif module_init(init_vfat_fs) module_exit(exit_vfat_fs) |