📄 super.c
字号:
/* * super.c * * PURPOSE * Super block routines for the OSTA-UDF(tm) filesystem. * * DESCRIPTION * OSTA-UDF(tm) = Optical Storage Technology Association * Universal Disk Format. * * This code is based on version 2.00 of the UDF specification, * and revision 3 of the ECMA 167 standard [equivalent to ISO 13346]. * http://www.osta.org/ * http://www.ecma.ch/ * http://www.iso.org/ * * CONTACTS * E-mail regarding any portion of the Linux UDF file system should be * directed to the development team mailing list (run by majordomo): * linux_udf@hpesjro.fc.hp.com * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 2000 Stelias Computing Inc * * HISTORY * * 09/24/98 dgb changed to allow compiling outside of kernel, and * added some debugging. * 10/01/98 dgb updated to allow (some) possibility of compiling w/2.0.34 * 10/16/98 attempting some multi-session support * 10/17/98 added freespace count for "df" * 11/11/98 gr added novrs option * 11/26/98 dgb added fileset,anchor mount options * 12/06/98 blf really hosed things royally. vat/sparing support. sequenced vol descs * rewrote option handling based on isofs * 12/20/98 find the free space bitmap (if it exists) */#include "udfdecl.h" #include <linux/config.h>#include <linux/blkdev.h>#include <linux/slab.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/parser.h>#include <linux/stat.h>#include <linux/cdrom.h>#include <linux/nls.h>#include <linux/smp_lock.h>#include <linux/buffer_head.h>#include <linux/vfs.h>#include <asm/byteorder.h>#include <linux/udf_fs.h>#include "udf_sb.h"#include "udf_i.h"#include <linux/init.h>#include <asm/uaccess.h>#define VDS_POS_PRIMARY_VOL_DESC 0#define VDS_POS_UNALLOC_SPACE_DESC 1#define VDS_POS_LOGICAL_VOL_DESC 2#define VDS_POS_PARTITION_DESC 3#define VDS_POS_IMP_USE_VOL_DESC 4#define VDS_POS_VOL_DESC_PTR 5#define VDS_POS_TERMINATING_DESC 6#define VDS_POS_LENGTH 7static char error_buf[1024];/* These are the "meat" - everything else is stuffing */static int udf_fill_super(struct super_block *, void *, int);static void udf_put_super(struct super_block *);static void udf_write_super(struct super_block *);static int udf_remount_fs(struct super_block *, int *, char *);static int udf_check_valid(struct super_block *, int, int);static int udf_vrs(struct super_block *sb, int silent);static int udf_load_partition(struct super_block *, lb_addr *);static int udf_load_logicalvol(struct super_block *, struct buffer_head *, lb_addr *);static void udf_load_logicalvolint(struct super_block *, extent_ad);static void udf_find_anchor(struct super_block *);static int udf_find_fileset(struct super_block *, lb_addr *, lb_addr *);static void udf_load_pvoldesc(struct super_block *, struct buffer_head *);static void udf_load_fileset(struct super_block *, struct buffer_head *, lb_addr *);static void udf_load_partdesc(struct super_block *, struct buffer_head *);static void udf_open_lvid(struct super_block *);static void udf_close_lvid(struct super_block *);static unsigned int udf_count_free(struct super_block *);static int udf_statfs(struct super_block *, struct kstatfs *);/* UDF filesystem type */static struct super_block *udf_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data){ return get_sb_bdev(fs_type, flags, dev_name, data, udf_fill_super);}static struct file_system_type udf_fstype = { .owner = THIS_MODULE, .name = "udf", .get_sb = udf_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV,};static kmem_cache_t * udf_inode_cachep;static struct inode *udf_alloc_inode(struct super_block *sb){ struct udf_inode_info *ei; ei = (struct udf_inode_info *)kmem_cache_alloc(udf_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode;}static void udf_destroy_inode(struct inode *inode){ kmem_cache_free(udf_inode_cachep, UDF_I(inode));}static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags){ struct udf_inode_info *ei = (struct udf_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { ei->i_ext.i_data = NULL; inode_init_once(&ei->vfs_inode); }}static int init_inodecache(void){ udf_inode_cachep = kmem_cache_create("udf_inode_cache", sizeof(struct udf_inode_info), 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (udf_inode_cachep == NULL) return -ENOMEM; return 0;}static void destroy_inodecache(void){ if (kmem_cache_destroy(udf_inode_cachep)) printk(KERN_INFO "udf_inode_cache: not all structures were freed\n");}/* Superblock operations */static struct super_operations udf_sb_ops = { .alloc_inode = udf_alloc_inode, .destroy_inode = udf_destroy_inode, .read_inode = udf_read_inode, .write_inode = udf_write_inode, .put_inode = udf_put_inode, .delete_inode = udf_delete_inode, .clear_inode = udf_clear_inode, .put_super = udf_put_super, .write_super = udf_write_super, .statfs = udf_statfs, .remount_fs = udf_remount_fs,};struct udf_options{ unsigned char novrs; unsigned int blocksize; unsigned int session; unsigned int lastblock; unsigned int anchor; unsigned int volume; unsigned short partition; unsigned int fileset; unsigned int rootdir; unsigned int flags; mode_t umask; gid_t gid; uid_t uid; struct nls_table *nls_map;};static int __init init_udf_fs(void){ int err; printk(KERN_NOTICE "udf: registering filesystem\n"); err = init_inodecache(); if (err) goto out1; err = register_filesystem(&udf_fstype); if (err) goto out; return 0;out: destroy_inodecache();out1: return err;}static void __exit exit_udf_fs(void){ printk(KERN_NOTICE "udf: unregistering filesystem\n"); unregister_filesystem(&udf_fstype); destroy_inodecache();}module_init(init_udf_fs)module_exit(exit_udf_fs)/* * udf_parse_options * * PURPOSE * Parse mount options. * * DESCRIPTION * The following mount options are supported: * * gid= Set the default group. * umask= Set the default umask. * uid= Set the default user. * bs= Set the block size. * unhide Show otherwise hidden files. * undelete Show deleted files in lists. * adinicb Embed data in the inode (default) * noadinicb Don't embed data in the inode * shortad Use short ad's * longad Use long ad's (default) * nostrict Unset strict conformance * iocharset= Set the NLS character set * * The remaining are for debugging and disaster recovery: * * novrs Skip volume sequence recognition * * The following expect a offset from 0. * * session= Set the CDROM session (default= last session) * anchor= Override standard anchor location. (default= 256) * volume= Override the VolumeDesc location. (unused) * partition= Override the PartitionDesc location. (unused) * lastblock= Set the last block of the filesystem/ * * The following expect a offset from the partition root. * * fileset= Override the fileset block location. (unused) * rootdir= Override the root directory location. (unused) * WARNING: overriding the rootdir to a non-directory may * yield highly unpredictable results. * * PRE-CONDITIONS * options Pointer to mount options string. * uopts Pointer to mount options variable. * * POST-CONDITIONS * <return> 1 Mount options parsed okay. * <return> 0 Error parsing mount options. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */enum { Opt_novrs, Opt_nostrict, Opt_bs, Opt_unhide, Opt_undelete, Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad, Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock, Opt_anchor, Opt_volume, Opt_partition, Opt_fileset, Opt_rootdir, Opt_utf8, Opt_iocharset, Opt_err};static match_table_t tokens = { {Opt_novrs, "novrs"}, {Opt_nostrict, "nostrict"}, {Opt_bs, "bs=%u"}, {Opt_unhide, "unhide"}, {Opt_undelete, "undelete"}, {Opt_noadinicb, "noadinicb"}, {Opt_adinicb, "adinicb"}, {Opt_shortad, "shortad"}, {Opt_longad, "longad"}, {Opt_gid, "gid=%u"}, {Opt_uid, "uid=%u"}, {Opt_umask, "umask=%o"}, {Opt_session, "session=%u"}, {Opt_lastblock, "lastblock=%u"}, {Opt_anchor, "anchor=%u"}, {Opt_volume, "volume=%u"}, {Opt_partition, "partition=%u"}, {Opt_fileset, "fileset=%u"}, {Opt_rootdir, "rootdir=%u"}, {Opt_utf8, "utf8"}, {Opt_iocharset, "iocharset=%s"}, {Opt_err, NULL}};static intudf_parse_options(char *options, struct udf_options *uopt){ char *p; int option; uopt->novrs = 0; uopt->blocksize = 2048; uopt->partition = 0xFFFF; uopt->session = 0xFFFFFFFF; uopt->lastblock = 0; uopt->anchor = 0; uopt->volume = 0xFFFFFFFF; uopt->rootdir = 0xFFFFFFFF; uopt->fileset = 0xFFFFFFFF; uopt->nls_map = NULL; if (!options) return 1; while ((p = strsep(&options, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; if (!*p) continue; token = match_token(p, tokens, args); switch (token) { case Opt_novrs: uopt->novrs = 1; case Opt_bs: if (match_int(&args[0], &option)) return 0; uopt->blocksize = option; break; case Opt_unhide: uopt->flags |= (1 << UDF_FLAG_UNHIDE); break; case Opt_undelete: uopt->flags |= (1 << UDF_FLAG_UNDELETE); break; case Opt_noadinicb: uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); break; case Opt_adinicb: uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); break; case Opt_shortad: uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); break; case Opt_longad: uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); break; case Opt_gid: if (match_int(args, &option)) return 0; uopt->gid = option; break; case Opt_uid: if (match_int(args, &option)) return 0; uopt->uid = option; break; case Opt_umask: if (match_octal(args, &option)) return 0; uopt->umask = option; break; case Opt_nostrict: uopt->flags &= ~(1 << UDF_FLAG_STRICT); break; case Opt_session: if (match_int(args, &option)) return 0; uopt->session = option; break; case Opt_lastblock: if (match_int(args, &option)) return 0; uopt->lastblock = option; break; case Opt_anchor: if (match_int(args, &option)) return 0; uopt->anchor = option; break; case Opt_volume: if (match_int(args, &option)) return 0; uopt->volume = option; break; case Opt_partition: if (match_int(args, &option)) return 0; uopt->partition = option; break; case Opt_fileset: if (match_int(args, &option)) return 0; uopt->fileset = option; break; case Opt_rootdir: if (match_int(args, &option)) return 0; uopt->rootdir = option; break; case Opt_utf8: uopt->flags |= (1 << UDF_FLAG_UTF8); break;#if defined(CONFIG_NLS) || defined(CONFIG_NLS_MODULE) case Opt_iocharset: uopt->nls_map = load_nls(args[0].from); uopt->flags |= (1 << UDF_FLAG_NLS_MAP); break;#endif default: printk(KERN_ERR "udf: bad mount option \"%s\" " "or missing value\n", p); return 0; } } return 1;}voidudf_write_super(struct super_block *sb){ lock_kernel(); if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); sb->s_dirt = 0; unlock_kernel();}static intudf_remount_fs(struct super_block *sb, int *flags, char *options){ struct udf_options uopt; uopt.flags = UDF_SB(sb)->s_flags ; uopt.uid = UDF_SB(sb)->s_uid ; uopt.gid = UDF_SB(sb)->s_gid ; uopt.umask = UDF_SB(sb)->s_umask ; if ( !udf_parse_options(options, &uopt) ) return -EINVAL; UDF_SB(sb)->s_flags = uopt.flags; UDF_SB(sb)->s_uid = uopt.uid; UDF_SB(sb)->s_gid = uopt.gid; UDF_SB(sb)->s_umask = uopt.umask; if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) udf_close_lvid(sb); else udf_open_lvid(sb); return 0;}/* * udf_set_blocksize * * PURPOSE * Set the block size to be used in all transfers. * * DESCRIPTION * To allow room for a DMA transfer, it is best to guess big when unsure. * This routine picks 2048 bytes as the blocksize when guessing. This * should be adequate until devices with larger block sizes become common. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -