* [PATCH 1/9] fat: kill is_bad_inode() check
@ 2008-04-20 18:13 OGAWA Hirofumi
2008-04-20 18:13 ` [PATCH 2/9] fat: fat_notify_change() and check_mode() cleanup OGAWA Hirofumi
0 siblings, 1 reply; 9+ messages in thread
From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, hirofumi
FAT doesn't need to check bad inode anymore.
Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
---
fs/fat/inode.c | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff -puN fs/fat/inode.c~fat-cleanup-bad-inode fs/fat/inode.c
--- linux-2.6/fs/fat/inode.c~fat-cleanup-bad-inode 2008-02-15 17:44:56.000000000 +0900
+++ linux-2.6-hirofumi/fs/fat/inode.c 2008-02-15 17:44:56.000000000 +0900
@@ -433,11 +433,8 @@ EXPORT_SYMBOL_GPL(fat_build_inode);
static void fat_delete_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
-
- if (!is_bad_inode(inode)) {
- inode->i_size = 0;
- fat_truncate(inode);
- }
+ inode->i_size = 0;
+ fat_truncate(inode);
clear_inode(inode);
}
@@ -445,8 +442,6 @@ static void fat_clear_inode(struct inode
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
- if (is_bad_inode(inode))
- return;
lock_kernel();
spin_lock(&sbi->inode_hash_lock);
fat_cache_inval_inode(inode);
_
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 2/9] fat: fat_notify_change() and check_mode() cleanup 2008-04-20 18:13 [PATCH 1/9] fat: kill is_bad_inode() check OGAWA Hirofumi @ 2008-04-20 18:13 ` OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 3/9] fat: fat_setattr() fix OGAWA Hirofumi 0 siblings, 1 reply; 9+ messages in thread From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw) To: akpm; +Cc: linux-kernel, hirofumi - Rename fat_notify_change() to fat_setattr() - check_mode() cleanup - Change layout of code Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> --- fs/fat/file.c | 183 +++++++++++++++++++++------------------------- fs/msdos/namei.c | 2 fs/vfat/namei.c | 2 include/linux/msdos_fs.h | 2 4 files changed, 87 insertions(+), 102 deletions(-) diff -puN fs/fat/file.c~fat_notify_change-cleanup fs/fat/file.c --- linux-2.6/fs/fat/file.c~fat_notify_change-cleanup 2008-02-15 19:09:45.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/file.c 2008-02-15 19:31:52.000000000 +0900 @@ -155,104 +155,6 @@ out: return err; } -static int check_mode(const struct msdos_sb_info *sbi, mode_t mode) -{ - mode_t req = mode & ~S_IFMT; - - /* - * Of the r and x bits, all (subject to umask) must be present. Of the - * w bits, either all (subject to umask) or none must be present. - */ - - if (S_ISREG(mode)) { - req &= ~sbi->options.fs_fmask; - - if ((req & (S_IRUGO | S_IXUGO)) != - ((S_IRUGO | S_IXUGO) & ~sbi->options.fs_fmask)) - return -EPERM; - - if ((req & S_IWUGO) != 0 && - (req & S_IWUGO) != (S_IWUGO & ~sbi->options.fs_fmask)) - return -EPERM; - } else if (S_ISDIR(mode)) { - req &= ~sbi->options.fs_dmask; - - if ((req & (S_IRUGO | S_IXUGO)) != - ((S_IRUGO | S_IXUGO) & ~sbi->options.fs_dmask)) - return -EPERM; - - if ((req & S_IWUGO) != 0 && - (req & S_IWUGO) != (S_IWUGO & ~sbi->options.fs_dmask)) - return -EPERM; - } else { - return -EPERM; - } - - return 0; -} - -int fat_notify_change(struct dentry *dentry, struct iattr *attr) -{ - struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); - struct inode *inode = dentry->d_inode; - int mask, error = 0; - - lock_kernel(); - - /* - * Expand the file. Since inode_setattr() updates ->i_size - * before calling the ->truncate(), but FAT needs to fill the - * hole before it. - */ - if (attr->ia_valid & ATTR_SIZE) { - if (attr->ia_size > inode->i_size) { - error = fat_cont_expand(inode, attr->ia_size); - if (error || attr->ia_valid == ATTR_SIZE) - goto out; - attr->ia_valid &= ~ATTR_SIZE; - } - } - - error = inode_change_ok(inode, attr); - if (error) { - 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) && - (attr->ia_gid != sbi->options.fs_gid))) - error = -EPERM; - - if (error) { - if (sbi->options.quiet) - error = 0; - goto out; - } - - if (attr->ia_valid & ATTR_MODE) { - error = check_mode(sbi, attr->ia_mode); - if (error != 0 && !sbi->options.quiet) - goto out; - } - - error = inode_setattr(inode, attr); - if (error) - goto out; - - if (S_ISDIR(inode->i_mode)) - mask = sbi->options.fs_dmask; - else - mask = sbi->options.fs_fmask; - inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask); -out: - unlock_kernel(); - return error; -} - -EXPORT_SYMBOL_GPL(fat_notify_change); - /* Free all clusters after the skip'th cluster. */ static int fat_free(struct inode *inode, int skip) { @@ -353,8 +255,91 @@ int fat_getattr(struct vfsmount *mnt, st } EXPORT_SYMBOL_GPL(fat_getattr); +static int fat_check_mode(const struct msdos_sb_info *sbi, mode_t mode) +{ + mode_t mask, req = mode & ~S_IFMT; + + if (S_ISREG(mode)) + mask = sbi->options.fs_fmask; + else + mask = sbi->options.fs_dmask; + + /* + * Of the r and x bits, all (subject to umask) must be present. Of the + * w bits, either all (subject to umask) or none must be present. + */ + req &= ~mask; + if ((req & (S_IRUGO | S_IXUGO)) != ((S_IRUGO | S_IXUGO) & ~mask)) + return -EPERM; + if ((req & S_IWUGO) && ((req & S_IWUGO) != (S_IWUGO & ~mask))) + return -EPERM; + + return 0; +} + +int fat_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + int mask, error = 0; + + lock_kernel(); + + /* + * Expand the file. Since inode_setattr() updates ->i_size + * before calling the ->truncate(), but FAT needs to fill the + * hole before it. + */ + if (attr->ia_valid & ATTR_SIZE) { + if (attr->ia_size > inode->i_size) { + error = fat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + goto out; + attr->ia_valid &= ~ATTR_SIZE; + } + } + + error = inode_change_ok(inode, attr); + if (error) { + 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) && + (attr->ia_gid != sbi->options.fs_gid))) + error = -EPERM; + + if (error) { + if (sbi->options.quiet) + error = 0; + goto out; + } + + if (attr->ia_valid & ATTR_MODE) { + error = fat_check_mode(sbi, attr->ia_mode); + if (error != 0 && !sbi->options.quiet) + goto out; + } + + error = inode_setattr(inode, attr); + if (error) + goto out; + + if (S_ISDIR(inode->i_mode)) + mask = sbi->options.fs_dmask; + else + mask = sbi->options.fs_fmask; + inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask); +out: + unlock_kernel(); + return error; +} +EXPORT_SYMBOL_GPL(fat_setattr); + const struct inode_operations fat_file_inode_operations = { .truncate = fat_truncate, - .setattr = fat_notify_change, + .setattr = fat_setattr, .getattr = fat_getattr, }; diff -puN fs/msdos/namei.c~fat_notify_change-cleanup fs/msdos/namei.c --- linux-2.6/fs/msdos/namei.c~fat_notify_change-cleanup 2008-02-15 19:13:05.000000000 +0900 +++ linux-2.6-hirofumi/fs/msdos/namei.c 2008-02-15 19:13:09.000000000 +0900 @@ -653,7 +653,7 @@ static const struct inode_operations msd .mkdir = msdos_mkdir, .rmdir = msdos_rmdir, .rename = msdos_rename, - .setattr = fat_notify_change, + .setattr = fat_setattr, .getattr = fat_getattr, }; diff -puN fs/vfat/namei.c~fat_notify_change-cleanup fs/vfat/namei.c --- linux-2.6/fs/vfat/namei.c~fat_notify_change-cleanup 2008-02-15 19:13:18.000000000 +0900 +++ linux-2.6-hirofumi/fs/vfat/namei.c 2008-02-15 19:13:23.000000000 +0900 @@ -1003,7 +1003,7 @@ static const struct inode_operations vfa .mkdir = vfat_mkdir, .rmdir = vfat_rmdir, .rename = vfat_rename, - .setattr = fat_notify_change, + .setattr = fat_setattr, .getattr = fat_getattr, }; diff -puN include/linux/msdos_fs.h~fat_notify_change-cleanup include/linux/msdos_fs.h --- linux-2.6/include/linux/msdos_fs.h~fat_notify_change-cleanup 2008-02-15 19:13:32.000000000 +0900 +++ linux-2.6-hirofumi/include/linux/msdos_fs.h 2008-02-15 19:13:37.000000000 +0900 @@ -401,7 +401,7 @@ extern int fat_generic_ioctl(struct inod unsigned int cmd, unsigned long arg); extern const struct file_operations fat_file_operations; extern const struct inode_operations fat_file_inode_operations; -extern int fat_notify_change(struct dentry * dentry, struct iattr * attr); +extern int fat_setattr(struct dentry * dentry, struct iattr * attr); extern void fat_truncate(struct inode *inode); extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); _ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/9] fat: fat_setattr() fix 2008-04-20 18:13 ` [PATCH 2/9] fat: fat_notify_change() and check_mode() cleanup OGAWA Hirofumi @ 2008-04-20 18:13 ` OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 4/9] fat: Add allow_utime option OGAWA Hirofumi 0 siblings, 1 reply; 9+ messages in thread From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw) To: akpm; +Cc: linux-kernel, hirofumi Fix fat_setattr() on the case of showexec option. If user specified showexec option, inode->i_mode may not have S_IXUGO. This just use inode->i_mode to fix it. And with this patch, we don't allow chmod() on memory inode, it's just bad behaviour. IOW, we allow changing S_IWUGO only which can be stored to disk. Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> --- fs/fat/file.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff -puN fs/fat/file.c~fat_setattr-fix fs/fat/file.c --- linux-2.6/fs/fat/file.c~fat_setattr-fix 2008-02-20 15:28:06.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/file.c 2008-02-21 11:42:25.000000000 +0900 @@ -255,7 +255,8 @@ int fat_getattr(struct vfsmount *mnt, st } EXPORT_SYMBOL_GPL(fat_getattr); -static int fat_check_mode(const struct msdos_sb_info *sbi, mode_t mode) +static int fat_check_mode(const struct msdos_sb_info *sbi, struct inode *inode, + mode_t mode) { mode_t mask, req = mode & ~S_IFMT; @@ -269,7 +270,7 @@ static int fat_check_mode(const struct m * w bits, either all (subject to umask) or none must be present. */ req &= ~mask; - if ((req & (S_IRUGO | S_IXUGO)) != ((S_IRUGO | S_IXUGO) & ~mask)) + if ((req & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO))) return -EPERM; if ((req & S_IWUGO) && ((req & S_IWUGO) != (S_IWUGO & ~mask))) return -EPERM; @@ -308,7 +309,9 @@ int fat_setattr(struct dentry *dentry, s if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != sbi->options.fs_uid)) || ((attr->ia_valid & ATTR_GID) && - (attr->ia_gid != sbi->options.fs_gid))) + (attr->ia_gid != sbi->options.fs_gid)) || + ((attr->ia_valid & ATTR_MODE) && + fat_check_mode(sbi, inode, attr->ia_mode) < 0)) error = -EPERM; if (error) { @@ -317,12 +320,6 @@ int fat_setattr(struct dentry *dentry, s goto out; } - if (attr->ia_valid & ATTR_MODE) { - error = fat_check_mode(sbi, attr->ia_mode); - if (error != 0 && !sbi->options.quiet) - goto out; - } - error = inode_setattr(inode, attr); if (error) goto out; _ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 4/9] fat: Add allow_utime option 2008-04-20 18:13 ` [PATCH 3/9] fat: fat_setattr() fix OGAWA Hirofumi @ 2008-04-20 18:13 ` OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 5/9] fat: Update free_clusters even if it is untrusted OGAWA Hirofumi 0 siblings, 1 reply; 9+ messages in thread From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw) To: akpm; +Cc: linux-kernel, hirofumi Normally utime(2) checks current process is owner of the file, or it has CAP_FOWNER capability. But FAT filesystem doesn't have uid/gid as on disk info, so normal check is too unflexible. With this option you can relax it. Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> --- Documentation/filesystems/vfat.txt | 15 +++++++++++++++ fs/fat/file.c | 24 ++++++++++++++++++++++++ fs/fat/inode.c | 18 +++++++++++++++--- include/linux/msdos_fs.h | 1 + 4 files changed, 55 insertions(+), 3 deletions(-) diff -puN fs/fat/file.c~fat_allow_utime fs/fat/file.c --- linux-2.6/fs/fat/file.c~fat_allow_utime 2008-03-14 05:02:22.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/file.c 2008-03-14 05:36:19.000000000 +0900 @@ -278,11 +278,27 @@ static int fat_check_mode(const struct m return 0; } +static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode) +{ + mode_t allow_utime = sbi->options.allow_utime; + + if (current->fsuid != inode->i_uid) { + if (in_group_p(inode->i_gid)) + allow_utime >>= 3; + if (allow_utime & MAY_WRITE) + return 1; + } + + /* use a default check */ + return 0; +} + int fat_setattr(struct dentry *dentry, struct iattr *attr) { struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); struct inode *inode = dentry->d_inode; int mask, error = 0; + unsigned int ia_valid; lock_kernel(); @@ -300,7 +316,15 @@ int fat_setattr(struct dentry *dentry, s } } + /* Check for setting the inode time. */ + ia_valid = attr->ia_valid; + if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { + if (fat_allow_set_time(sbi, inode)) + attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET); + } + error = inode_change_ok(inode, attr); + attr->ia_valid = ia_valid; if (error) { if (sbi->options.quiet) error = 0; diff -puN fs/fat/inode.c~fat_allow_utime fs/fat/inode.c --- linux-2.6/fs/fat/inode.c~fat_allow_utime 2008-03-14 05:02:22.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/inode.c 2008-03-14 05:35:37.000000000 +0900 @@ -785,6 +785,8 @@ static int fat_show_options(struct seq_f seq_printf(m, ",gid=%u", opts->fs_gid); seq_printf(m, ",fmask=%04o", opts->fs_fmask); seq_printf(m, ",dmask=%04o", opts->fs_dmask); + if (opts->allow_utime) + seq_printf(m, ",allow_utime=%04o", opts->allow_utime); if (sbi->nls_disk) seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); if (isvfat) { @@ -840,9 +842,9 @@ static int fat_show_options(struct seq_f enum { Opt_check_n, Opt_check_r, Opt_check_s, Opt_uid, Opt_gid, - Opt_umask, Opt_dmask, Opt_fmask, Opt_codepage, Opt_usefree, Opt_nocase, - Opt_quiet, Opt_showexec, Opt_debug, Opt_immutable, - Opt_dots, Opt_nodots, + Opt_umask, Opt_dmask, Opt_fmask, Opt_allow_utime, Opt_codepage, + Opt_usefree, Opt_nocase, Opt_quiet, Opt_showexec, Opt_debug, + Opt_immutable, Opt_dots, Opt_nodots, 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, @@ -861,6 +863,7 @@ static match_table_t fat_tokens = { {Opt_umask, "umask=%o"}, {Opt_dmask, "dmask=%o"}, {Opt_fmask, "fmask=%o"}, + {Opt_allow_utime, "allow_utime=%o"}, {Opt_codepage, "codepage=%u"}, {Opt_usefree, "usefree"}, {Opt_nocase, "nocase"}, @@ -932,6 +935,7 @@ static int parse_options(char *options, opts->fs_uid = current->uid; opts->fs_gid = current->gid; opts->fs_fmask = opts->fs_dmask = current->fs->umask; + opts->allow_utime = -1; opts->codepage = fat_default_codepage; opts->iocharset = fat_default_iocharset; if (is_vfat) @@ -1019,6 +1023,11 @@ static int parse_options(char *options, return 0; opts->fs_fmask = option; break; + case Opt_allow_utime: + if (match_octal(&args[0], &option)) + return 0; + opts->allow_utime = option & (S_IWGRP | S_IWOTH); + break; case Opt_codepage: if (match_int(&args[0], &option)) return 0; @@ -1101,6 +1110,9 @@ static int parse_options(char *options, " for FAT filesystems, filesystem will be case sensitive!\n"); } + /* If user doesn't specify allow_utime, it's initialized from dmask. */ + if (opts->allow_utime == (unsigned short)-1) + opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); if (opts->unicode_xlate) opts->utf8 = 0; diff -puN include/linux/msdos_fs.h~fat_allow_utime include/linux/msdos_fs.h --- linux-2.6/include/linux/msdos_fs.h~fat_allow_utime 2008-03-14 05:02:22.000000000 +0900 +++ linux-2.6-hirofumi/include/linux/msdos_fs.h 2008-03-14 05:35:37.000000000 +0900 @@ -195,6 +195,7 @@ struct fat_mount_options { char *iocharset; /* Charset used for filename input/display */ unsigned short shortname; /* flags for shortname display/create rule */ unsigned char name_check; /* r = relaxed, n = normal, s = strict */ + unsigned short allow_utime;/* permission for setting the [am]time */ unsigned quiet:1, /* set = fake successful chmods and chowns */ showexec:1, /* set = only set x bit for com/exe/bat */ sys_immutable:1, /* set = system files are immutable */ diff -puN Documentation/filesystems/vfat.txt~fat_allow_utime Documentation/filesystems/vfat.txt --- linux-2.6/Documentation/filesystems/vfat.txt~fat_allow_utime 2008-03-14 05:02:22.000000000 +0900 +++ linux-2.6-hirofumi/Documentation/filesystems/vfat.txt 2008-03-14 05:02:22.000000000 +0900 @@ -17,6 +17,21 @@ dmask=### -- The permission mask for fmask=### -- The permission mask for files. The default is the umask of current process. +allow_utime=### -- This option controls the permission check of mtime/atime. + + 20 - If current process is in group of file's group ID, + you can change timestamp. + 2 - Other users can change timestamp. + + The default is set from `dmask' option. (If the directory is + writable, utime(2) is also allowed. I.e. ~dmask & 022) + + Normally utime(2) checks current process is owner of + the file, or it has CAP_FOWNER capability. But FAT + filesystem doesn't have uid/gid on disk, so normal + check is too unflexible. With this option you can + relax it. + codepage=### -- Sets the codepage number for converting to shortname characters on FAT filesystem. By default, FAT_DEFAULT_CODEPAGE setting is used. _ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 5/9] fat: Update free_clusters even if it is untrusted 2008-04-20 18:13 ` [PATCH 4/9] fat: Add allow_utime option OGAWA Hirofumi @ 2008-04-20 18:13 ` OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 6/9] fat: Remove fat_clusters_flush() OGAWA Hirofumi 0 siblings, 1 reply; 9+ messages in thread From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw) To: akpm; +Cc: linux-kernel, hirofumi Currently, free_clusters is not updated until it is trusted, because Windows doesn't update it correctly. But if user is using FAT driver of Linux, it updates free_clusters correctly. Instead, this updates it even if it's untrusted, so if free_clustes is correct, now keep correct value. Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> --- fs/fat/fatent.c | 7 +++++-- fs/fat/inode.c | 7 ++++--- include/linux/msdos_fs.h | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff -puN include/linux/msdos_fs.h~fat_fix-free_clusters-check include/linux/msdos_fs.h --- linux-2.6/include/linux/msdos_fs.h~fat_fix-free_clusters-check 2008-03-14 05:38:40.000000000 +0900 +++ linux-2.6-hirofumi/include/linux/msdos_fs.h 2008-03-14 05:38:40.000000000 +0900 @@ -233,6 +233,7 @@ struct msdos_sb_info { struct mutex fat_lock; unsigned int prev_free; /* previously allocated cluster number */ unsigned int free_clusters; /* -1 if undefined */ + unsigned int free_clus_valid; /* is free_clusters valid? */ struct fat_mount_options options; struct nls_table *nls_disk; /* Codepage used on disk */ struct nls_table *nls_io; /* Charset used for input and display */ diff -puN fs/fat/inode.c~fat_fix-free_clusters-check fs/fat/inode.c --- linux-2.6/fs/fat/inode.c~fat_fix-free_clusters-check 2008-03-14 05:38:40.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/inode.c 2008-03-14 05:38:40.000000000 +0900 @@ -537,7 +537,7 @@ static int fat_statfs(struct dentry *den struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); /* If the count of free cluster is still unknown, counts it here. */ - if (sbi->free_clusters == -1) { + if (sbi->free_clusters == -1 || !sbi->free_clus_valid) { int err = fat_count_free_clusters(dentry->d_sb); if (err) return err; @@ -1274,6 +1274,7 @@ int fat_fill_super(struct super_block *s sbi->fat_length = le16_to_cpu(b->fat_length); sbi->root_cluster = 0; sbi->free_clusters = -1; /* Don't know yet */ + sbi->free_clus_valid = 0; sbi->prev_free = FAT_START_ENT; if (!sbi->fat_length && b->fat32_length) { @@ -1309,8 +1310,8 @@ int fat_fill_super(struct super_block *s sbi->fsinfo_sector); } else { if (sbi->options.usefree) - sbi->free_clusters = - le32_to_cpu(fsinfo->free_clusters); + sbi->free_clus_valid = 1; + sbi->free_clusters = le32_to_cpu(fsinfo->free_clusters); sbi->prev_free = le32_to_cpu(fsinfo->next_cluster); } diff -puN fs/fat/fatent.c~fat_fix-free_clusters-check fs/fat/fatent.c --- linux-2.6/fs/fat/fatent.c~fat_fix-free_clusters-check 2008-03-14 05:38:40.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/fatent.c 2008-03-14 05:38:40.000000000 +0900 @@ -450,7 +450,8 @@ int fat_alloc_clusters(struct inode *ino BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2)); /* fixed limit */ lock_fat(sbi); - if (sbi->free_clusters != -1 && sbi->free_clusters < nr_cluster) { + if (sbi->free_clusters != -1 && sbi->free_clus_valid && + sbi->free_clusters < nr_cluster) { unlock_fat(sbi); return -ENOSPC; } @@ -504,6 +505,7 @@ int fat_alloc_clusters(struct inode *ino /* Couldn't allocate the free entries */ sbi->free_clusters = 0; + sbi->free_clus_valid = 1; sb->s_dirt = 1; err = -ENOSPC; @@ -615,7 +617,7 @@ int fat_count_free_clusters(struct super int err = 0, free; lock_fat(sbi); - if (sbi->free_clusters != -1) + if (sbi->free_clusters != -1 && sbi->free_clus_valid) goto out; reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits; @@ -643,6 +645,7 @@ int fat_count_free_clusters(struct super } while (fat_ent_next(sbi, &fatent)); } sbi->free_clusters = free; + sbi->free_clus_valid = 1; sb->s_dirt = 1; fatent_brelse(&fatent); out: _ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 6/9] fat: Remove fat_clusters_flush() 2008-04-20 18:13 ` [PATCH 5/9] fat: Update free_clusters even if it is untrusted OGAWA Hirofumi @ 2008-04-20 18:13 ` OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 7/9] Add balance_dirty_pages_ratelimited() to cont_expand_zero() OGAWA Hirofumi 0 siblings, 1 reply; 9+ messages in thread From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw) To: akpm; +Cc: linux-kernel, hirofumi This removes unneeded fat_clusters_flush(). Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> --- fs/fat/fatent.c | 2 -- 1 file changed, 2 deletions(-) diff -puN fs/fat/fatent.c~fat-remove-fat_clusters_flush fs/fat/fatent.c --- linux-2.6/fs/fat/fatent.c~fat-remove-fat_clusters_flush 2008-04-19 11:55:07.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/fatent.c 2008-04-19 11:55:07.000000000 +0900 @@ -585,8 +585,6 @@ error: brelse(bhs[i]); unlock_fat(sbi); - fat_clusters_flush(sb); - return err; } _ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 7/9] Add balance_dirty_pages_ratelimited() to cont_expand_zero() 2008-04-20 18:13 ` [PATCH 6/9] fat: Remove fat_clusters_flush() OGAWA Hirofumi @ 2008-04-20 18:13 ` OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 8/9] vfat: bug fix for vfat cannot handle filename with 255 OGAWA Hirofumi 0 siblings, 1 reply; 9+ messages in thread From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw) To: akpm; +Cc: linux-kernel, hirofumi On the systems, ftruncate() which expand size for FAT became the cause of OOM. The cont_expand_zero() filled all memory with dirty pages, and since disk is very slow, limit of page scanning was exceeded, then it triggered OOM. This adds balance_dirty_pages_ratelimited() to avoid filling memory with dirty pages. Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> --- fs/buffer.c | 2 ++ 1 file changed, 2 insertions(+) diff -puN fs/buffer.c~cont_expand_zero-fix fs/buffer.c --- linux-2.6/fs/buffer.c~cont_expand_zero-fix 2008-04-05 18:22:57.000000000 +0900 +++ linux-2.6-hirofumi/fs/buffer.c 2008-04-05 18:22:57.000000000 +0900 @@ -2251,6 +2251,8 @@ int cont_expand_zero(struct file *file, goto out; BUG_ON(err != len); err = 0; + + balance_dirty_pages_ratelimited(mapping); } /* page covers the boundary, find the boundary offset */ _ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 8/9] vfat: bug fix for vfat cannot handle filename with 255 2008-04-20 18:13 ` [PATCH 7/9] Add balance_dirty_pages_ratelimited() to cont_expand_zero() OGAWA Hirofumi @ 2008-04-20 18:13 ` OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 9/9] fat: use __getname() OGAWA Hirofumi 0 siblings, 1 reply; 9+ messages in thread From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw) To: akpm; +Cc: linux-kernel, hirofumi, ek9852 From: Keith Mok <ek9852@gmail.com> This patch fix the problem that the buffer allocated for convert of unicode to utf8 in fat/dir.c is too small. And cannot handle filename with 255 asian characters when mounted with utf8 options. Also it fix the filename length limitation checking in vfat/namei.c that the filename length should be checked against the number of converted unicode characters. Not the length before NLS/UTF8 converted. Signed-off-by: Keith Mok <ek9852@gmail.com> Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> --- fs/fat/dir.c | 44 ++++++++++++++++++++++++++++---------------- fs/vfat/namei.c | 25 +++++++++++-------------- 2 files changed, 39 insertions(+), 30 deletions(-) diff -puN fs/fat/dir.c~fat_vfat-fix-filename-handing fs/fat/dir.c --- linux-2.6/fs/fat/dir.c~fat_vfat-fix-filename-handing 2008-04-19 11:55:08.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/dir.c 2008-04-21 00:59:00.000000000 +0900 @@ -124,8 +124,8 @@ static inline int fat_get_entry(struct i * but ignore that right now. * Ahem... Stack smashing in ring 0 isn't fun. Fixed. */ -static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate, - struct nls_table *nls) +static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int len, + int uni_xlate, struct nls_table *nls) { wchar_t *ip, ec; unsigned char *op, nc; @@ -135,10 +135,11 @@ static int uni16_to_x8(unsigned char *as ip = uni; op = ascii; - while (*ip) { + while (*ip && ((len - NLS_MAX_CHARSET_SIZE) > 0)) { ec = *ip++; if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) { op += charlen; + len -= charlen; } else { if (uni_xlate == 1) { *op = ':'; @@ -149,16 +150,19 @@ static int uni16_to_x8(unsigned char *as ec >>= 4; } op += 5; + len -= 5; } else { *op++ = '?'; + len--; } } - /* We have some slack there, so it's OK */ - if (op>ascii+256) { - op = ascii + 256; - break; - } } + + if (unlikely(*ip)) { + printk(KERN_WARNING "FAT: filename was truncated while " + "converting."); + } + *op = 0; return (op - ascii); } @@ -311,9 +315,11 @@ int fat_search_long(struct inode *inode, struct nls_table *nls_io = sbi->nls_io; struct nls_table *nls_disk = sbi->nls_disk; wchar_t bufuname[14]; - unsigned char xlate_len, nr_slots; + unsigned char nr_slots; + int xlate_len; wchar_t *unicode = NULL; - unsigned char work[MSDOS_NAME], bufname[260]; /* 256 + 4 */ + unsigned char work[MSDOS_NAME]; + unsigned char *bufname = NULL; int uni_xlate = sbi->options.unicode_xlate; int utf8 = sbi->options.utf8; int anycase = (sbi->options.name_check != 's'); @@ -321,6 +327,10 @@ int fat_search_long(struct inode *inode, loff_t cpos = 0; int chl, i, j, last_u, err; + bufname = (unsigned char*)__get_free_page(GFP_KERNEL); + if (!bufname) + return -ENOMEM; + err = -ENOENT; while(1) { if (fat_get_entry(inode, &cpos, &bh, &de) == -1) @@ -386,8 +396,8 @@ parse_record: bufuname[last_u] = 0x0000; xlate_len = utf8 - ?utf8_wcstombs(bufname, bufuname, sizeof(bufname)) - :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io); + ?utf8_wcstombs(bufname, bufuname, PAGE_SIZE) + :uni16_to_x8(bufname, bufuname, PAGE_SIZE, uni_xlate, nls_io); if (xlate_len == name_len) if ((!anycase && !memcmp(name, bufname, xlate_len)) || (anycase && !nls_strnicmp(nls_io, name, bufname, @@ -396,8 +406,8 @@ parse_record: if (nr_slots) { xlate_len = utf8 - ?utf8_wcstombs(bufname, unicode, sizeof(bufname)) - :uni16_to_x8(bufname, unicode, uni_xlate, nls_io); + ?utf8_wcstombs(bufname, unicode, PAGE_SIZE) + :uni16_to_x8(bufname, unicode, PAGE_SIZE, uni_xlate, nls_io); if (xlate_len != name_len) continue; if ((!anycase && !memcmp(name, bufname, xlate_len)) || @@ -416,6 +426,8 @@ Found: sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); err = 0; EODir: + if (bufname) + free_page((unsigned long)bufname); if (unicode) free_page((unsigned long)unicode); @@ -598,7 +610,7 @@ parse_record: if (isvfat) { bufuname[j] = 0x0000; i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname)) - : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io); + : uni16_to_x8(bufname, bufuname, sizeof(bufname), uni_xlate, nls_io); } fill_name = bufname; @@ -610,7 +622,7 @@ parse_record: int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0])); int long_len = utf8 ? utf8_wcstombs(longname, unicode, buf_size) - : uni16_to_x8(longname, unicode, uni_xlate, nls_io); + : uni16_to_x8(longname, unicode, buf_size, uni_xlate, nls_io); if (!both) { fill_name = longname; diff -puN fs/vfat/namei.c~fat_vfat-fix-filename-handing fs/vfat/namei.c --- linux-2.6/fs/vfat/namei.c~fat_vfat-fix-filename-handing 2008-04-19 11:55:08.000000000 +0900 +++ linux-2.6-hirofumi/fs/vfat/namei.c 2008-04-21 00:57:11.000000000 +0900 @@ -176,15 +176,10 @@ static inline int vfat_is_used_badchars( for (i = 0; i < len; i++) if (vfat_bad_char(s[i])) return -EINVAL; - return 0; -} -static int vfat_valid_longname(const unsigned char *name, unsigned int len) -{ - if (name[len - 1] == ' ') + if (s[i - 1] == ' ') /* last character cannot be space */ return -EINVAL; - if (len >= 256) - return -ENAMETOOLONG; + return 0; } @@ -485,11 +480,14 @@ xlate_to_uni(const unsigned char *name, */ *outlen -= (name_len - len); + if (*outlen > 255) + return -ENAMETOOLONG; + op = &outname[*outlen * sizeof(wchar_t)]; } else { if (nls) { for (i = 0, ip = name, op = outname, *outlen = 0; - i < len && *outlen <= 260; + i < len && *outlen <= 255; *outlen += 1) { if (escape && (*ip == ':')) { @@ -525,18 +523,20 @@ xlate_to_uni(const unsigned char *name, op += 2; } } + if (i < len) + return -ENAMETOOLONG; } else { for (i = 0, ip = name, op = outname, *outlen = 0; - i < len && *outlen <= 260; + i < len && *outlen <= 255; i++, *outlen += 1) { *op++ = *ip++; *op++ = 0; } + if (i < len) + return -ENAMETOOLONG; } } - if (*outlen > 260) - return -ENAMETOOLONG; *longlen = *outlen; if (*outlen % 13) { @@ -574,9 +574,6 @@ static int vfat_build_slots(struct inode loff_t offset; *nr_slots = 0; - err = vfat_valid_longname(name, len); - if (err) - return err; page = __get_free_page(GFP_KERNEL); if (!page) _ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 9/9] fat: use __getname() 2008-04-20 18:13 ` [PATCH 8/9] vfat: bug fix for vfat cannot handle filename with 255 OGAWA Hirofumi @ 2008-04-20 18:13 ` OGAWA Hirofumi 0 siblings, 0 replies; 9+ messages in thread From: OGAWA Hirofumi @ 2008-04-20 18:13 UTC (permalink / raw) To: akpm; +Cc: linux-kernel, hirofumi __getname() is faster than __get_free_page(). Use it. Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> --- fs/fat/dir.c | 20 ++++++++++---------- fs/vfat/namei.c | 10 ++++------ 2 files changed, 14 insertions(+), 16 deletions(-) diff -puN fs/vfat/namei.c~fat_use-__getname fs/vfat/namei.c --- linux-2.6/fs/vfat/namei.c~fat_use-__getname 2008-04-21 01:36:07.000000000 +0900 +++ linux-2.6-hirofumi/fs/vfat/namei.c 2008-04-21 01:36:07.000000000 +0900 @@ -472,7 +472,7 @@ xlate_to_uni(const unsigned char *name, if (utf8) { int name_len = strlen(name); - *outlen = utf8_mbstowcs((wchar_t *)outname, name, PAGE_SIZE); + *outlen = utf8_mbstowcs((wchar_t *)outname, name, PATH_MAX); /* * We stripped '.'s before and set len appropriately, @@ -565,7 +565,6 @@ static int vfat_build_slots(struct inode struct fat_mount_options *opts = &sbi->options; struct msdos_dir_slot *ps; struct msdos_dir_entry *de; - unsigned long page; unsigned char cksum, lcase; unsigned char msdos_name[MSDOS_NAME]; wchar_t *uname; @@ -575,11 +574,10 @@ static int vfat_build_slots(struct inode *nr_slots = 0; - page = __get_free_page(GFP_KERNEL); - if (!page) + uname = __getname(); + if (!uname) return -ENOMEM; - uname = (wchar_t *)page; err = xlate_to_uni(name, len, (unsigned char *)uname, &ulen, &usize, opts->unicode_xlate, opts->utf8, sbi->nls_io); if (err) @@ -631,7 +629,7 @@ shortname: de->starthi = cpu_to_le16(cluster >> 16); de->size = 0; out_free: - free_page(page); + __putname(uname); return err; } diff -puN fs/fat/dir.c~fat_use-__getname fs/fat/dir.c --- linux-2.6/fs/fat/dir.c~fat_use-__getname 2008-04-21 01:36:07.000000000 +0900 +++ linux-2.6-hirofumi/fs/fat/dir.c 2008-04-21 01:36:07.000000000 +0900 @@ -247,7 +247,7 @@ static int fat_parse_long(struct inode * unsigned char id, slot, slots, alias_checksum; if (!*unicode) { - *unicode = (wchar_t *)__get_free_page(GFP_KERNEL); + *unicode = __getname(); if (!*unicode) { brelse(*bh); return -ENOMEM; @@ -327,7 +327,7 @@ int fat_search_long(struct inode *inode, loff_t cpos = 0; int chl, i, j, last_u, err; - bufname = (unsigned char*)__get_free_page(GFP_KERNEL); + bufname = __getname(); if (!bufname) return -ENOMEM; @@ -396,8 +396,8 @@ parse_record: bufuname[last_u] = 0x0000; xlate_len = utf8 - ?utf8_wcstombs(bufname, bufuname, PAGE_SIZE) - :uni16_to_x8(bufname, bufuname, PAGE_SIZE, uni_xlate, nls_io); + ?utf8_wcstombs(bufname, bufuname, PATH_MAX) + :uni16_to_x8(bufname, bufuname, PATH_MAX, uni_xlate, nls_io); if (xlate_len == name_len) if ((!anycase && !memcmp(name, bufname, xlate_len)) || (anycase && !nls_strnicmp(nls_io, name, bufname, @@ -406,8 +406,8 @@ parse_record: if (nr_slots) { xlate_len = utf8 - ?utf8_wcstombs(bufname, unicode, PAGE_SIZE) - :uni16_to_x8(bufname, unicode, PAGE_SIZE, uni_xlate, nls_io); + ?utf8_wcstombs(bufname, unicode, PATH_MAX) + :uni16_to_x8(bufname, unicode, PATH_MAX, uni_xlate, nls_io); if (xlate_len != name_len) continue; if ((!anycase && !memcmp(name, bufname, xlate_len)) || @@ -427,9 +427,9 @@ Found: err = 0; EODir: if (bufname) - free_page((unsigned long)bufname); + __putname(bufname); if (unicode) - free_page((unsigned long)unicode); + __putname(unicode); return err; } @@ -619,7 +619,7 @@ parse_record: /* convert the unicode long name. 261 is maximum size * of unicode buffer. (13 * slots + nul) */ void *longname = unicode + 261; - int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0])); + int buf_size = PATH_MAX - (261 * sizeof(unicode[0])); int long_len = utf8 ? utf8_wcstombs(longname, unicode, buf_size) : uni16_to_x8(longname, unicode, buf_size, uni_xlate, nls_io); @@ -652,7 +652,7 @@ EODir: FillFailed: brelse(bh); if (unicode) - free_page((unsigned long)unicode); + __putname(unicode); out: unlock_kernel(); return ret; _ ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-04-20 18:29 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-04-20 18:13 [PATCH 1/9] fat: kill is_bad_inode() check OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 2/9] fat: fat_notify_change() and check_mode() cleanup OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 3/9] fat: fat_setattr() fix OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 4/9] fat: Add allow_utime option OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 5/9] fat: Update free_clusters even if it is untrusted OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 6/9] fat: Remove fat_clusters_flush() OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 7/9] Add balance_dirty_pages_ratelimited() to cont_expand_zero() OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 8/9] vfat: bug fix for vfat cannot handle filename with 255 OGAWA Hirofumi 2008-04-20 18:13 ` [PATCH 9/9] fat: use __getname() OGAWA Hirofumi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®