cregit-Linux how code gets into the kernel

Release 4.10 fs/isofs/inode.c

Directory: fs/isofs
/*
 *  linux/fs/isofs/inode.c
 *
 *  (C) 1991  Linus Torvalds - minix filesystem
 *      1992, 1993, 1994  Eric Youngdale Modified for ISO 9660 filesystem.
 *      1994  Eberhard Mönkeberg - multi session handling.
 *      1995  Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs.
 *      1997  Gordon Chaffee - Joliet CDs
 *      1998  Eric Lammerts - ISO 9660 Level 3
 *      2004  Paul Serice - Inode Support pushed out from 4GB to 128GB
 *      2004  Paul Serice - NFS Export Operations
 */

#include <linux/init.h>
#include <linux/module.h>

#include <linux/slab.h>
#include <linux/nls.h>
#include <linux/ctype.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(const struct dentry *parent, struct qstr *qstr);
static int isofs_dentry_cmpi(const struct dentry *dentry,
		unsigned int len, const char *str, const struct qstr *name);

#ifdef CONFIG_JOLIET
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 *dentry,
		unsigned int len, const char *str, const struct qstr *name);
static int isofs_dentry_cmp_ms(const struct dentry *dentry,
		unsigned int len, const char *str, const struct qstr *name);
#endif


static void isofs_put_super(struct super_block *sb) { struct isofs_sb_info *sbi = ISOFS_SB(sb); #ifdef CONFIG_JOLIET unload_nls(sbi->s_nls_iocharset); #endif kfree(sbi); sb->s_fs_info = NULL; return; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2351.11%466.67%
brian gerstbrian gerst2248.89%233.33%
Total45100.00%6100.00%

static int isofs_read_inode(struct inode *, int relocated); static int isofs_statfs (struct dentry *, struct kstatfs *); static struct kmem_cache *isofs_inode_cachep;
static struct inode *isofs_alloc_inode(struct super_block *sb) { struct iso_inode_info *ei; ei = kmem_cache_alloc(isofs_inode_cachep, GFP_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds4097.56%150.00%
christoph lameterchristoph lameter12.44%150.00%
Total41100.00%2100.00%


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)); }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds1850.00%150.00%
nick pigginnick piggin1850.00%150.00%
Total36100.00%2100.00%


static void isofs_destroy_inode(struct inode *inode) { call_rcu(&inode->i_rcu, isofs_i_callback); }

Contributors

PersonTokensPropCommitsCommitProp
nick pigginnick piggin21100.00%1100.00%
Total21100.00%1100.00%


static void init_once(void *foo) { struct iso_inode_info *ei = foo; inode_init_once(&ei->vfs_inode); }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds2288.00%150.00%
christoph lameterchristoph lameter312.00%150.00%
Total25100.00%2100.00%


static int __init init_inodecache(void) { isofs_inode_cachep = kmem_cache_create("isofs_inode_cache", sizeof(struct iso_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD|SLAB_ACCOUNT), init_once); if (isofs_inode_cachep == NULL) return -ENOMEM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds3982.98%116.67%
paul jacksonpaul jackson48.51%233.33%
vladimir davydovvladimir davydov24.26%116.67%
fabian frederickfabian frederick12.13%116.67%
andrew mortonandrew morton12.13%116.67%
Total47100.00%6100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds1376.47%150.00%
kirill a. shutemovkirill a. shutemov423.53%150.00%
Total17100.00%2100.00%


static int isofs_remount(struct super_block *sb, int *flags, char *data) { sync_filesystem(sb); if (!(*flags & MS_RDONLY)) return -EROFS; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro2560.98%133.33%
jan karajan kara1126.83%133.33%
theodore tsotheodore tso512.20%133.33%
Total41100.00%3100.00%

static const struct super_operations isofs_sops = { .alloc_inode = isofs_alloc_inode, .destroy_inode = isofs_destroy_inode, .put_super = isofs_put_super, .statfs = isofs_statfs, .remount_fs = isofs_remount, .show_options = generic_show_options, }; static const struct dentry_operations isofs_dentry_ops[] = { { .d_hash = isofs_hashi, .d_compare = isofs_dentry_cmpi, }, #ifdef CONFIG_JOLIET { .d_hash = isofs_hash_ms, .d_compare = isofs_dentry_cmp_ms, }, { .d_hash = isofs_hashi_ms, .d_compare = isofs_dentry_cmpi_ms, }, #endif }; struct iso9660_options{ unsigned int rock:1; unsigned int joliet:1; unsigned int cruft:1; unsigned int hide:1; unsigned int showassoc:1; unsigned int nocompress:1; unsigned int overriderockperm:1; unsigned int uid_set:1; unsigned int gid_set:1; unsigned int utf8:1; unsigned char map; unsigned char check; unsigned int blocksize; umode_t fmode; umode_t dmode; kgid_t gid; kuid_t uid; char *iocharset; /* LVE */ s32 session; s32 sbsector; }; /* * Compute the hash for the isofs name corresponding to the dentry. */
static int isofs_hashi_common(const struct dentry *dentry, struct qstr *qstr, int ms) { const char *name; int len; char c; unsigned long hash; len = qstr->len; name = qstr->name; if (ms) { while (len && name[len-1] == '.') len--; } hash = init_name_hash(dentry); while (len--) { c = tolower(*name++); hash = partial_name_hash(c, hash); } qstr->hash = end_name_hash(hash); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git10492.04%150.00%
linus torvaldslinus torvalds97.96%150.00%
Total113100.00%2100.00%

/* * Compare of two isofs names. */
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 = name->len; blen = len; if (ms) { while (alen && name->name[alen-1] == '.') alen--; while (blen && str[blen-1] == '.') blen--; } if (alen == blen) { if (ci) { if (strncasecmp(name->name, str, alen) == 0) return 0; } else { if (strncmp(name->name, str, alen) == 0) return 0; } } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git11280.58%133.33%
nick pigginnick piggin2618.71%133.33%
rasmus villemoesrasmus villemoes10.72%133.33%
Total139100.00%3100.00%


static int isofs_hashi(const struct dentry *dentry, struct qstr *qstr) { return isofs_hashi_common(dentry, qstr, 0); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2488.89%133.33%
linus torvaldslinus torvalds27.41%133.33%
nick pigginnick piggin13.70%133.33%
Total27100.00%3100.00%


static int isofs_dentry_cmpi(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 0, 1); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2253.66%266.67%
nick pigginnick piggin1946.34%133.33%
Total41100.00%3100.00%

#ifdef CONFIG_JOLIET /* * Compute the hash for the isofs name corresponding to the dentry. */
static int isofs_hash_common(const struct dentry *dentry, struct qstr *qstr, int ms) { const char *name; int len; len = qstr->len; name = qstr->name; if (ms) { while (len && name[len-1] == '.') len--; } qstr->hash = full_name_hash(dentry, name, len); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
arnd bergmannarnd bergmann7089.74%150.00%
linus torvaldslinus torvalds810.26%150.00%
Total78100.00%2100.00%


static int isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr) { return isofs_hash_common(dentry, qstr, 1); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2488.89%133.33%
linus torvaldslinus torvalds27.41%133.33%
nick pigginnick piggin13.70%133.33%
Total27100.00%3100.00%


static int isofs_hashi_ms(const struct dentry *dentry, struct qstr *qstr) { return isofs_hashi_common(dentry, qstr, 1); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2488.89%250.00%
linus torvaldslinus torvalds27.41%125.00%
nick pigginnick piggin13.70%125.00%
Total27100.00%4100.00%


static int isofs_dentry_cmp_ms(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 1, 0); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2356.10%375.00%
nick pigginnick piggin1843.90%125.00%
Total41100.00%4100.00%


static int isofs_dentry_cmpi_ms(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 1, 1); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2253.66%375.00%
nick pigginnick piggin1946.34%125.00%
Total41100.00%4100.00%

#endif enum { Opt_block, Opt_check_r, Opt_check_s, Opt_cruft, Opt_gid, Opt_ignore, Opt_iocharset, Opt_map_a, Opt_map_n, Opt_map_o, Opt_mode, Opt_nojoliet, Opt_norock, Opt_sb, Opt_session, Opt_uid, Opt_unhide, Opt_utf8, Opt_err, Opt_nocompress, Opt_hide, Opt_showassoc, Opt_dmode, Opt_overriderockperm, }; static const match_table_t tokens = { {Opt_norock, "norock"}, {Opt_nojoliet, "nojoliet"}, {Opt_unhide, "unhide"}, {Opt_hide, "hide"}, {Opt_showassoc, "showassoc"}, {Opt_cruft, "cruft"}, {Opt_utf8, "utf8"}, {Opt_iocharset, "iocharset=%s"}, {Opt_map_a, "map=acorn"}, {Opt_map_a, "map=a"}, {Opt_map_n, "map=normal"}, {Opt_map_n, "map=n"}, {Opt_map_o, "map=off"}, {Opt_map_o, "map=o"}, {Opt_session, "session=%u"}, {Opt_sb, "sbsector=%u"}, {Opt_check_r, "check=relaxed"}, {Opt_check_r, "check=r"}, {Opt_check_s, "check=strict"}, {Opt_check_s, "check=s"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_mode, "mode=%u"}, {Opt_dmode, "dmode=%u"}, {Opt_overriderockperm, "overriderockperm"}, {Opt_block, "block=%u"}, {Opt_ignore, "conv=binary"}, {Opt_ignore, "conv=b"}, {Opt_ignore, "conv=text"}, {Opt_ignore, "conv=t"}, {Opt_ignore, "conv=mtext"}, {Opt_ignore, "conv=m"}, {Opt_ignore, "conv=auto"}, {Opt_ignore, "conv=a"}, {Opt_nocompress, "nocompress"}, {Opt_err, NULL} };
static int parse_options(char *options, struct iso9660_options *popt) { char *p; int option; popt->map = 'n'; popt->rock = 1; popt->joliet = 1; popt->cruft = 0; popt->hide = 0; popt->showassoc = 0; popt->check = 'u'; /* unset */ popt->nocompress = 0; popt->blocksize = 1024; popt->fmode = popt->dmode = ISOFS_INVALID_MODE; popt->uid_set = 0; popt->gid_set = 0; popt->gid = GLOBAL_ROOT_GID; popt->uid = GLOBAL_ROOT_UID; popt->iocharset = NULL; popt->utf8 = 0; popt->overriderockperm = 0; popt->session=-1; popt->sbsector=-1; if (!options) return 1; while ((p = strsep(&options, ",")) != NULL) { int token; substring_t args[MAX_OPT_ARGS]; unsigned n; if (!*p) continue; token = match_token(p, tokens, args); switch (token) { case Opt_norock: popt->rock = 0; break; case Opt_nojoliet: popt->joliet = 0; break; case Opt_hide: popt->hide = 1; break; case Opt_unhide: case Opt_showassoc: popt->showassoc = 1; break; case Opt_cruft: popt->cruft = 1; break; case Opt_utf8: popt->utf8 = 1; break; #ifdef CONFIG_JOLIET case Opt_iocharset: popt->iocharset = match_strdup(&args[0]); break; #endif case Opt_map_a: popt->map = 'a'; break; case Opt_map_o: popt->map = 'o'; break; case Opt_map_n: popt->map = 'n'; break; case Opt_session: if (match_int(&args[0], &option)) return 0; n = option; if (n > 99) return 0; popt->session = n + 1; break; case Opt_sb: if (match_int(&args[0], &option)) return 0; popt->sbsector = option; break; case Opt_check_r: popt->check = 'r'; break; case Opt_check_s: popt->check = 's'; break; case Opt_ignore: break; case Opt_uid: if (match_int(&args[0], &option)) return 0; 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 = make_kgid(current_user_ns(), option); if (!gid_valid(popt->gid)) return 0; popt->gid_set = 1; break; case Opt_mode: if (match_int(&args[0], &option)) return 0; popt->fmode = option; break; case Opt_dmode: if (match_int(&args[0], &option)) return 0; popt->dmode = option; break; case Opt_overriderockperm: popt->overriderockperm = 1; break; case Opt_block: if (match_int(&args[0], &option)) return 0; n = option; if (n != 512 && n != 1024 && n != 2048) return 0; popt->blocksize = n; break; case Opt_nocompress: popt->nocompress = 1; break; default: return 0; } } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git29145.05%1458.33%
andrew mortonandrew morton19430.03%28.33%
jan karajan kara8413.00%416.67%
eric w. biedermaneric w. biederman406.19%14.17%
jeremy whitejeremy white192.94%14.17%
dave jonesdave jones121.86%14.17%
linus torvaldslinus torvalds60.93%14.17%
Total646100.00%24100.00%

/* * look if the driver can tell the multi session redirection value * * don't change this if you don't know what you do, please! * Multisession is legal only with XA disks. * A non-XA disk with more than one volume descriptor may do it right, but * usually is written in a nowhere standardized "multi-partition" manner. * Multisession uses absolute addressing (solely the first frame of the whole * track is #0), multi-partition uses relative addressing (each first frame of * each track is #0), and a track is not a session. * * A broken CDwriter software or drive firmware does not set new standards, * at least not if conflicting with the existing ones. * * emoenke@gwdg.de */ #define WE_OBEY_THE_WRITTEN_STANDARDS 1
static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) { struct cdrom_multisession ms_info; unsigned int vol_desc_start; struct block_device *bdev = sb->s_bdev; int i; vol_desc_start=0; ms_info.addr_format=CDROM_LBA; if(session >= 0 && session <= 99) { struct cdrom_tocentry Te; Te.cdte_track=session; Te.cdte_format=CDROM_LBA; i = ioctl_by_bdev(bdev, CDROMREADTOCENTRY, (unsigned long) &Te); if (!i) { printk(KERN_DEBUG "ISOFS: Session %d start %d type %d\n", session, Te.cdte_addr.lba, Te.cdte_ctrl&CDROM_DATA_TRACK); if ((Te.cdte_ctrl&CDROM_DATA_TRACK) == 4) return Te.cdte_addr.lba; } printk(KERN_ERR "ISOFS: Invalid session number or type of track\n"); } i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); if (session > 0) printk(KERN_ERR "ISOFS: Invalid session number\n"); #if 0 printk(KERN_DEBUG "isofs.inode: CDROMMULTISESSION: rc=%d\n",i); if (i==0) { printk(KERN_DEBUG "isofs.inode: XA disk: %s\n",ms_info.xa_flag?"yes":"no"); printk(KERN_DEBUG "isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba); } #endif if (i==0) #if WE_OBEY_THE_WRITTEN_STANDARDS if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ #endif vol_desc_start=ms_info.addr.lba; return vol_desc_start; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git20098.04%685.71%
dave jonesdave jones41.96%114.29%
Total204100.00%7100.00%

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

Contributors

PersonTokensPropCommitsCommitProp
ondrej zaryondrej zary110100.00%1100.00%
Total110100.00%1100.00%

/* * Initialize the superblock and read the root inode. * * Note: a check_disk_change() has been done immediately prior * to this call, so we don't need to check again. */
static int isofs_fill_super(struct super_block *s, void *data, int silent) { struct buffer_head *bh = NULL, *pri_bh = NULL; struct hs_primary_descriptor *h_pri = NULL; struct iso_primary_descriptor *pri = NULL; struct iso_supplementary_descriptor *sec = NULL; struct iso_directory_record *rootp; struct inode *inode; struct iso9660_options opt; struct isofs_sb_info *sbi; unsigned long first_data_zone; int joliet_level = 0; int iso_blknum, block; int orig_zonesize; int table, error = -EINVAL; unsigned int vol_desc_start; save_mount_options(s, data); sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) return -ENOMEM; s->s_fs_info = sbi; if (!parse_options((char *)data, &opt)) goto out_freesbi; /* * First of all, get the hardware blocksize for this device. * If we don't know what it is, or the hardware blocksize is * larger than the blocksize the user specified, then use * that value. */ /* * What if bugger tells us to go beyond page size? */ opt.blocksize = sb_min_blocksize(s, opt.blocksize); sbi->s_high_sierra = 0; /* default is iso9660 */ vol_desc_start = (opt.sbsector != -1) ? opt.sbsector : isofs_get_last_session(s,opt.session); for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) { struct hs_volume_descriptor *hdp; struct iso_volume_descriptor *vdp; block = iso_blknum << (ISOFS_BLOCK_BITS - s->s_blocksize_bits); if (!(bh = sb_bread(s, block))) goto out_no_read; vdp = (struct iso_volume_descriptor *)bh->b_data; hdp = (struct hs_volume_descriptor *)bh->b_data; /* * Due to the overlapping physical location of the descriptors, * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure * proper identification in this case, we first check for ISO. */ if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) { if (isonum_711(vdp->type) == ISO_VD_END) break; if (isonum_711(vdp->type) == ISO_VD_PRIMARY) { if (pri == NULL) { pri = (struct iso_primary_descriptor *)vdp; /* Save the buffer in case we need it ... */ pri_bh = bh; bh = NULL; } } #ifdef CONFIG_JOLIET else if (isonum_711(vdp->type) == ISO_VD_SUPPLEMENTARY) { sec = (struct iso_supplementary_descriptor *)vdp; if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) { if (opt.joliet) { if (sec->escape[2] == 0x40) joliet_level = 1; else if (sec->escape[2] == 0x43) joliet_level = 2; else if (sec->escape[2] == 0x45) joliet_level = 3; printk(KERN_DEBUG "ISO 9660 Extensions: " "Microsoft Joliet Level %d\n", joliet_level); } goto root_found; } else { /* Unknown supplementary volume descriptor */ sec = NULL; } } #endif } else { if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) { if (isonum_711(hdp->type) != ISO_VD_PRIMARY) goto out_freebh; sbi->s_high_sierra = 1; opt.rock = 0; h_pri = (struct hs_primary_descriptor *)vdp; goto root_found; } } /* Just skip any volume descriptors we don't recognize */ brelse(bh); bh = NULL; } /* * If we fall through, either no volume descriptor was found, * or else we passed a primary descriptor looking for others. */ if (!pri) goto out_unknown_format; brelse(bh); bh = pri_bh; pri_bh = NULL; root_found: /* We don't support read-write mounts */ if (!(s->s_flags & MS_RDONLY)) { error = -EACCES