📄 file.c
字号:
/* * linux/fs/affs/file.c * * (c) 1996 Hans-Joachim Widmaier - Rewritten * * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. * * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. * * (C) 1991 Linus Torvalds - minix filesystem * * affs regular file handling primitives */#define DEBUG 0#include <asm/div64.h>#include <asm/uaccess.h>#include <asm/system.h>#include <linux/sched.h>#include <linux/affs_fs.h>#include <linux/fcntl.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/malloc.h>#include <linux/stat.h>#include <linux/locks.h>#include <linux/smp_lock.h>#include <linux/dirent.h>#include <linux/fs.h>#include <linux/amigaffs.h>#include <linux/mm.h>#include <linux/pagemap.h>#define MIN(a,b) (((a)<(b))?(a):(b))#define MAX(a,b) (((a)>(b))?(a):(b))#if PAGE_SIZE < 4096#error PAGE_SIZE must be at least 4096#endifstatic struct buffer_head *affs_getblock(struct inode *inode, s32 block);static ssize_t affs_file_read_ofs(struct file *filp, char *buf, size_t count, loff_t *ppos);static ssize_t affs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos);static ssize_t affs_file_write_ofs(struct file *filp, const char *buf, size_t cnt, loff_t *ppos);static int alloc_ext_cache(struct inode *inode);struct file_operations affs_file_operations = { read: generic_file_read, write: affs_file_write, mmap: generic_file_mmap, fsync: file_fsync,};struct inode_operations affs_file_inode_operations = { truncate: affs_truncate, setattr: affs_notify_change,};struct file_operations affs_file_operations_ofs = { read: affs_file_read_ofs, write: affs_file_write_ofs, fsync: file_fsync,};#define AFFS_ISINDEX(x) ((x < 129) || \ (x < 512 && (x & 1) == 0) || \ (x < 1024 && (x & 3) == 0) || \ (x < 2048 && (x & 15) == 0) || \ (x < 4096 && (x & 63) == 0) || \ (x < 20480 && (x & 255) == 0) || \ (x < 36864 && (x & 511) == 0))/* The keys of the extension blocks are stored in a 512-entry * deep cache. In order to save memory, not every key of later * extension blocks is stored - the larger the file gets, the * bigger the holes in between. */static intseqnum_to_index(int seqnum){ /* All of the first 127 keys are stored */ if (seqnum < 128) return seqnum; seqnum -= 128; /* Of the next 384 keys, every 2nd is kept */ if (seqnum < (192 * 2)) return 128 + (seqnum >> 1); seqnum -= 192 * 2; /* Every 4th of the next 512 */ if (seqnum < (128 * 4)) return 128 + 192 + (seqnum >> 2); seqnum -= 128 * 4; /* Every 16th of the next 1024 */ if (seqnum < (64 * 16)) return 128 + 192 + 128 + (seqnum >> 4); seqnum -= 64 * 16; /* Every 64th of the next 2048 */ if (seqnum < (32 * 64)) return 128 + 192 + 128 + 64 + (seqnum >> 6); seqnum -= 32 * 64; /* Every 256th of the next 16384 */ if (seqnum < (64 * 256)) return 128 + 192 + 128 + 64 + 32 + (seqnum >> 8); seqnum -= 64 * 256; /* Every 512th upto 36479 (1.3 GB with 512 byte blocks). * Seeking to positions behind this will get slower * than dead snails nailed to the ground. But if * someone uses files that large with 512-byte blocks, * he or she deserves no better. */ if (seqnum > (31 * 512)) seqnum = 31 * 512; return 128 + 192 + 128 + 64 + 32 + 64 + (seqnum >> 9);}/* Now the other way round: Calculate the sequence * number of an extension block of a key at the * given index in the cache. */static intindex_to_seqnum(int index){ if (index < 128) return index; index -= 128; if (index < 192) return 128 + (index << 1); index -= 192; if (index < 128) return 128 + 192 * 2 + (index << 2); index -= 128; if (index < 64) return 128 + 192 * 2 + 128 * 4 + (index << 4); index -= 64; if (index < 32) return 128 + 192 * 2 + 128 * 4 + 64 * 16 + (index << 6); index -= 32; if (index < 64) return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + (index << 8); index -= 64; return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + 64 * 256 + (index << 9);}static s32 __inline__calc_key(struct inode *inode, int *ext){ int index; struct key_cache *kc; for (index = 0; index < 4; index++) { kc = &inode->u.affs_i.i_ec->kc[index]; if (kc->kc_last == -1) continue; /* don't look in cache if invalid. */ if (*ext == kc->kc_this_seq) { return kc->kc_this_key; } else if (*ext == kc->kc_this_seq + 1) { if (kc->kc_next_key) return kc->kc_next_key; else { (*ext)--; return kc->kc_this_key; } } } index = seqnum_to_index(*ext); if (index > inode->u.affs_i.i_ec->max_ext) index = inode->u.affs_i.i_ec->max_ext; *ext = index_to_seqnum(index); return inode->u.affs_i.i_ec->ec[index];}intaffs_bmap(struct inode *inode, int block){ struct buffer_head *bh; s32 key, nkey; s32 ptype, stype; int ext; int index; int keycount; struct key_cache *kc; struct key_cache *tkc; struct timeval tv; s32 *keyp; int i; pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block); lock_kernel(); if (block < 0) { affs_error(inode->i_sb,"bmap","Block < 0"); goto out_fail; } if (!inode->u.affs_i.i_ec) { if (alloc_ext_cache(inode)) { goto out_fail; } } /* Try to find the requested key in the cache. * In order to speed this up as much as possible, * the cache line lookup is done in a separate * step. */ for (i = 0; i < 4; i++) { tkc = &inode->u.affs_i.i_ec->kc[i]; /* Look in any cache if the key is there */ if (block <= tkc->kc_last && block >= tkc->kc_first) { unlock_kernel(); return tkc->kc_keys[block - tkc->kc_first]; } } kc = NULL; tv = xtime; for (i = 0; i < 4; i++) { tkc = &inode->u.affs_i.i_ec->kc[i]; if (tkc->kc_lru_time.tv_sec > tv.tv_sec) continue; if (tkc->kc_lru_time.tv_sec < tv.tv_sec || tkc->kc_lru_time.tv_usec < tv.tv_usec) { kc = tkc; tv = tkc->kc_lru_time; } } if (!kc) /* Really shouldn't happen */ kc = tkc; kc->kc_lru_time = xtime; keyp = kc->kc_keys; kc->kc_first = block; kc->kc_last = -1; keycount = AFFS_KCSIZE; /* Calculate sequence number of the extension block where the * number of the requested block is stored. 0 means it's in * the file header. */ ext = block / AFFS_I2HSIZE(inode); key = calc_key(inode,&ext); block -= ext * AFFS_I2HSIZE(inode); for (;;) { bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)); if (!bh) goto out_fail; index = seqnum_to_index(ext); if (index > inode->u.affs_i.i_ec->max_ext && (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE)) { affs_brelse(bh); goto out_fail; } nkey = be32_to_cpu(FILE_END(bh->b_data,inode)->extension); if (block < AFFS_I2HSIZE(inode)) { /* Fill cache as much as possible */ if (keycount) { kc->kc_first = ext * AFFS_I2HSIZE(inode) + block; keycount = keycount < AFFS_I2HSIZE(inode) - block ? keycount : AFFS_I2HSIZE(inode) - block; for (i = 0; i < keycount; i++) kc->kc_keys[i] = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block + i)); kc->kc_last = kc->kc_first + i - 1; } break; } block -= AFFS_I2HSIZE(inode); affs_brelse(bh); ext++; if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) { inode->u.affs_i.i_ec->ec[index] = nkey; inode->u.affs_i.i_ec->max_ext = index; } key = nkey; } kc->kc_this_key = key; kc->kc_this_seq = ext; kc->kc_next_key = nkey; key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block)); affs_brelse(bh);out: unlock_kernel(); return key;out_fail: key=0; goto out;}static int affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create){ int err, phys=0, new=0; if (!create) { phys = affs_bmap(inode, block); if (phys) { bh_result->b_dev = inode->i_dev; bh_result->b_blocknr = phys; bh_result->b_state |= (1UL << BH_Mapped); } return 0; } err = -EIO; lock_kernel(); if (block < 0) goto abort_negative; if (affs_getblock(inode, block)==NULL) { err = -EIO; goto abort; } bh_result->b_dev = inode->i_dev; bh_result->b_blocknr = phys; bh_result->b_state |= (1UL << BH_Mapped); if (new) bh_result->b_state |= (1UL << BH_New); abort: unlock_kernel(); return err;abort_negative: affs_error(inode->i_sb,"affs_get_block","Block < 0"); goto abort;} static int affs_writepage(struct page *page){ return block_write_full_page(page,affs_get_block);}static int affs_readpage(struct file *file, struct page *page){ return block_read_full_page(page,affs_get_block);}static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to){ return cont_prepare_write(page,from,to,affs_get_block, &page->mapping->host->u.affs_i.mmu_private);}static int _affs_bmap(struct address_space *mapping, long block){ return generic_block_bmap(mapping,block,affs_get_block);}struct address_space_operations affs_aops = { readpage: affs_readpage, writepage: affs_writepage, sync_page: block_sync_page, prepare_write: affs_prepare_write, commit_write: generic_commit_write, bmap: _affs_bmap};/* With the affs, getting a random block from a file is not * a simple business. Since this fs does not allow holes, * it may be necessary to allocate all the missing blocks * in between, as well as some new extension blocks. The OFS * is even worse: All data blocks contain pointers to the * next ones, so you have to fix [n-1] after allocating [n]. * What a mess. */static struct buffer_head * affs_getblock(struct inode *inode, s32 block){ struct super_block *sb = inode->i_sb; int ofs = sb->u.affs_sb.s_flags & SF_OFS; int ext = block / AFFS_I2HSIZE(inode); struct buffer_head *bh, *ebh, *pbh = NULL; struct key_cache *kc; s32 key, nkey; int cf, j, pt; int index; int err; pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block); key = calc_key(inode,&ext); block -= ext * AFFS_I2HSIZE(inode); pt = ext ? T_LIST : T_SHORT; /* Key refers now to the last known extension block, * ext is its sequence number (if 0, key refers to the * header block), and block is the block number relative * to the first block stored in that extension block. */ for (;;) { /* Loop over header block and extension blocks */ struct file_front *fdp; bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)); if (!bh) goto out_fail; fdp = (struct file_front *) bh->b_data; err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j); if (err || cf != pt || j != ST_FILE) { affs_error(sb, "getblock", "Block %d is not a valid %s", key, pt == T_SHORT ? "file header" : "ext block"); goto out_free_bh; } j = be32_to_cpu(((struct file_front *)bh->b_data)->block_count); for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) { if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) { if (j > 0) { s32 k = AFFS_BLOCK(bh->b_data, inode, j - 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -