2005-04-17 02:20:36 +04:00
/*
* dir . c
*
* Copyright ( C ) 1995 , 1996 by Paal - Kr . Engstad and Volker Lendecke
* Copyright ( C ) 1997 by Volker Lendecke
*
* Please add a note about your changes to smbfs in the ChangeLog file .
*/
# include <linux/time.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/smp_lock.h>
# include <linux/ctype.h>
# include <linux/net.h>
Detach sched.h from mm.h
First thing mm.h does is including sched.h solely for can_do_mlock() inline
function which has "current" dereference inside. By dealing with can_do_mlock()
mm.h can be detached from sched.h which is good. See below, why.
This patch
a) removes unconditional inclusion of sched.h from mm.h
b) makes can_do_mlock() normal function in mm/mlock.c
c) exports can_do_mlock() to not break compilation
d) adds sched.h inclusions back to files that were getting it indirectly.
e) adds less bloated headers to some files (asm/signal.h, jiffies.h) that were
getting them indirectly
Net result is:
a) mm.h users would get less code to open, read, preprocess, parse, ... if
they don't need sched.h
b) sched.h stops being dependency for significant number of files:
on x86_64 allmodconfig touching sched.h results in recompile of 4083 files,
after patch it's only 3744 (-8.3%).
Cross-compile tested on
all arm defconfigs, all mips defconfigs, all powerpc defconfigs,
alpha alpha-up
arm
i386 i386-up i386-defconfig i386-allnoconfig
ia64 ia64-up
m68k
mips
parisc parisc-up
powerpc powerpc-up
s390 s390-up
sparc sparc-up
sparc64 sparc64-up
um-x86_64
x86_64 x86_64-up x86_64-defconfig x86_64-allnoconfig
as well as my two usual configs.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-21 01:22:52 +04:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/smb_fs.h>
# include <linux/smb_mount.h>
# include <linux/smbno.h>
# include "smb_debug.h"
# include "proto.h"
static int smb_readdir ( struct file * , void * , filldir_t ) ;
static int smb_dir_open ( struct inode * , struct file * ) ;
static struct dentry * smb_lookup ( struct inode * , struct dentry * , struct nameidata * ) ;
static int smb_create ( struct inode * , struct dentry * , int , struct nameidata * ) ;
static int smb_mkdir ( struct inode * , struct dentry * , int ) ;
static int smb_rmdir ( struct inode * , struct dentry * ) ;
static int smb_unlink ( struct inode * , struct dentry * ) ;
static int smb_rename ( struct inode * , struct dentry * ,
struct inode * , struct dentry * ) ;
static int smb_make_node ( struct inode * , struct dentry * , int , dev_t ) ;
static int smb_link ( struct dentry * , struct inode * , struct dentry * ) ;
2006-03-28 13:56:42 +04:00
const struct file_operations smb_dir_operations =
2005-04-17 02:20:36 +04:00
{
. read = generic_read_dir ,
. readdir = smb_readdir ,
. ioctl = smb_ioctl ,
. open = smb_dir_open ,
} ;
2007-02-12 11:55:40 +03:00
const struct inode_operations smb_dir_inode_operations =
2005-04-17 02:20:36 +04:00
{
. create = smb_create ,
. lookup = smb_lookup ,
. unlink = smb_unlink ,
. mkdir = smb_mkdir ,
. rmdir = smb_rmdir ,
. rename = smb_rename ,
. getattr = smb_getattr ,
. setattr = smb_notify_change ,
} ;
2007-02-12 11:55:40 +03:00
const struct inode_operations smb_dir_inode_operations_unix =
2005-04-17 02:20:36 +04:00
{
. create = smb_create ,
. lookup = smb_lookup ,
. unlink = smb_unlink ,
. mkdir = smb_mkdir ,
. rmdir = smb_rmdir ,
. rename = smb_rename ,
. getattr = smb_getattr ,
. setattr = smb_notify_change ,
. symlink = smb_symlink ,
. mknod = smb_make_node ,
. link = smb_link ,
} ;
/*
* Read a directory , using filldir to fill the dirent memory .
* smb_proc_readdir does the actual reading from the smb server .
*
* The cache code is almost directly taken from ncpfs
*/
static int
smb_readdir ( struct file * filp , void * dirent , filldir_t filldir )
{
2006-12-08 13:37:39 +03:00
struct dentry * dentry = filp - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
struct inode * dir = dentry - > d_inode ;
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
union smb_dir_cache * cache = NULL ;
struct smb_cache_control ctl ;
struct page * page = NULL ;
int result ;
ctl . page = NULL ;
ctl . cache = NULL ;
VERBOSE ( " reading %s/%s, f_pos=%d \n " ,
DENTRY_PATH ( dentry ) , ( int ) filp - > f_pos ) ;
result = 0 ;
lock_kernel ( ) ;
switch ( ( unsigned int ) filp - > f_pos ) {
case 0 :
if ( filldir ( dirent , " . " , 1 , 0 , dir - > i_ino , DT_DIR ) < 0 )
goto out ;
filp - > f_pos = 1 ;
/* fallthrough */
case 1 :
if ( filldir ( dirent , " .. " , 2 , 1 , parent_ino ( dentry ) , DT_DIR ) < 0 )
goto out ;
filp - > f_pos = 2 ;
}
/*
* Make sure our inode is up - to - date .
*/
result = smb_revalidate_inode ( dentry ) ;
if ( result )
goto out ;
page = grab_cache_page ( & dir - > i_data , 0 ) ;
if ( ! page )
goto read_really ;
ctl . cache = cache = kmap ( page ) ;
ctl . head = cache - > head ;
if ( ! PageUptodate ( page ) | | ! ctl . head . eof ) {
VERBOSE ( " %s/%s, page uptodate=%d, eof=%d \n " ,
DENTRY_PATH ( dentry ) , PageUptodate ( page ) , ctl . head . eof ) ;
goto init_cache ;
}
if ( filp - > f_pos = = 2 ) {
if ( jiffies - ctl . head . time > = SMB_MAX_AGE ( server ) )
goto init_cache ;
/*
* N . B . ncpfs checks mtime of dentry too here , we don ' t .
* 1. common smb servers do not update mtime on dir changes
* 2. it requires an extra smb request
* ( revalidate has the same timeout as ctl . head . time )
*
* Instead smbfs invalidates its own cache on local changes
* and remote changes are not seen until timeout .
*/
}
if ( filp - > f_pos > ctl . head . end )
goto finished ;
ctl . fpos = filp - > f_pos + ( SMB_DIRCACHE_START - 2 ) ;
ctl . ofs = ctl . fpos / SMB_DIRCACHE_SIZE ;
ctl . idx = ctl . fpos % SMB_DIRCACHE_SIZE ;
for ( ; ; ) {
if ( ctl . ofs ! = 0 ) {
ctl . page = find_lock_page ( & dir - > i_data , ctl . ofs ) ;
if ( ! ctl . page )
goto invalid_cache ;
ctl . cache = kmap ( ctl . page ) ;
if ( ! PageUptodate ( ctl . page ) )
goto invalid_cache ;
}
while ( ctl . idx < SMB_DIRCACHE_SIZE ) {
struct dentry * dent ;
int res ;
dent = smb_dget_fpos ( ctl . cache - > dentry [ ctl . idx ] ,
dentry , filp - > f_pos ) ;
if ( ! dent )
goto invalid_cache ;
res = filldir ( dirent , dent - > d_name . name ,
dent - > d_name . len , filp - > f_pos ,
dent - > d_inode - > i_ino , DT_UNKNOWN ) ;
dput ( dent ) ;
if ( res )
goto finished ;
filp - > f_pos + = 1 ;
ctl . idx + = 1 ;
if ( filp - > f_pos > ctl . head . end )
goto finished ;
}
if ( ctl . page ) {
kunmap ( ctl . page ) ;
SetPageUptodate ( ctl . page ) ;
unlock_page ( ctl . page ) ;
page_cache_release ( ctl . page ) ;
ctl . page = NULL ;
}
ctl . idx = 0 ;
ctl . ofs + = 1 ;
}
invalid_cache :
if ( ctl . page ) {
kunmap ( ctl . page ) ;
unlock_page ( ctl . page ) ;
page_cache_release ( ctl . page ) ;
ctl . page = NULL ;
}
ctl . cache = cache ;
init_cache :
smb_invalidate_dircache_entries ( dentry ) ;
ctl . head . time = jiffies ;
ctl . head . eof = 0 ;
ctl . fpos = 2 ;
ctl . ofs = 0 ;
ctl . idx = SMB_DIRCACHE_START ;
ctl . filled = 0 ;
ctl . valid = 1 ;
read_really :
result = server - > ops - > readdir ( filp , dirent , filldir , & ctl ) ;
2006-02-01 14:04:39 +03:00
if ( result = = - ERESTARTSYS & & page )
ClearPageUptodate ( page ) ;
2005-04-17 02:20:36 +04:00
if ( ctl . idx = = - 1 )
goto invalid_cache ; /* retry */
ctl . head . end = ctl . fpos - 1 ;
ctl . head . eof = ctl . valid ;
finished :
if ( page ) {
cache - > head = ctl . head ;
kunmap ( page ) ;
2006-02-01 14:04:39 +03:00
if ( result ! = - ERESTARTSYS )
SetPageUptodate ( page ) ;
2005-04-17 02:20:36 +04:00
unlock_page ( page ) ;
page_cache_release ( page ) ;
}
if ( ctl . page ) {
kunmap ( ctl . page ) ;
SetPageUptodate ( ctl . page ) ;
unlock_page ( ctl . page ) ;
page_cache_release ( ctl . page ) ;
}
out :
unlock_kernel ( ) ;
return result ;
}
static int
smb_dir_open ( struct inode * dir , struct file * file )
{
2006-12-08 13:37:39 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
struct smb_sb_info * server ;
int error = 0 ;
VERBOSE ( " (%s/%s) \n " , dentry - > d_parent - > d_name . name ,
2006-12-08 13:37:39 +03:00
file - > f_path . dentry - > d_name . name ) ;
2005-04-17 02:20:36 +04:00
/*
* Directory timestamps in the core protocol aren ' t updated
* when a file is added , so we give them a very short TTL .
*/
lock_kernel ( ) ;
server = server_from_dentry ( dentry ) ;
if ( server - > opt . protocol < SMB_PROTOCOL_LANMAN2 ) {
unsigned long age = jiffies - SMB_I ( dir ) - > oldmtime ;
if ( age > 2 * HZ )
smb_invalid_dir_cache ( dir ) ;
}
/*
* Note : in order to allow the smbmount process to open the
* mount point , we only revalidate if the connection is valid or
* if the process is trying to access something other than the root .
*/
if ( server - > state = = CONN_VALID | | ! IS_ROOT ( dentry ) )
error = smb_revalidate_inode ( dentry ) ;
unlock_kernel ( ) ;
return error ;
}
/*
* Dentry operations routines
*/
static int smb_lookup_validate ( struct dentry * , struct nameidata * ) ;
static int smb_hash_dentry ( struct dentry * , struct qstr * ) ;
static int smb_compare_dentry ( struct dentry * , struct qstr * , struct qstr * ) ;
static int smb_delete_dentry ( struct dentry * ) ;
static struct dentry_operations smbfs_dentry_operations =
{
. d_revalidate = smb_lookup_validate ,
. d_hash = smb_hash_dentry ,
. d_compare = smb_compare_dentry ,
. d_delete = smb_delete_dentry ,
} ;
static struct dentry_operations smbfs_dentry_operations_case =
{
. d_revalidate = smb_lookup_validate ,
. d_delete = smb_delete_dentry ,
} ;
/*
* This is the callback when the dcache has a lookup hit .
*/
static int
smb_lookup_validate ( struct dentry * dentry , struct nameidata * nd )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
struct inode * inode = dentry - > d_inode ;
unsigned long age = jiffies - dentry - > d_time ;
int valid ;
/*
* The default validation is based on dentry age :
* we believe in dentries for a few seconds . ( But each
* successful server lookup renews the timestamp . )
*/
valid = ( age < = SMB_MAX_AGE ( server ) ) ;
# ifdef SMBFS_DEBUG_VERBOSE
if ( ! valid )
VERBOSE ( " %s/%s not valid, age=%lu \n " ,
DENTRY_PATH ( dentry ) , age ) ;
# endif
if ( inode ) {
lock_kernel ( ) ;
if ( is_bad_inode ( inode ) ) {
PARANOIA ( " %s/%s has dud inode \n " , DENTRY_PATH ( dentry ) ) ;
valid = 0 ;
} else if ( ! valid )
valid = ( smb_revalidate_inode ( dentry ) = = 0 ) ;
unlock_kernel ( ) ;
} else {
/*
* What should we do for negative dentries ?
*/
}
return valid ;
}
static int
smb_hash_dentry ( struct dentry * dir , struct qstr * this )
{
unsigned long hash ;
int i ;
hash = init_name_hash ( ) ;
for ( i = 0 ; i < this - > len ; i + + )
hash = partial_name_hash ( tolower ( this - > name [ i ] ) , hash ) ;
this - > hash = end_name_hash ( hash ) ;
return 0 ;
}
static int
smb_compare_dentry ( struct dentry * dir , struct qstr * a , struct qstr * b )
{
int i , result = 1 ;
if ( a - > len ! = b - > len )
goto out ;
for ( i = 0 ; i < a - > len ; i + + ) {
if ( tolower ( a - > name [ i ] ) ! = tolower ( b - > name [ i ] ) )
goto out ;
}
result = 0 ;
out :
return result ;
}
/*
* This is the callback from dput ( ) when d_count is going to 0.
* We use this to unhash dentries with bad inodes .
*/
static int
smb_delete_dentry ( struct dentry * dentry )
{
if ( dentry - > d_inode ) {
if ( is_bad_inode ( dentry - > d_inode ) ) {
PARANOIA ( " bad inode, unhashing %s/%s \n " ,
DENTRY_PATH ( dentry ) ) ;
return 1 ;
}
} else {
/* N.B. Unhash negative dentries? */
}
return 0 ;
}
/*
* Initialize a new dentry
*/
void
smb_new_dentry ( struct dentry * dentry )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
if ( server - > mnt - > flags & SMB_MOUNT_CASE )
dentry - > d_op = & smbfs_dentry_operations_case ;
else
dentry - > d_op = & smbfs_dentry_operations ;
dentry - > d_time = jiffies ;
}
/*
* Whenever a lookup succeeds , we know the parent directories
* are all valid , so we want to update the dentry timestamps .
* N . B . Move this to dcache ?
*/
void
smb_renew_times ( struct dentry * dentry )
{
dget ( dentry ) ;
spin_lock ( & dentry - > d_lock ) ;
for ( ; ; ) {
struct dentry * parent ;
dentry - > d_time = jiffies ;
if ( IS_ROOT ( dentry ) )
break ;
parent = dentry - > d_parent ;
dget ( parent ) ;
spin_unlock ( & dentry - > d_lock ) ;
dput ( dentry ) ;
dentry = parent ;
spin_lock ( & dentry - > d_lock ) ;
}
spin_unlock ( & dentry - > d_lock ) ;
dput ( dentry ) ;
}
static struct dentry *
smb_lookup ( struct inode * dir , struct dentry * dentry , struct nameidata * nd )
{
struct smb_fattr finfo ;
struct inode * inode ;
int error ;
struct smb_sb_info * server ;
error = - ENAMETOOLONG ;
if ( dentry - > d_name . len > SMB_MAXNAMELEN )
goto out ;
2006-05-15 20:43:57 +04:00
/* Do not allow lookup of names with backslashes in */
error = - EINVAL ;
if ( memchr ( dentry - > d_name . name , ' \\ ' , dentry - > d_name . len ) )
goto out ;
2005-04-17 02:20:36 +04:00
lock_kernel ( ) ;
error = smb_proc_getattr ( dentry , & finfo ) ;
# ifdef SMBFS_PARANOIA
if ( error & & error ! = - ENOENT )
PARANOIA ( " find %s/%s failed, error=%d \n " ,
DENTRY_PATH ( dentry ) , error ) ;
# endif
inode = NULL ;
if ( error = = - ENOENT )
goto add_entry ;
if ( ! error ) {
error = - EACCES ;
finfo . f_ino = iunique ( dentry - > d_sb , 2 ) ;
inode = smb_iget ( dir - > i_sb , & finfo ) ;
if ( inode ) {
add_entry :
server = server_from_dentry ( dentry ) ;
if ( server - > mnt - > flags & SMB_MOUNT_CASE )
dentry - > d_op = & smbfs_dentry_operations_case ;
else
dentry - > d_op = & smbfs_dentry_operations ;
d_add ( dentry , inode ) ;
smb_renew_times ( dentry ) ;
error = 0 ;
}
}
unlock_kernel ( ) ;
out :
return ERR_PTR ( error ) ;
}
/*
* This code is common to all routines creating a new inode .
*/
static int
smb_instantiate ( struct dentry * dentry , __u16 fileid , int have_id )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
struct inode * inode ;
int error ;
struct smb_fattr fattr ;
VERBOSE ( " file %s/%s, fileid=%u \n " , DENTRY_PATH ( dentry ) , fileid ) ;
error = smb_proc_getattr ( dentry , & fattr ) ;
if ( error )
goto out_close ;
smb_renew_times ( dentry ) ;
fattr . f_ino = iunique ( dentry - > d_sb , 2 ) ;
inode = smb_iget ( dentry - > d_sb , & fattr ) ;
if ( ! inode )
goto out_no_inode ;
if ( have_id ) {
struct smb_inode_info * ei = SMB_I ( inode ) ;
ei - > fileid = fileid ;
ei - > access = SMB_O_RDWR ;
ei - > open = server - > generation ;
}
d_instantiate ( dentry , inode ) ;
out :
return error ;
out_no_inode :
error = - EACCES ;
out_close :
if ( have_id ) {
PARANOIA ( " %s/%s failed, error=%d, closing %u \n " ,
DENTRY_PATH ( dentry ) , error , fileid ) ;
smb_close_fileid ( dentry , fileid ) ;
}
goto out ;
}
/* N.B. How should the mode argument be used? */
static int
smb_create ( struct inode * dir , struct dentry * dentry , int mode ,
struct nameidata * nd )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
__u16 fileid ;
int error ;
struct iattr attr ;
VERBOSE ( " creating %s/%s, mode=%d \n " , DENTRY_PATH ( dentry ) , mode ) ;
lock_kernel ( ) ;
smb_invalid_dir_cache ( dir ) ;
error = smb_proc_create ( dentry , 0 , get_seconds ( ) , & fileid ) ;
if ( ! error ) {
if ( server - > opt . capabilities & SMB_CAP_UNIX ) {
/* Set attributes for new file */
attr . ia_valid = ATTR_MODE ;
attr . ia_mode = mode ;
error = smb_proc_setattr_unix ( dentry , & attr , 0 , 0 ) ;
}
error = smb_instantiate ( dentry , fileid , 1 ) ;
} else {
PARANOIA ( " %s/%s failed, error=%d \n " ,
DENTRY_PATH ( dentry ) , error ) ;
}
unlock_kernel ( ) ;
return error ;
}
/* N.B. How should the mode argument be used? */
static int
smb_mkdir ( struct inode * dir , struct dentry * dentry , int mode )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
int error ;
struct iattr attr ;
lock_kernel ( ) ;
smb_invalid_dir_cache ( dir ) ;
error = smb_proc_mkdir ( dentry ) ;
if ( ! error ) {
if ( server - > opt . capabilities & SMB_CAP_UNIX ) {
/* Set attributes for new directory */
attr . ia_valid = ATTR_MODE ;
attr . ia_mode = mode ;
error = smb_proc_setattr_unix ( dentry , & attr , 0 , 0 ) ;
}
error = smb_instantiate ( dentry , 0 , 0 ) ;
}
unlock_kernel ( ) ;
return error ;
}
static int
smb_rmdir ( struct inode * dir , struct dentry * dentry )
{
struct inode * inode = dentry - > d_inode ;
int error ;
/*
* Close the directory if it ' s open .
*/
lock_kernel ( ) ;
smb_close ( inode ) ;
/*
* Check that nobody else is using the directory . .
*/
error = - EBUSY ;
if ( ! d_unhashed ( dentry ) )
goto out ;
smb_invalid_dir_cache ( dir ) ;
error = smb_proc_rmdir ( dentry ) ;
out :
unlock_kernel ( ) ;
return error ;
}
static int
smb_unlink ( struct inode * dir , struct dentry * dentry )
{
int error ;
/*
* Close the file if it ' s open .
*/
lock_kernel ( ) ;
smb_close ( dentry - > d_inode ) ;
smb_invalid_dir_cache ( dir ) ;
error = smb_proc_unlink ( dentry ) ;
if ( ! error )
smb_renew_times ( dentry ) ;
unlock_kernel ( ) ;
return error ;
}
static int
smb_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
int error ;
/*
* Close any open files , and check whether to delete the
* target before attempting the rename .
*/
lock_kernel ( ) ;
if ( old_dentry - > d_inode )
smb_close ( old_dentry - > d_inode ) ;
if ( new_dentry - > d_inode ) {
smb_close ( new_dentry - > d_inode ) ;
error = smb_proc_unlink ( new_dentry ) ;
if ( error ) {
VERBOSE ( " unlink %s/%s, error=%d \n " ,
DENTRY_PATH ( new_dentry ) , error ) ;
goto out ;
}
/* FIXME */
d_delete ( new_dentry ) ;
}
smb_invalid_dir_cache ( old_dir ) ;
smb_invalid_dir_cache ( new_dir ) ;
error = smb_proc_mv ( old_dentry , new_dentry ) ;
if ( ! error ) {
smb_renew_times ( old_dentry ) ;
smb_renew_times ( new_dentry ) ;
}
out :
unlock_kernel ( ) ;
return error ;
}
/*
* FIXME : samba servers won ' t let you create device nodes unless uid / gid
* matches the connection credentials ( and we don ' t know which those are . . . )
*/
static int
smb_make_node ( struct inode * dir , struct dentry * dentry , int mode , dev_t dev )
{
int error ;
struct iattr attr ;
attr . ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID ;
attr . ia_mode = mode ;
2008-11-14 02:39:18 +03:00
current_euid_egid ( & attr . ia_uid , & attr . ia_gid ) ;
2005-04-17 02:20:36 +04:00
if ( ! new_valid_dev ( dev ) )
return - EINVAL ;
smb_invalid_dir_cache ( dir ) ;
error = smb_proc_setattr_unix ( dentry , & attr , MAJOR ( dev ) , MINOR ( dev ) ) ;
if ( ! error ) {
error = smb_instantiate ( dentry , 0 , 0 ) ;
}
return error ;
}
/*
* dentry = existing file
* new_dentry = new file
*/
static int
smb_link ( struct dentry * dentry , struct inode * dir , struct dentry * new_dentry )
{
int error ;
DEBUG1 ( " smb_link old=%s/%s new=%s/%s \n " ,
DENTRY_PATH ( dentry ) , DENTRY_PATH ( new_dentry ) ) ;
smb_invalid_dir_cache ( dir ) ;
error = smb_proc_link ( server_from_dentry ( dentry ) , dentry , new_dentry ) ;
if ( ! error ) {
smb_renew_times ( dentry ) ;
error = smb_instantiate ( new_dentry , 0 , 0 ) ;
}
return error ;
}