📄 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@hootie.lvld.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-2000 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/version.h>#include <linux/blkdev.h>#include <linux/malloc.h>#include <linux/kernel.h>#include <linux/locks.h>#include <linux/module.h>#include <linux/stat.h>#include <linux/cdrom.h>#include <linux/nls.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 struct super_block *udf_read_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 int udf_find_anchor(struct super_block *, int, int);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 statfs *);/* UDF filesystem type */static DECLARE_FSTYPE_DEV(udf_fstype, "udf", udf_read_super);/* Superblock operations */static struct super_operations udf_sb_ops = { read_inode: udf_read_inode, write_inode: udf_write_inode, put_inode: udf_put_inode, delete_inode: udf_delete_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;};static int __init init_udf_fs(void){ printk(KERN_NOTICE "udf: registering filesystem\n"); return register_filesystem(&udf_fstype);}static void __exit exit_udf_fs(void){ printk(KERN_NOTICE "udf: unregistering filesystem\n"); unregister_filesystem(&udf_fstype);}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) * strict Set strict conformance (unused) * * 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> 0 Mount options parsed okay. * <return> -1 Error parsing mount options. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */static intudf_parse_options(char *options, struct udf_options *uopt){ char *opt, *val; uopt->novrs = 0; uopt->blocksize = 512; uopt->partition = 0xFFFF; uopt->session = 0xFFFFFFFF; uopt->lastblock = 0xFFFFFFFF; uopt->anchor = 0xFFFFFFFF; uopt->volume = 0xFFFFFFFF; uopt->rootdir = 0xFFFFFFFF; uopt->fileset = 0xFFFFFFFF; if (!options) return 1; for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) { /* Make "opt=val" into two strings */ val = strchr(opt, '='); if (val) *(val++) = 0; if (!strcmp(opt, "novrs") && !val) uopt->novrs = 1; else if (!strcmp(opt, "bs") && val) uopt->blocksize = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "unhide") && !val) uopt->flags |= (1 << UDF_FLAG_UNHIDE); else if (!strcmp(opt, "undelete") && !val) uopt->flags |= (1 << UDF_FLAG_UNDELETE); else if (!strcmp(opt, "noadinicb") && !val) uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); else if (!strcmp(opt, "adinicb") && !val) uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); else if (!strcmp(opt, "shortad") && !val) uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); else if (!strcmp(opt, "longad") && !val) uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); else if (!strcmp(opt, "gid") && val) uopt->gid = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "umask") && val) uopt->umask = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "strict") && !val) uopt->flags |= (1 << UDF_FLAG_STRICT); else if (!strcmp(opt, "uid") && val) uopt->uid = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "session") && val) uopt->session = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "lastblock") && val) uopt->lastblock = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "anchor") && val) uopt->anchor = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "volume") && val) uopt->volume = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "partition") && val) uopt->partition = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "fileset") && val) uopt->fileset = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "rootdir") && val) uopt->rootdir = simple_strtoul(val, NULL, 0); else if (val) { printk(KERN_ERR "udf: bad mount option \"%s=%s\"\n", opt, val); return 0; } else { printk(KERN_ERR "udf: bad mount option \"%s\"\n", opt); return 0; } } return 1;}voidudf_write_super(struct super_block *sb){ if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); sb->s_dirt = 0;} 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 CONFIG_UDF_RW != 1 *flags |= MS_RDONLY;#endif 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. * * Note that the Linux kernel can currently only deal with blocksizes of * 512, 1024, 2048, 4096, and 8192 bytes. * * PRE-CONDITIONS * sb Pointer to _locked_ superblock. * * POST-CONDITIONS * sb->s_blocksize Blocksize. * sb->s_blocksize_bits log2 of blocksize. * <return> 0 Blocksize is valid. * <return> 1 Blocksize is invalid. * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */static intudf_set_blocksize(struct super_block *sb, int bsize){ /* Use specified block size if specified */ if (!(sb->s_blocksize = get_hardblocksize(sb->s_dev))) sb->s_blocksize = 2048; if (bsize > sb->s_blocksize) sb->s_blocksize = bsize; /* Block size must be an even multiple of 512 */ switch (sb->s_blocksize) { case 512: sb->s_blocksize_bits = 9; break; case 1024: sb->s_blocksize_bits = 10; break; case 2048: sb->s_blocksize_bits = 11; break; case 4096: sb->s_blocksize_bits = 12; break; case 8192: sb->s_blocksize_bits = 13; break; default: { udf_debug("Bad block size (%ld)\n", sb->s_blocksize); printk(KERN_ERR "udf: bad block size (%ld)\n", sb->s_blocksize); return 0; } } /* Set the block size */ set_blocksize(sb->s_dev, sb->s_blocksize); return sb->s_blocksize;}static intudf_vrs(struct super_block *sb, int silent){ struct VolStructDesc *vsd = NULL; int sector = 32768; struct buffer_head *bh = NULL; int iso9660=0; int nsr02=0; int nsr03=0; /* Block size must be a multiple of 512 */ if (sb->s_blocksize & 511) return sector; sector += (UDF_SB_SESSION(sb) << sb->s_blocksize_bits); udf_debug("Starting at sector %u (%ld byte sectors)\n", (sector >> sb->s_blocksize_bits), sb->s_blocksize); /* Process the sequence (if applicable) */ for (;!nsr02 && !nsr03; sector += 2048) { /* Read a block */ bh = udf_tread(sb, sector >> sb->s_blocksize_bits, sb->s_blocksize); if (!bh) break; /* Look for ISO descriptors */ vsd = (struct VolStructDesc *)(bh->b_data + (sector & (sb->s_blocksize - 1))); if (vsd->stdIdent[0] == 0) { udf_release_data(bh); break; } else if (!strncmp(vsd->stdIdent, STD_ID_CD001, STD_ID_LEN)) { iso9660 = sector; switch (vsd->structType) { case 0: udf_debug("ISO9660 Boot Record found\n"); break; case 1: udf_debug("ISO9660 Primary Volume Descriptor found\n"); break; case 2: udf_debug("ISO9660 Supplementary Volume Descriptor found\n"); break; case 3: udf_debug("ISO9660 Volume Partition Descriptor found\n"); break; case 255: udf_debug("ISO9660 Volume Descriptor Set Terminator found\n"); break; default: udf_debug("ISO9660 VRS (%u) found\n", vsd->structType); break; } } else if (!strncmp(vsd->stdIdent, STD_ID_BEA01, STD_ID_LEN)) { } else if (!strncmp(vsd->stdIdent, STD_ID_TEA01, STD_ID_LEN)) { udf_release_data(bh); break; } else if (!strncmp(vsd->stdIdent, STD_ID_NSR02, STD_ID_LEN)) { nsr02 = sector; } else if (!strncmp(vsd->stdIdent, STD_ID_NSR03, STD_ID_LEN)) { nsr03 = sector; } udf_release_data(bh); } if (nsr03) return nsr03; else if (nsr02) return nsr02; else if (sector - (UDF_SB_SESSION(sb) << sb->s_blocksize_bits) == 32768) return -1; else return 0;}/* * udf_find_anchor * * PURPOSE * Find an anchor volume descriptor. * * PRE-CONDITIONS * sb Pointer to _locked_ superblock. * lastblock Last block on media. * * POST-CONDITIONS * <return> 1 if not found, 0 if ok * * HISTORY * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */static intudf_find_anchor(struct super_block *sb, int useranchor, int lastblock){ int varlastblock = udf_variable_to_fixed(lastblock); int last[] = { lastblock, lastblock - 2, lastblock - 150, lastblock - 152, varlastblock, varlastblock - 2, varlastblock - 150, varlastblock - 152 }; struct buffer_head *bh = NULL; Uint16 ident; Uint32 location; int i; UDF_SB_ANCHOR(sb)[0] = 0; UDF_SB_ANCHOR(sb)[1] = 0; UDF_SB_ANCHOR(sb)[2] = 0; UDF_SB_ANCHOR(sb)[3] = 256 + UDF_SB_SESSION(sb); lastblock = 0; /* Search for an anchor volume descriptor pointer */ /* according to spec, anchor is in either: * block 256 * lastblock-256 * lastblock * however, if the disc isn't closed, it could be 512 */ for (i=0; (!lastblock && i<sizeof(last)/sizeof(int)); i++) { if (!(bh = bread(sb->s_dev, last[i], sb->s_blocksize))) { ident = location = 0; } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); udf_release_data(bh); } if (ident == TID_ANCHOR_VOL_DESC_PTR) { if (location == last[i] - UDF_SB_SESSION(sb)) { lastblock = UDF_SB_ANCHOR(sb)[0] = last[i]; UDF_SB_ANCHOR(sb)[1] = last[i] - 256; } else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb)) { UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); lastblock = UDF_SB_ANCHOR(sb)[0] = udf_variable_to_fixed(last[i]); UDF_SB_ANCHOR(sb)[1] = lastblock - 256; } else udf_debug("Anchor found at block %d, location mismatch %d.\n", last[i], location); } else if (ident == TID_FILE_ENTRY || ident == TID_EXTENDED_FILE_ENTRY)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -