diff options
Diffstat (limited to 'fs/isofs/inode.c')
| -rw-r--r-- | fs/isofs/inode.c | 277 |
1 files changed, 164 insertions, 113 deletions
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 6b4dcd4f294..4556ce1af5b 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -17,26 +17,35 @@ #include <linux/slab.h> #include <linux/nls.h> #include <linux/ctype.h> -#include <linux/smp_lock.h> #include <linux/statfs.h> #include <linux/cdrom.h> #include <linux/parser.h> +#include <linux/mpage.h> +#include <linux/user_namespace.h> #include "isofs.h" #include "zisofs.h" #define BEQUIET -static int isofs_hashi(struct dentry *parent, struct qstr *qstr); -static int isofs_hash(struct dentry *parent, struct qstr *qstr); -static int isofs_dentry_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b); -static int isofs_dentry_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b); +static int isofs_hashi(const struct dentry *parent, struct qstr *qstr); +static int isofs_hash(const struct dentry *parent, struct qstr *qstr); +static int isofs_dentry_cmpi(const struct dentry *parent, + const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); +static int isofs_dentry_cmp(const struct dentry *parent, + const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); #ifdef CONFIG_JOLIET -static int isofs_hashi_ms(struct dentry *parent, struct qstr *qstr); -static int isofs_hash_ms(struct dentry *parent, struct qstr *qstr); -static int isofs_dentry_cmpi_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); -static int isofs_dentry_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); +static int isofs_hashi_ms(const struct dentry *parent, struct qstr *qstr); +static int isofs_hash_ms(const struct dentry *parent, struct qstr *qstr); +static int isofs_dentry_cmpi_ms(const struct dentry *parent, + const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); +static int isofs_dentry_cmp_ms(const struct dentry *parent, + const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); #endif static void isofs_put_super(struct super_block *sb) @@ -44,11 +53,7 @@ static void isofs_put_super(struct super_block *sb) struct isofs_sb_info *sbi = ISOFS_SB(sb); #ifdef CONFIG_JOLIET - lock_kernel(); - unload_nls(sbi->s_nls_iocharset); - - unlock_kernel(); #endif kfree(sbi); @@ -70,11 +75,17 @@ static struct inode *isofs_alloc_inode(struct super_block *sb) return &ei->vfs_inode; } -static void isofs_destroy_inode(struct inode *inode) +static void isofs_i_callback(struct rcu_head *head) { + struct inode *inode = container_of(head, struct inode, i_rcu); kmem_cache_free(isofs_inode_cachep, ISOFS_I(inode)); } +static void isofs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, isofs_i_callback); +} + static void init_once(void *foo) { struct iso_inode_info *ei = foo; @@ -82,7 +93,7 @@ static void init_once(void *foo) inode_init_once(&ei->vfs_inode); } -static int init_inodecache(void) +static int __init init_inodecache(void) { isofs_inode_cachep = kmem_cache_create("isofs_inode_cache", sizeof(struct iso_inode_info), @@ -96,13 +107,19 @@ static int init_inodecache(void) static void destroy_inodecache(void) { + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); kmem_cache_destroy(isofs_inode_cachep); } static int isofs_remount(struct super_block *sb, int *flags, char *data) { - /* we probably want a lot more here */ - *flags |= MS_RDONLY; + sync_filesystem(sb); + if (!(*flags & MS_RDONLY)) + return -EROFS; return 0; } @@ -151,10 +168,10 @@ struct iso9660_options{ unsigned char map; unsigned char check; unsigned int blocksize; - mode_t fmode; - mode_t dmode; - gid_t gid; - uid_t uid; + umode_t fmode; + umode_t dmode; + kgid_t gid; + kuid_t uid; char *iocharset; /* LVE */ s32 session; @@ -165,7 +182,7 @@ struct iso9660_options{ * Compute the hash for the isofs name corresponding to the dentry. */ static int -isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms) +isofs_hash_common(struct qstr *qstr, int ms) { const char *name; int len; @@ -186,7 +203,7 @@ isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms) * Compute the hash for the isofs name corresponding to the dentry. */ static int -isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms) +isofs_hashi_common(struct qstr *qstr, int ms) { const char *name; int len; @@ -211,100 +228,86 @@ isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms) } /* - * Case insensitive compare of two isofs names. + * Compare of two isofs names. */ -static int isofs_dentry_cmpi_common(struct dentry *dentry, struct qstr *a, - struct qstr *b, int ms) +static int isofs_dentry_cmp_common( + unsigned int len, const char *str, + const struct qstr *name, int ms, int ci) { int alen, blen; /* A filename cannot end in '.' or we treat it like it has none */ - alen = a->len; - blen = b->len; + alen = name->len; + blen = len; if (ms) { - while (alen && a->name[alen-1] == '.') + while (alen && name->name[alen-1] == '.') alen--; - while (blen && b->name[blen-1] == '.') + while (blen && str[blen-1] == '.') blen--; } if (alen == blen) { - if (strnicmp(a->name, b->name, alen) == 0) - return 0; - } - return 1; -} - -/* - * Case sensitive compare of two isofs names. - */ -static int isofs_dentry_cmp_common(struct dentry *dentry, struct qstr *a, - struct qstr *b, int ms) -{ - int alen, blen; - - /* A filename cannot end in '.' or we treat it like it has none */ - alen = a->len; - blen = b->len; - if (ms) { - while (alen && a->name[alen-1] == '.') - alen--; - while (blen && b->name[blen-1] == '.') - blen--; - } - if (alen == blen) { - if (strncmp(a->name, b->name, alen) == 0) - return 0; + if (ci) { + if (strnicmp(name->name, str, alen) == 0) + return 0; + } else { + if (strncmp(name->name, str, alen) == 0) + return 0; + } } return 1; } static int -isofs_hash(struct dentry *dentry, struct qstr *qstr) +isofs_hash(const struct dentry *dentry, struct qstr *qstr) { - return isofs_hash_common(dentry, qstr, 0); + return isofs_hash_common(qstr, 0); } static int -isofs_hashi(struct dentry *dentry, struct qstr *qstr) +isofs_hashi(const struct dentry *dentry, struct qstr *qstr) { - return isofs_hashi_common(dentry, qstr, 0); + return isofs_hashi_common(qstr, 0); } static int -isofs_dentry_cmp(struct dentry *dentry,struct qstr *a,struct qstr *b) +isofs_dentry_cmp(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { - return isofs_dentry_cmp_common(dentry, a, b, 0); + return isofs_dentry_cmp_common(len, str, name, 0, 0); } static int -isofs_dentry_cmpi(struct dentry *dentry,struct qstr *a,struct qstr *b) +isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { - return isofs_dentry_cmpi_common(dentry, a, b, 0); + return isofs_dentry_cmp_common(len, str, name, 0, 1); } #ifdef CONFIG_JOLIET static int -isofs_hash_ms(struct dentry *dentry, struct qstr *qstr) +isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr) { - return isofs_hash_common(dentry, qstr, 1); + return isofs_hash_common(qstr, 1); } static int -isofs_hashi_ms(struct dentry *dentry, struct qstr *qstr) +isofs_hashi_ms(const struct dentry *dentry, struct qstr *qstr) { - return isofs_hashi_common(dentry, qstr, 1); + return isofs_hashi_common(qstr, 1); } static int -isofs_dentry_cmp_ms(struct dentry *dentry,struct qstr *a,struct qstr *b) +isofs_dentry_cmp_ms(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { - return isofs_dentry_cmp_common(dentry, a, b, 1); + return isofs_dentry_cmp_common(len, str, name, 1, 0); } static int -isofs_dentry_cmpi_ms(struct dentry *dentry,struct qstr *a,struct qstr *b) +isofs_dentry_cmpi_ms(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { - return isofs_dentry_cmpi_common(dentry, a, b, 1); + return isofs_dentry_cmp_common(len, str, name, 1, 1); } #endif @@ -371,8 +374,8 @@ static int parse_options(char *options, struct iso9660_options *popt) popt->fmode = popt->dmode = ISOFS_INVALID_MODE; popt->uid_set = 0; popt->gid_set = 0; - popt->gid = 0; - popt->uid = 0; + popt->gid = GLOBAL_ROOT_GID; + popt->uid = GLOBAL_ROOT_UID; popt->iocharset = NULL; popt->utf8 = 0; popt->overriderockperm = 0; @@ -448,13 +451,17 @@ static int parse_options(char *options, struct iso9660_options *popt) case Opt_uid: if (match_int(&args[0], &option)) return 0; - popt->uid = option; + popt->uid = make_kuid(current_user_ns(), option); + if (!uid_valid(popt->uid)) + return 0; popt->uid_set = 1; break; case Opt_gid: if (match_int(&args[0], &option)) return 0; - popt->gid = option; + popt->gid = make_kgid(current_user_ns(), option); + if (!gid_valid(popt->gid)) + return 0; popt->gid_set = 1; break; case Opt_mode: @@ -549,6 +556,34 @@ static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) } /* + * Check if root directory is empty (has less than 3 files). + * + * Used to detect broken CDs where ISO root directory is empty but Joliet root + * directory is OK. If such CD has Rock Ridge extensions, they will be disabled + * (and Joliet used instead) or else no files would be visible. + */ +static bool rootdir_empty(struct super_block *sb, unsigned long block) +{ + int offset = 0, files = 0, de_len; + struct iso_directory_record *de; + struct buffer_head *bh; + + bh = sb_bread(sb, block); + if (!bh) + return true; + while (files < 3) { + de = (struct iso_directory_record *) (bh->b_data + offset); + de_len = *(unsigned char *) de; + if (de_len == 0) + break; + files++; + offset += de_len; + } + brelse(bh); + return files < 3; +} + +/* * Initialize the superblock and read the root inode. * * Note: a check_disk_change() has been done immediately prior @@ -722,16 +757,12 @@ root_found: } s->s_magic = ISOFS_SUPER_MAGIC; - s->s_maxbytes = 0xffffffff; /* We can handle files up to 4 GB */ /* - * The CDROM is read-only, has no nodes (devices) on it, and since - * all of the files appear to be owned by root, we really do not want - * to allow suid. (suid or devices will not show up unless we have - * Rock Ridge extensions) + * With multi-extent files, file size is only limited by the maximum + * size of a file system, which is 8 TB. */ - - s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */; + s->s_maxbytes = 0x80000000000LL; /* Set this for reference. Its not currently used except on write which we don't have .. */ @@ -842,6 +873,18 @@ root_found: goto out_no_root; /* + * Fix for broken CDs with Rock Ridge and empty ISO root directory but + * correct Joliet root directory. + */ + if (sbi->s_rock == 1 && joliet_level && + rootdir_empty(s, sbi->s_firstdatazone)) { + printk(KERN_NOTICE + "ISOFS: primary root directory is empty. " + "Disabling Rock Ridge and switching to Joliet."); + sbi->s_rock = 0; + } + + /* * If this disk has both Rock Ridge and Joliet on it, then we * want to use Rock Ridge by default. This can be overridden * by using the norock mount option. There is still one other @@ -881,17 +924,20 @@ root_found: goto out_iput; } - /* get the root dentry */ - s->s_root = d_alloc_root(inode); - if (!(s->s_root)) - goto out_no_root; - table = 0; if (joliet_level) table += 2; if (opt.check == 'r') table++; - s->s_root->d_op = &isofs_dentry_ops[table]; + + s->s_d_op = &isofs_dentry_ops[table]; + + /* get the root dentry */ + s->s_root = d_make_root(inode); + if (!(s->s_root)) { + error = -ENOMEM; + goto out_no_inode; + } kfree(opt.iocharset); @@ -915,7 +961,7 @@ out_no_inode: out_no_read: printk(KERN_WARNING "%s: bread failed, dev=%s, iso_blknum=%d, block=%d\n", __func__, s->s_id, iso_blknum, block); - goto out_freesbi; + goto out_freebh; out_bad_zone_size: printk(KERN_WARNING "ISOFS: Bad logical zone size %ld\n", sbi->s_log_zone_size); @@ -930,6 +976,7 @@ out_unknown_format: out_freebh: brelse(bh); + brelse(pri_bh); out_freesbi: kfree(opt.iocharset); kfree(sbi); @@ -961,27 +1008,23 @@ static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) * or getblk() if they are not. Returns the number of blocks inserted * (-ve == error.) */ -int isofs_get_blocks(struct inode *inode, sector_t iblock_s, +int isofs_get_blocks(struct inode *inode, sector_t iblock, struct buffer_head **bh, unsigned long nblocks) { - unsigned long b_off; + unsigned long b_off = iblock; unsigned offset, sect_size; unsigned int firstext; unsigned long nextblk, nextoff; - long iblock = (long)iblock_s; int section, rv, error; struct iso_inode_info *ei = ISOFS_I(inode); - lock_kernel(); - error = -EIO; rv = 0; - if (iblock < 0 || iblock != iblock_s) { + if (iblock != b_off) { printk(KERN_DEBUG "%s: block number too large\n", __func__); goto abort; } - b_off = iblock; offset = 0; firstext = ei->i_first_extent; @@ -999,8 +1042,9 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, * I/O errors. */ if (b_off > ((inode->i_size + PAGE_CACHE_SIZE - 1) >> ISOFS_BUFFER_BITS(inode))) { - printk(KERN_DEBUG "%s: block >= EOF (%ld, %ld)\n", - __func__, iblock, (unsigned long) inode->i_size); + printk(KERN_DEBUG "%s: block >= EOF (%lu, %llu)\n", + __func__, b_off, + (unsigned long long)inode->i_size); goto abort; } @@ -1026,9 +1070,9 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, if (++section > 100) { printk(KERN_DEBUG "%s: More than 100 file sections ?!?" " aborting...\n", __func__); - printk(KERN_DEBUG "%s: block=%ld firstext=%u sect_size=%u " + printk(KERN_DEBUG "%s: block=%lu firstext=%u sect_size=%u " "nextblk=%lu nextoff=%lu\n", __func__, - iblock, firstext, (unsigned) sect_size, + b_off, firstext, (unsigned) sect_size, nextblk, nextoff); goto abort; } @@ -1049,7 +1093,6 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, error = 0; abort: - unlock_kernel(); return rv != 0 ? rv : error; } @@ -1093,7 +1136,13 @@ struct buffer_head *isofs_bread(struct inode *inode, sector_t block) static int isofs_readpage(struct file *file, struct page *page) { - return block_read_full_page(page,isofs_get_block); + return mpage_readpage(page, isofs_get_block); +} + +static int isofs_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + return mpage_readpages(mapping, pages, nr_pages, isofs_get_block); } static sector_t _isofs_bmap(struct address_space *mapping, sector_t block) @@ -1103,7 +1152,7 @@ static sector_t _isofs_bmap(struct address_space *mapping, sector_t block) static const struct address_space_operations isofs_aops = { .readpage = isofs_readpage, - .sync_page = block_sync_page, + .readpages = isofs_readpages, .bmap = _isofs_bmap }; @@ -1265,7 +1314,7 @@ static int isofs_read_inode(struct inode *inode) inode->i_mode = S_IFDIR | sbi->s_dmode; else inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; - inode->i_nlink = 1; /* + set_nlink(inode, 1); /* * Set to 1. We know there are 2, but * the find utility tries to optimize * if it is 2, and it screws up. It is @@ -1283,7 +1332,7 @@ static int isofs_read_inode(struct inode *inode) */ inode->i_mode = S_IFREG | S_IRUGO | S_IXUGO; } - inode->i_nlink = 1; + set_nlink(inode, 1); } inode->i_uid = sbi->s_uid; inode->i_gid = sbi->s_gid; @@ -1470,20 +1519,24 @@ struct inode *isofs_iget(struct super_block *sb, return inode; } -static int isofs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) +static struct dentry *isofs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) { - return get_sb_bdev(fs_type, flags, dev_name, data, isofs_fill_super, - mnt); + /* We don't support read-write mounts */ + if (!(flags & MS_RDONLY)) + return ERR_PTR(-EACCES); + return mount_bdev(fs_type, flags, dev_name, data, isofs_fill_super); } static struct file_system_type iso9660_fs_type = { .owner = THIS_MODULE, .name = "iso9660", - .get_sb = isofs_get_sb, + .mount = isofs_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; +MODULE_ALIAS_FS("iso9660"); +MODULE_ALIAS("iso9660"); static int __init init_iso9660_fs(void) { @@ -1521,5 +1574,3 @@ static void __exit exit_iso9660_fs(void) module_init(init_iso9660_fs) module_exit(exit_iso9660_fs) MODULE_LICENSE("GPL"); -/* Actual filesystem name is iso9660, as requested in filesystems.c */ -MODULE_ALIAS("iso9660"); |
