Release 4.10 fs/affs/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
*/
#include <linux/uio.h>
#include "affs.h"
static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext);
static int
affs_file_open(struct inode *inode, struct file *filp)
{
pr_debug("open(%lu,%d)\n",
inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt));
atomic_inc(&AFFS_I(inode)->i_opencnt);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 28 | 56.00% | 2 | 33.33% |
roman zippel | roman zippel | 12 | 24.00% | 1 | 16.67% |
pre-git | pre-git | 9 | 18.00% | 2 | 33.33% |
fabian frederick | fabian frederick | 1 | 2.00% | 1 | 16.67% |
| Total | 50 | 100.00% | 6 | 100.00% |
static int
affs_file_release(struct inode *inode, struct file *filp)
{
pr_debug("release(%lu, %d)\n",
inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt));
if (atomic_dec_and_test(&AFFS_I(inode)->i_opencnt)) {
inode_lock(inode);
if (inode->i_size != AFFS_I(inode)->mmu_private)
affs_truncate(inode);
affs_free_prealloc(inode);
inode_unlock(inode);
}
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 43 | 49.43% | 2 | 25.00% |
roman zippel | roman zippel | 34 | 39.08% | 1 | 12.50% |
pre-git | pre-git | 7 | 8.05% | 3 | 37.50% |
al viro | al viro | 2 | 2.30% | 1 | 12.50% |
fabian frederick | fabian frederick | 1 | 1.15% | 1 | 12.50% |
| Total | 87 | 100.00% | 8 | 100.00% |
static int
affs_grow_extcache(struct inode *inode, u32 lc_idx)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
u32 lc_max;
int i, j, key;
if (!AFFS_I(inode)->i_lc) {
char *ptr = (char *)get_zeroed_page(GFP_NOFS);
if (!ptr)
return -ENOMEM;
AFFS_I(inode)->i_lc = (u32 *)ptr;
AFFS_I(inode)->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
}
lc_max = AFFS_LC_SIZE << AFFS_I(inode)->i_lc_shift;
if (AFFS_I(inode)->i_extcnt > lc_max) {
u32 lc_shift, lc_mask, tmp, off;
/* need to recalculate linear cache, start from old size */
lc_shift = AFFS_I(inode)->i_lc_shift;
tmp = (AFFS_I(inode)->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
for (; tmp; tmp >>= 1)
lc_shift++;
lc_mask = (1 << lc_shift) - 1;
/* fix idx and old size to new shift */
lc_idx >>= (lc_shift - AFFS_I(inode)->i_lc_shift);
AFFS_I(inode)->i_lc_size >>= (lc_shift - AFFS_I(inode)->i_lc_shift);
/* first shrink old cache to make more space */
off = 1 << (lc_shift - AFFS_I(inode)->i_lc_shift);
for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off)
AFFS_I(inode)->i_ac[i] = AFFS_I(inode)->i_ac[j];
AFFS_I(inode)->i_lc_shift = lc_shift;
AFFS_I(inode)->i_lc_mask = lc_mask;
}
/* fill cache to the needed index */
i = AFFS_I(inode)->i_lc_size;
AFFS_I(inode)->i_lc_size = lc_idx + 1;
for (; i <= lc_idx; i++) {
if (!i) {
AFFS_I(inode)->i_lc[0] = inode->i_ino;
continue;
}
key = AFFS_I(inode)->i_lc[i - 1];
j = AFFS_I(inode)->i_lc_mask + 1;
// unlock cache
for (; j > 0; j--) {
bh = affs_bread(sb, key);
if (!bh)
goto err;
key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
affs_brelse(bh);
}
// lock cache
AFFS_I(inode)->i_lc[i] = key;
}
return 0;
err:
// lock cache
return -EIO;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 364 | 81.98% | 3 | 50.00% |
pre-git | pre-git | 80 | 18.02% | 3 | 50.00% |
| Total | 444 | 100.00% | 6 | 100.00% |
static struct buffer_head *
affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *new_bh;
u32 blocknr, tmp;
blocknr = affs_alloc_block(inode, bh->b_blocknr);
if (!blocknr)
return ERR_PTR(-ENOSPC);
new_bh = affs_getzeroblk(sb, blocknr);
if (!new_bh) {
affs_free_block(sb, blocknr);
return ERR_PTR(-EIO);
}
AFFS_HEAD(new_bh)->ptype = cpu_to_be32(T_LIST);
AFFS_HEAD(new_bh)->key = cpu_to_be32(blocknr);
AFFS_TAIL(sb, new_bh)->stype = cpu_to_be32(ST_FILE);
AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino);
affs_fix_checksum(sb, new_bh);
mark_buffer_dirty_inode(new_bh, inode);
tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
if (tmp)
affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp);
AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr);
affs_adjust_checksum(bh, blocknr - tmp);
mark_buffer_dirty_inode(bh, inode);
AFFS_I(inode)->i_extcnt++;
mark_inode_dirty(inode);
return new_bh;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 145 | 61.44% | 3 | 37.50% |
pre-git | pre-git | 91 | 38.56% | 5 | 62.50% |
| Total | 236 | 100.00% | 8 | 100.00% |
static inline struct buffer_head *
affs_get_extblock(struct inode *inode, u32 ext)
{
/* inline the simplest case: same extended block as last time */
struct buffer_head *bh = AFFS_I(inode)->i_ext_bh;
if (ext == AFFS_I(inode)->i_ext_last)
get_bh(bh);
else
/* we have to do more (not inlined) */
bh = affs_get_extblock_slow(inode, ext);
return bh;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 47 | 78.33% | 2 | 40.00% |
pre-git | pre-git | 12 | 20.00% | 2 | 40.00% |
roman zippel | roman zippel | 1 | 1.67% | 1 | 20.00% |
| Total | 60 | 100.00% | 5 | 100.00% |
static struct buffer_head *
affs_get_extblock_slow(struct inode *inode, u32 ext)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
u32 ext_key;
u32 lc_idx, lc_off, ac_idx;
u32 tmp, idx;
if (ext == AFFS_I(inode)->i_ext_last + 1) {
/* read the next extended block from the current one */
bh = AFFS_I(inode)->i_ext_bh;
ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
if (ext < AFFS_I(inode)->i_extcnt)
goto read_ext;
BUG_ON(ext > AFFS_I(inode)->i_extcnt);
bh = affs_alloc_extblock(inode, bh, ext);
if (IS_ERR(bh))
return bh;
goto store_ext;
}
if (ext == 0) {
/* we seek back to the file header block */
ext_key = inode->i_ino;
goto read_ext;
}
if (ext >= AFFS_I(inode)->i_extcnt) {
struct buffer_head *prev_bh;
/* allocate a new extended block */
BUG_ON(ext > AFFS_I(inode)->i_extcnt);
/* get previous extended block */
prev_bh = affs_get_extblock(inode, ext - 1);
if (IS_ERR(prev_bh))
return prev_bh;
bh = affs_alloc_extblock(inode, prev_bh, ext);
affs_brelse(prev_bh);
if (IS_ERR(bh))
return bh;
goto store_ext;
}
again:
/* check if there is an extended cache and whether it's large enough */
lc_idx = ext >> AFFS_I(inode)->i_lc_shift;
lc_off = ext & AFFS_I(inode)->i_lc_mask;
if (lc_idx >= AFFS_I(inode)->i_lc_size) {
int err;
err = affs_grow_extcache(inode, lc_idx);
if (err)
return ERR_PTR(err);
goto again;
}
/* every n'th key we find in the linear cache */
if (!lc_off) {
ext_key = AFFS_I(inode)->i_lc[lc_idx];
goto read_ext;
}
/* maybe it's still in the associative cache */
ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK;
if (AFFS_I(inode)->i_ac[ac_idx].ext == ext) {
ext_key = AFFS_I(inode)->i_ac[ac_idx].key;
goto read_ext;
}
/* try to find one of the previous extended blocks */
tmp = ext;
idx = ac_idx;
while (--tmp, --lc_off > 0) {
idx = (idx - 1) & AFFS_AC_MASK;
if (AFFS_I(inode)->i_ac[idx].ext == tmp) {
ext_key = AFFS_I(inode)->i_ac[idx].key;
goto find_ext;
}
}
/* fall back to the linear cache */
ext_key = AFFS_I(inode)->i_lc[lc_idx];
find_ext:
/* read all extended blocks until we find the one we need */
//unlock cache
do {
bh = affs_bread(sb, ext_key);
if (!bh)
goto err_bread;
ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
affs_brelse(bh);
tmp++;
} while (tmp < ext);
//lock cache
/* store it in the associative cache */
// recalculate ac_idx?
AFFS_I(inode)->i_ac[ac_idx].ext = ext;
AFFS_I(inode)->i_ac[ac_idx].key = ext_key;
read_ext:
/* finally read the right extended block */
//unlock cache
bh = affs_bread(sb, ext_key);
if (!bh)
goto err_bread;
//lock cache
store_ext:
/* release old cached extended block and store the new one */
affs_brelse(AFFS_I(inode)->i_ext_bh);
AFFS_I(inode)->i_ext_last = ext;
AFFS_I(inode)->i_ext_bh = bh;
get_bh(bh);
return bh;
err_bread:
affs_brelse(bh);
return ERR_PTR(-EIO);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 484 | 79.87% | 2 | 22.22% |
pre-git | pre-git | 115 | 18.98% | 5 | 55.56% |
fabian frederick | fabian frederick | 6 | 0.99% | 1 | 11.11% |
roman zippel | roman zippel | 1 | 0.17% | 1 | 11.11% |
| Total | 606 | 100.00% | 9 | 100.00% |
static int
affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *ext_bh;
u32 ext;
pr_debug("%s(%lu, %llu)\n", __func__, inode->i_ino,
(unsigned long long)block);
BUG_ON(block > (sector_t)0x7fffffffUL);
if (block >= AFFS_I(inode)->i_blkcnt) {
if (block > AFFS_I(inode)->i_blkcnt || !create)
goto err_big;
} else
create = 0;
//lock cache
affs_lock_ext(inode);
ext = (u32)block / AFFS_SB(sb)->s_hashsize;
block -= ext * AFFS_SB(sb)->s_hashsize;
ext_bh = affs_get_extblock(inode, ext);
if (IS_ERR(ext_bh))
goto err_ext;
map_bh(bh_result, sb, (sector_t)be32_to_cpu(AFFS_BLOCK(sb, ext_bh, block)));
if (create) {
u32 blocknr = affs_alloc_block(inode, ext_bh->b_blocknr);
if (!blocknr)
goto err_alloc;
set_buffer_new(bh_result);
AFFS_I(inode)->mmu_private += AFFS_SB(sb)->s_data_blksize;
AFFS_I(inode)->i_blkcnt++;
/* store new block */
if (bh_result->b_blocknr)
affs_warning(sb, "get_block",
"block already set (%llx)",
(unsigned long long)bh_result->b_blocknr);
AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr);
AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1);
affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1);
bh_result->b_blocknr = blocknr;
if (!block) {
/* insert first block into header block */
u32 tmp = be32_to_cpu(AFFS_HEAD(ext_bh)->first_data);
if (tmp)
affs_warning(sb, "get_block", "first block already set (%d)", tmp);
AFFS_HEAD(ext_bh)->first_data = cpu_to_be32(blocknr);
affs_adjust_checksum(ext_bh, blocknr - tmp);
}
}
affs_brelse(ext_bh);
//unlock cache
affs_unlock_ext(inode);
return 0;
err_big:
affs_error(inode->i_sb, "get_block", "strange block request %llu",
(unsigned long long)block);
return -EIO;
err_ext:
// unlock cache
affs_unlock_ext(inode);
return PTR_ERR(ext_bh);
err_alloc:
brelse(ext_bh);
clear_buffer_mapped(bh_result);
bh_result->b_bdev = NULL;
// unlock cache
affs_unlock_ext(inode);
return -ENOSPC;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 367 | 84.17% | 6 | 40.00% |
andrew morton | andrew morton | 22 | 5.05% | 2 | 13.33% |
pre-git | pre-git | 19 | 4.36% | 2 | 13.33% |
brian gerst | brian gerst | 9 | 2.06% | 1 | 6.67% |
geert uytterhoeven | geert uytterhoeven | 8 | 1.83% | 1 | 6.67% |
fabian frederick | fabian frederick | 8 | 1.83% | 2 | 13.33% |
julia lawall | julia lawall | 3 | 0.69% | 1 | 6.67% |
| Total | 436 | 100.00% | 15 | 100.00% |
static int affs_writepage(struct page *page, struct writeback_control *wbc)
{
return block_write_full_page(page, affs_get_block, wbc);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 19 | 73.08% | 1 | 50.00% |
andrew morton | andrew morton | 7 | 26.92% | 1 | 50.00% |
| Total | 26 | 100.00% | 2 | 100.00% |
static int affs_readpage(struct file *file, struct page *page)
{
return block_read_full_page(page, affs_get_block);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 24 | 100.00% | 1 | 100.00% |
| Total | 24 | 100.00% | 1 | 100.00% |
static void affs_write_failed(struct address_space *mapping, loff_t to)
{
struct inode *inode = mapping->host;
if (to > inode->i_size) {
truncate_pagecache(inode, inode->i_size);
affs_truncate(inode);
}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marco stornelli | marco stornelli | 47 | 100.00% | 1 | 100.00% |
| Total | 47 | 100.00% | 1 | 100.00% |
static ssize_t
affs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host;
size_t count = iov_iter_count(iter);
loff_t offset = iocb->ki_pos;
ssize_t ret;
if (iov_iter_rw(iter) == WRITE) {
loff_t size = offset + count;
if (AFFS_I(inode)->mmu_private < size)
return 0;
}
ret = blockdev_direct_IO(iocb, inode, iter, affs_get_block);
if (ret < 0 && iov_iter_rw(iter) == WRITE)
affs_write_failed(mapping, offset + count);
return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
fabian frederick | fabian frederick | 115 | 87.79% | 2 | 50.00% |
omar sandoval | omar sandoval | 9 | 6.87% | 1 | 25.00% |
christoph hellwig | christoph hellwig | 7 | 5.34% | 1 | 25.00% |
| Total | 131 | 100.00% | 4 | 100.00% |
static int affs_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
int ret;
*pagep = NULL;
ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
affs_get_block,
&AFFS_I(mapping->host)->mmu_private);
if (unlikely(ret))
affs_write_failed(mapping, pos + len);
return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
nick piggin | nick piggin | 36 | 38.30% | 1 | 20.00% |
linus torvalds | linus torvalds | 34 | 36.17% | 2 | 40.00% |
christoph hellwig | christoph hellwig | 21 | 22.34% | 1 | 20.00% |
marco stornelli | marco stornelli | 3 | 3.19% | 1 | 20.00% |
| Total | 94 | 100.00% | 5 | 100.00% |
static sector_t _affs_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,affs_get_block);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 22 | 91.67% | 1 | 50.00% |
andrew morton | andrew morton | 2 | 8.33% | 1 | 50.00% |
| Total | 24 | 100.00% | 2 | 100.00% |
const struct address_space_operations affs_aops = {
.readpage = affs_readpage,
.writepage = affs_writepage,
.write_begin = affs_write_begin,
.write_end = generic_write_end,
.direct_IO = affs_direct_IO,
.bmap = _affs_bmap
};
static inline struct buffer_head *
affs_bread_ino(struct inode *inode, int block, int create)
{
struct buffer_head *bh, tmp_bh;
int err;
tmp_bh.b_state = 0;
err = affs_get_block(inode, block, &tmp_bh, create);
if (!err) {
bh = affs_bread(inode->i_sb, tmp_bh.b_blocknr);
if (bh) {
bh->b_state |= tmp_bh.b_state;
return bh;
}
err = -EIO;
}
return ERR_PTR(err);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 98 | 100.00% | 1 | 100.00% |
| Total | 98 | 100.00% | 1 | 100.00% |
static inline struct buffer_head *
affs_getzeroblk_ino(struct inode *inode, int block)
{
struct buffer_head *bh, tmp_bh;
int err;
tmp_bh.b_state = 0;
err = affs_get_block(inode, block, &tmp_bh, 1);
if (!err) {
bh = affs_getzeroblk(inode->i_sb, tmp_bh.b_blocknr);
if (bh) {
bh->b_state |= tmp_bh.b_state;
return bh;
}
err = -EIO;
}
return ERR_PTR(err);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 95 | 100.00% | 1 | 100.00% |
| Total | 95 | 100.00% | 1 | 100.00% |
static inline struct buffer_head *
affs_getemptyblk_ino(struct inode *inode, int block)
{
struct buffer_head *bh, tmp_bh;
int err;
tmp_bh.b_state = 0;
err = affs_get_block(inode, block, &tmp_bh, 1);
if (!err) {
bh = affs_getemptyblk(inode->i_sb, tmp_bh.b_blocknr);
if (bh) {
bh->b_state |= tmp_bh.b_state;
return bh;
}
err = -EIO;
}
return ERR_PTR(err);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 95 | 100.00% | 1 | 100.00% |
| Total | 95 | 100.00% | 1 | 100.00% |
static int
affs_do_readpage_ofs(struct page *page, unsigned to)
{
struct inode *inode = page->mapping->host;
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
char *data;
unsigned pos = 0;
u32 bidx, boff, bsize;
u32 tmp;
pr_debug("%s(%lu, %ld, 0, %d)\n", __func__, inode->i_ino,
page->index, to);
BUG_ON(to > PAGE_SIZE);
bsize = AFFS_SB(sb)->s_data_blksize;
tmp = page->index << PAGE_SHIFT;
bidx = tmp / bsize;
boff = tmp % bsize;
while (pos < to) {
bh = affs_bread_ino(inode, bidx, 0);
if (IS_ERR(bh))
return PTR_ERR(bh);
tmp = min(bsize - boff, to - pos);
BUG_ON(pos + tmp > to || tmp > bsize);
data = kmap_atomic(page);
memcpy(data + pos, AFFS_DATA(bh) + boff, tmp);
kunmap_atomic(data);
affs_brelse(bh);
bidx++;
pos += tmp;
boff = 0;
}
flush_dcache_page(page);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
linus torvalds | linus torvalds | 164 | 74.21% | 2 | 18.18% |
roman zippel | roman zippel | 16 | 7.24% | 1 | 9.09% |
al viro | al viro | 12 | 5.43% | 1 | 9.09% |
fabian frederick | fabian frederick | 12 | 5.43% | 2 | 18.18% |
julia lawall | julia lawall | 6 | 2.71% | 1 | 9.09% |
andrew morton | andrew morton | 5 | 2.26% | 1 | 9.09% |
brian gerst | brian gerst | 3 | 1.36% | 1 | 9.09% |
kirill a. shutemov | kirill a. shutemov | 2 | 0.90% | 1 | 9.09% |
geert uytterhoeven | geert uytterhoeven | 1 | 0.45% | 1 | 9.09% |
| Total | 221 | 100.00% | 11 | 100.00% |
static int
affs_extent_file_ofs(struct inode *inode, u32 newsize)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh, *prev_bh;
u32 bidx, boff;
u32 size, bsize;
u32 tmp;
pr_debug("%s(%lu, %d)\n", __func__, inode->i_ino, newsize);
bsize = AFFS_SB(sb)->s_data_blksize;
bh = NULL;
size = AFFS_I(inode)->mmu_private;
bidx = size / bsize;
boff = size % bsize;
if (boff) {
bh = affs_bread_ino(inode, bidx, 0);
if (IS_ERR(bh))
return PTR_ERR(bh);
tmp = min(bsize - boff, newsize - size);
BUG_ON(boff + tmp > bsize || tmp > bsize);
memset(AFFS_DATA(bh) + boff, 0, tmp);
be32_add_cpu(&AFFS_DATA_HEAD(bh)->size, tmp);
affs_fix_checksum(sb, bh);
mark_buffer_dirty_inode(bh, inode);
size += tmp;
bidx++;
} else if (bidx) {
bh = affs_bread_ino(inode, bidx - 1, 0);
if (IS_ERR(bh))
return PTR_ERR(bh);
}
while (size < newsize) {
prev_bh = bh;
bh = affs_getzeroblk_ino(inode, bidx);
if (IS_ERR(bh))
goto out;
tmp = min(bsize, newsize - size);
BUG_ON(tmp > bsize);
AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino