4b6f5d20b0
This is a conversion to make the various file_operations structs in fs/ const. Basically a regexp job, with a few manual fixups The goal is both to increase correctness (harder to accidentally write to shared datastructures) and reducing the false sharing of cachelines with things that get dirty in .data (while .rodata is nicely read only and thus cache clean) Signed-off-by: Arjan van de Ven <arjan@infradead.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
263 lines
6.2 KiB
C
263 lines
6.2 KiB
C
/*
|
|
* dir.c
|
|
*
|
|
* PURPOSE
|
|
* Directory handling routines for the OSTA-UDF(tm) filesystem.
|
|
*
|
|
* 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-2004 Ben Fennema
|
|
*
|
|
* HISTORY
|
|
*
|
|
* 10/05/98 dgb Split directory operations into its own file
|
|
* Implemented directory reads via do_udf_readdir
|
|
* 10/06/98 Made directory operations work!
|
|
* 11/17/98 Rewrote directory to support ICBTAG_FLAG_AD_LONG
|
|
* 11/25/98 blf Rewrote directory handling (readdir+lookup) to support reading
|
|
* across blocks.
|
|
* 12/12/98 Split out the lookup code to namei.c. bulk of directory
|
|
* code now in directory.c:udf_fileident_read.
|
|
*/
|
|
|
|
#include "udfdecl.h"
|
|
|
|
#include <linux/string.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/buffer_head.h>
|
|
|
|
#include "udf_i.h"
|
|
#include "udf_sb.h"
|
|
|
|
/* Prototypes for file operations */
|
|
static int udf_readdir(struct file *, void *, filldir_t);
|
|
static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *);
|
|
|
|
/* readdir and lookup functions */
|
|
|
|
const struct file_operations udf_dir_operations = {
|
|
.read = generic_read_dir,
|
|
.readdir = udf_readdir,
|
|
.ioctl = udf_ioctl,
|
|
.fsync = udf_fsync_file,
|
|
};
|
|
|
|
/*
|
|
* udf_readdir
|
|
*
|
|
* PURPOSE
|
|
* Read a directory entry.
|
|
*
|
|
* DESCRIPTION
|
|
* Optional - sys_getdents() will return -ENOTDIR if this routine is not
|
|
* available.
|
|
*
|
|
* Refer to sys_getdents() in fs/readdir.c
|
|
* sys_getdents() -> .
|
|
*
|
|
* PRE-CONDITIONS
|
|
* filp Pointer to directory file.
|
|
* buf Pointer to directory entry buffer.
|
|
* filldir Pointer to filldir function.
|
|
*
|
|
* POST-CONDITIONS
|
|
* <return> >=0 on success.
|
|
*
|
|
* HISTORY
|
|
* July 1, 1997 - Andrew E. Mileski
|
|
* Written, tested, and released.
|
|
*/
|
|
|
|
int udf_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|
{
|
|
struct inode *dir = filp->f_dentry->d_inode;
|
|
int result;
|
|
|
|
lock_kernel();
|
|
|
|
if ( filp->f_pos == 0 )
|
|
{
|
|
if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0)
|
|
{
|
|
unlock_kernel();
|
|
return 0;
|
|
}
|
|
filp->f_pos ++;
|
|
}
|
|
|
|
result = do_udf_readdir(dir, filp, filldir, dirent);
|
|
unlock_kernel();
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent)
|
|
{
|
|
struct udf_fileident_bh fibh;
|
|
struct fileIdentDesc *fi=NULL;
|
|
struct fileIdentDesc cfi;
|
|
int block, iblock;
|
|
loff_t nf_pos = filp->f_pos - 1;
|
|
int flen;
|
|
char fname[UDF_NAME_LEN];
|
|
char *nameptr;
|
|
uint16_t liu;
|
|
uint8_t lfi;
|
|
loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
|
|
struct buffer_head * bh = NULL, * tmp, * bha[16];
|
|
kernel_lb_addr bloc, eloc;
|
|
uint32_t extoffset, elen, offset;
|
|
int i, num;
|
|
unsigned int dt_type;
|
|
|
|
if (nf_pos >= size)
|
|
return 0;
|
|
|
|
if (nf_pos == 0)
|
|
nf_pos = (udf_ext0_offset(dir) >> 2);
|
|
|
|
fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
|
|
if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
|
|
fibh.sbh = fibh.ebh = NULL;
|
|
else if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2),
|
|
&bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
|
|
{
|
|
offset >>= dir->i_sb->s_blocksize_bits;
|
|
block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
|
|
if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
|
|
{
|
|
if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
|
|
extoffset -= sizeof(short_ad);
|
|
else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
|
|
extoffset -= sizeof(long_ad);
|
|
}
|
|
else
|
|
offset = 0;
|
|
|
|
if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
|
|
{
|
|
udf_release_data(bh);
|
|
return -EIO;
|
|
}
|
|
|
|
if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
|
|
{
|
|
i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
|
|
if (i+offset > (elen >> dir->i_sb->s_blocksize_bits))
|
|
i = (elen >> dir->i_sb->s_blocksize_bits)-offset;
|
|
for (num=0; i>0; i--)
|
|
{
|
|
block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i);
|
|
tmp = udf_tgetblk(dir->i_sb, block);
|
|
if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
|
|
bha[num++] = tmp;
|
|
else
|
|
brelse(tmp);
|
|
}
|
|
if (num)
|
|
{
|
|
ll_rw_block(READA, num, bha);
|
|
for (i=0; i<num; i++)
|
|
brelse(bha[i]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
udf_release_data(bh);
|
|
return -ENOENT;
|
|
}
|
|
|
|
while ( nf_pos < size )
|
|
{
|
|
filp->f_pos = nf_pos + 1;
|
|
|
|
fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
|
|
|
|
if (!fi)
|
|
{
|
|
if (fibh.sbh != fibh.ebh)
|
|
udf_release_data(fibh.ebh);
|
|
udf_release_data(fibh.sbh);
|
|
udf_release_data(bh);
|
|
return 0;
|
|
}
|
|
|
|
liu = le16_to_cpu(cfi.lengthOfImpUse);
|
|
lfi = cfi.lengthFileIdent;
|
|
|
|
if (fibh.sbh == fibh.ebh)
|
|
nameptr = fi->fileIdent + liu;
|
|
else
|
|
{
|
|
int poffset; /* Unpaded ending offset */
|
|
|
|
poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi;
|
|
|
|
if (poffset >= lfi)
|
|
nameptr = (char *)(fibh.ebh->b_data + poffset - lfi);
|
|
else
|
|
{
|
|
nameptr = fname;
|
|
memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
|
|
memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset);
|
|
}
|
|
}
|
|
|
|
if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
|
|
{
|
|
if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) )
|
|
continue;
|
|
}
|
|
|
|
if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 )
|
|
{
|
|
if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) )
|
|
continue;
|
|
}
|
|
|
|
if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT )
|
|
{
|
|
iblock = parent_ino(filp->f_dentry);
|
|
flen = 2;
|
|
memcpy(fname, "..", flen);
|
|
dt_type = DT_DIR;
|
|
}
|
|
else
|
|
{
|
|
kernel_lb_addr tloc = lelb_to_cpu(cfi.icb.extLocation);
|
|
|
|
iblock = udf_get_lb_pblock(dir->i_sb, tloc, 0);
|
|
flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
|
|
dt_type = DT_UNKNOWN;
|
|
}
|
|
|
|
if (flen)
|
|
{
|
|
if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0)
|
|
{
|
|
if (fibh.sbh != fibh.ebh)
|
|
udf_release_data(fibh.ebh);
|
|
udf_release_data(fibh.sbh);
|
|
udf_release_data(bh);
|
|
return 0;
|
|
}
|
|
}
|
|
} /* end while */
|
|
|
|
filp->f_pos = nf_pos + 1;
|
|
|
|
if (fibh.sbh != fibh.ebh)
|
|
udf_release_data(fibh.ebh);
|
|
udf_release_data(fibh.sbh);
|
|
udf_release_data(bh);
|
|
|
|
return 0;
|
|
}
|