2005-04-17 02:20:36 +04:00
/*
* fs / cifs / dir . c
*
* vfs operations that deal with dentries
2007-06-05 22:30:44 +04:00
*
2009-02-20 07:32:45 +03:00
* Copyright ( C ) International Business Machines Corp . , 2002 , 2009
2005-04-17 02:20:36 +04:00
* Author ( s ) : Steve French ( sfrench @ us . ibm . com )
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/fs.h>
# include <linux/stat.h>
# include <linux/slab.h>
# include <linux/namei.h>
# include "cifsfs.h"
# include "cifspdu.h"
# include "cifsglob.h"
# include "cifsproto.h"
# include "cifs_debug.h"
# include "cifs_fs_sb.h"
2007-02-27 08:35:17 +03:00
static void
2005-04-17 02:20:36 +04:00
renew_parental_timestamps ( struct dentry * direntry )
{
2007-06-05 22:30:44 +04:00
/* BB check if there is a way to get the kernel to do this or if we
really need this */
2005-04-17 02:20:36 +04:00
do {
direntry - > d_time = jiffies ;
direntry = direntry - > d_parent ;
2007-06-05 22:30:44 +04:00
} while ( ! IS_ROOT ( direntry ) ) ;
2005-04-17 02:20:36 +04:00
}
/* Note: caller must free return buffer */
char *
build_path_from_dentry ( struct dentry * direntry )
{
struct dentry * temp ;
2006-09-21 11:02:52 +04:00
int namelen ;
int pplen ;
2008-05-15 05:50:56 +04:00
int dfsplen ;
2005-04-17 02:20:36 +04:00
char * full_path ;
2006-03-10 01:21:45 +03:00
char dirsep ;
2008-05-15 05:50:56 +04:00
struct cifs_sb_info * cifs_sb ;
2005-04-17 02:20:36 +04:00
2007-06-05 22:30:44 +04:00
if ( direntry = = NULL )
2005-04-17 02:20:36 +04:00
return NULL ; /* not much we can do if dentry is freed and
we need to reopen the file after it was closed implicitly
when the server crashed */
2008-05-15 05:50:56 +04:00
cifs_sb = CIFS_SB ( direntry - > d_sb ) ;
dirsep = CIFS_DIR_SEP ( cifs_sb ) ;
pplen = cifs_sb - > prepathlen ;
if ( cifs_sb - > tcon & & ( cifs_sb - > tcon - > Flags & SMB_SHARE_IS_IN_DFS ) )
dfsplen = strnlen ( cifs_sb - > tcon - > treeName , MAX_TREE_SIZE + 1 ) ;
else
dfsplen = 0 ;
2005-04-17 02:20:36 +04:00
cifs_bp_rename_retry :
2008-05-15 05:50:56 +04:00
namelen = pplen + dfsplen ;
2005-04-17 02:20:36 +04:00
for ( temp = direntry ; ! IS_ROOT ( temp ) ; ) {
namelen + = ( 1 + temp - > d_name . len ) ;
temp = temp - > d_parent ;
2007-06-05 22:30:44 +04:00
if ( temp = = NULL ) {
cERROR ( 1 , ( " corrupt dentry " ) ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
}
full_path = kmalloc ( namelen + 1 , GFP_KERNEL ) ;
2007-06-05 22:30:44 +04:00
if ( full_path = = NULL )
2005-04-17 02:20:36 +04:00
return full_path ;
full_path [ namelen ] = 0 ; /* trailing null */
for ( temp = direntry ; ! IS_ROOT ( temp ) ; ) {
namelen - = 1 + temp - > d_name . len ;
if ( namelen < 0 ) {
break ;
} else {
2005-08-30 22:32:14 +04:00
full_path [ namelen ] = dirsep ;
2005-04-17 02:20:36 +04:00
strncpy ( full_path + namelen + 1 , temp - > d_name . name ,
temp - > d_name . len ) ;
2006-09-21 11:02:52 +04:00
cFYI ( 0 , ( " name: %s " , full_path + namelen ) ) ;
2005-04-17 02:20:36 +04:00
}
temp = temp - > d_parent ;
2007-06-05 22:30:44 +04:00
if ( temp = = NULL ) {
cERROR ( 1 , ( " corrupt dentry " ) ) ;
2005-04-17 02:20:36 +04:00
kfree ( full_path ) ;
return NULL ;
}
}
2008-05-15 05:50:56 +04:00
if ( namelen ! = pplen + dfsplen ) {
2005-04-17 02:20:36 +04:00
cERROR ( 1 ,
2006-09-21 11:02:52 +04:00
( " did not end path lookup where expected namelen is %d " ,
2005-04-17 02:20:36 +04:00
namelen ) ) ;
2007-06-05 22:30:44 +04:00
/* presumably this is only possible if racing with a rename
2005-04-17 02:20:36 +04:00
of one of the parent directories ( we can not lock the dentries
above us to prevent this , but retrying should be harmless ) */
kfree ( full_path ) ;
goto cifs_bp_rename_retry ;
}
2006-09-21 11:02:52 +04:00
/* DIR_SEP already set for byte 0 / vs \ but not for
subsequent slashes in prepath which currently must
be entered the right way - not sure if there is an alternative
since the ' \ ' is a valid posix character so we can not switch
those safely to ' / ' if any are found in the middle of the prepath */
/* BB test paths to Windows with '/' in the midst of prepath */
2008-05-15 05:50:56 +04:00
if ( dfsplen ) {
strncpy ( full_path , cifs_sb - > tcon - > treeName , dfsplen ) ;
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS ) {
int i ;
for ( i = 0 ; i < dfsplen ; i + + ) {
if ( full_path [ i ] = = ' \\ ' )
full_path [ i ] = ' / ' ;
}
}
}
strncpy ( full_path + dfsplen , CIFS_SB ( direntry - > d_sb ) - > prepath , pplen ) ;
2005-04-17 02:20:36 +04:00
return full_path ;
}
2009-04-09 05:14:32 +04:00
static void
cifs_fill_fileinfo ( struct inode * newinode , __u16 fileHandle ,
struct cifsTconInfo * tcon , bool write_only )
{
int oplock = 0 ;
struct cifsFileInfo * pCifsFile ;
struct cifsInodeInfo * pCifsInode ;
pCifsFile = kzalloc ( sizeof ( struct cifsFileInfo ) , GFP_KERNEL ) ;
if ( pCifsFile = = NULL )
return ;
if ( oplockEnabled )
oplock = REQ_OPLOCK ;
pCifsFile - > netfid = fileHandle ;
pCifsFile - > pid = current - > tgid ;
pCifsFile - > pInode = newinode ;
pCifsFile - > invalidHandle = false ;
2009-04-12 22:18:40 +04:00
pCifsFile - > closePend = false ;
2009-04-09 05:14:32 +04:00
mutex_init ( & pCifsFile - > fh_mutex ) ;
mutex_init ( & pCifsFile - > lock_mutex ) ;
INIT_LIST_HEAD ( & pCifsFile - > llist ) ;
atomic_set ( & pCifsFile - > wrtPending , 0 ) ;
/* set the following in open now
pCifsFile - > pfile = file ; */
write_lock ( & GlobalSMBSeslock ) ;
list_add ( & pCifsFile - > tlist , & tcon - > openFileList ) ;
pCifsInode = CIFS_I ( newinode ) ;
if ( pCifsInode ) {
/* if readable file instance put first in list*/
2009-04-12 22:18:40 +04:00
if ( write_only )
2009-04-09 05:14:32 +04:00
list_add_tail ( & pCifsFile - > flist ,
& pCifsInode - > openFileList ) ;
2009-04-12 22:18:40 +04:00
else
list_add ( & pCifsFile - > flist , & pCifsInode - > openFileList ) ;
2009-04-09 05:14:32 +04:00
if ( ( oplock & 0xF ) = = OPLOCK_EXCLUSIVE ) {
pCifsInode - > clientCanCacheAll = true ;
pCifsInode - > clientCanCacheRead = true ;
2009-04-12 22:18:40 +04:00
cFYI ( 1 , ( " Exclusive Oplock inode %p " , newinode ) ) ;
2009-04-09 05:14:32 +04:00
} else if ( ( oplock & 0xF ) = = OPLOCK_READ )
2009-04-12 22:18:40 +04:00
pCifsInode - > clientCanCacheRead = true ;
2009-04-09 05:14:32 +04:00
}
write_unlock ( & GlobalSMBSeslock ) ;
}
2009-02-23 23:43:11 +03:00
int cifs_posix_open ( char * full_path , struct inode * * pinode ,
2009-02-20 07:32:45 +03:00
struct super_block * sb , int mode , int oflags ,
int * poplock , __u16 * pnetfid , int xid )
{
int rc ;
__u32 oplock ;
2009-04-09 05:14:32 +04:00
bool write_only = false ;
2009-02-20 07:32:45 +03:00
FILE_UNIX_BASIC_INFO * presp_data ;
__u32 posix_flags = 0 ;
struct cifs_sb_info * cifs_sb = CIFS_SB ( sb ) ;
cFYI ( 1 , ( " posix open %s " , full_path ) ) ;
presp_data = kzalloc ( sizeof ( FILE_UNIX_BASIC_INFO ) , GFP_KERNEL ) ;
if ( presp_data = = NULL )
return - ENOMEM ;
/* So far cifs posix extensions can only map the following flags.
There are other valid fmode oflags such as FMODE_LSEEK , FMODE_PREAD , but
so far we do not seem to need them , and we can treat them as local only */
if ( ( oflags & ( FMODE_READ | FMODE_WRITE ) ) = =
( FMODE_READ | FMODE_WRITE ) )
posix_flags = SMB_O_RDWR ;
else if ( oflags & FMODE_READ )
posix_flags = SMB_O_RDONLY ;
else if ( oflags & FMODE_WRITE )
posix_flags = SMB_O_WRONLY ;
if ( oflags & O_CREAT )
posix_flags | = SMB_O_CREAT ;
if ( oflags & O_EXCL )
posix_flags | = SMB_O_EXCL ;
if ( oflags & O_TRUNC )
posix_flags | = SMB_O_TRUNC ;
if ( oflags & O_APPEND )
posix_flags | = SMB_O_APPEND ;
if ( oflags & O_SYNC )
posix_flags | = SMB_O_SYNC ;
if ( oflags & O_DIRECTORY )
posix_flags | = SMB_O_DIRECTORY ;
if ( oflags & O_NOFOLLOW )
posix_flags | = SMB_O_NOFOLLOW ;
if ( oflags & O_DIRECT )
posix_flags | = SMB_O_DIRECT ;
2009-04-09 05:14:32 +04:00
if ( ! ( oflags & FMODE_READ ) )
write_only = true ;
2009-02-20 07:32:45 +03:00
rc = CIFSPOSIXCreate ( xid , cifs_sb - > tcon , posix_flags , mode ,
pnetfid , presp_data , & oplock , full_path ,
cifs_sb - > local_nls , cifs_sb - > mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
if ( rc )
goto posix_open_ret ;
if ( presp_data - > Type = = cpu_to_le32 ( - 1 ) )
goto posix_open_ret ; /* open ok, caller does qpathinfo */
/* get new inode and set it up */
if ( ! pinode )
goto posix_open_ret ; /* caller does not need info */
2009-04-01 09:22:00 +04:00
if ( * pinode = = NULL ) {
__u64 unique_id = le64_to_cpu ( presp_data - > UniqueId ) ;
* pinode = cifs_new_inode ( sb , & unique_id ) ;
}
2009-02-23 23:43:11 +03:00
/* else an inode was passed in. Update its info, don't create one */
2009-02-20 07:32:45 +03:00
/* We do not need to close the file if new_inode fails since
the caller will retry qpathinfo as long as inode is null */
if ( * pinode = = NULL )
goto posix_open_ret ;
posix_fill_in_inode ( * pinode , presp_data , 1 ) ;
2009-04-09 05:14:32 +04:00
cifs_fill_fileinfo ( * pinode , * pnetfid , cifs_sb - > tcon , write_only ) ;
2009-02-20 07:32:45 +03:00
posix_open_ret :
kfree ( presp_data ) ;
return rc ;
}
2009-01-19 05:38:35 +03:00
static void setup_cifs_dentry ( struct cifsTconInfo * tcon ,
struct dentry * direntry ,
struct inode * newinode )
{
if ( tcon - > nocase )
direntry - > d_op = & cifs_ci_dentry_ops ;
else
direntry - > d_op = & cifs_dentry_ops ;
d_instantiate ( direntry , newinode ) ;
}
2006-06-01 02:40:51 +04:00
/* Inode operations in similar order to how they appear in Linux file fs.h */
2005-04-17 02:20:36 +04:00
int
cifs_create ( struct inode * inode , struct dentry * direntry , int mode ,
struct nameidata * nd )
{
int rc = - ENOENT ;
int xid ;
2008-05-10 02:28:02 +04:00
int create_options = CREATE_NOT_DIR ;
2005-04-17 02:20:36 +04:00
int oplock = 0 ;
2009-02-20 07:32:45 +03:00
int oflags ;
/*
* BB below access is probably too much for mknod to request
* but we have to do query and setpathinfo so requesting
* less could fail ( unless we want to request getatr and setatr
* permissions ( only ) . At least for POSIX we do not have to
* request so much .
*/
2005-04-17 02:20:36 +04:00
int desiredAccess = GENERIC_READ | GENERIC_WRITE ;
__u16 fileHandle ;
struct cifs_sb_info * cifs_sb ;
2009-01-19 05:38:35 +03:00
struct cifsTconInfo * tcon ;
2005-04-17 02:20:36 +04:00
char * full_path = NULL ;
2007-07-10 05:16:18 +04:00
FILE_ALL_INFO * buf = NULL ;
2005-04-17 02:20:36 +04:00
struct inode * newinode = NULL ;
int disposition = FILE_OVERWRITE_IF ;
2008-04-29 04:06:05 +04:00
bool write_only = false ;
2005-04-17 02:20:36 +04:00
xid = GetXid ( ) ;
cifs_sb = CIFS_SB ( inode - > i_sb ) ;
2009-01-19 05:38:35 +03:00
tcon = cifs_sb - > tcon ;
2005-04-17 02:20:36 +04:00
full_path = build_path_from_dentry ( direntry ) ;
2007-06-05 22:30:44 +04:00
if ( full_path = = NULL ) {
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
return - ENOMEM ;
}
2009-03-30 03:08:22 +04:00
mode & = ~ current_umask ( ) ;
2009-02-20 07:32:45 +03:00
if ( oplockEnabled )
oplock = REQ_OPLOCK ;
2009-01-19 05:38:35 +03:00
2009-02-20 07:32:45 +03:00
if ( nd & & ( nd - > flags & LOOKUP_OPEN ) )
oflags = nd - > intent . open . flags ;
else
oflags = FMODE_READ ;
if ( tcon - > unix_ext & & ( tcon - > ses - > capabilities & CAP_UNIX ) & &
( CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu ( tcon - > fsUnixInfo . Capability ) ) ) {
rc = cifs_posix_open ( full_path , & newinode , inode - > i_sb ,
mode , oflags , & oplock , & fileHandle , xid ) ;
/* EIO could indicate that (posix open) operation is not
supported , despite what server claimed in capability
negotation . EREMOTE indicates DFS junction , which is not
handled in posix open */
if ( ( rc = = 0 ) & & ( newinode = = NULL ) )
goto cifs_create_get_file_info ; /* query inode info */
else if ( rc = = 0 ) /* success, no need to query */
goto cifs_create_set_dentry ;
else if ( ( rc ! = - EIO ) & & ( rc ! = - EREMOTE ) & &
( rc ! = - EOPNOTSUPP ) ) /* path not found or net err */
goto cifs_create_out ;
/* else fallthrough to retry, using older open call, this is
case where server does not support this SMB level , and
falsely claims capability ( also get here for DFS case
which should be rare for path not covered on files ) */
}
2005-09-07 02:18:26 +04:00
2009-02-20 07:32:45 +03:00
if ( nd & & ( nd - > flags & LOOKUP_OPEN ) ) {
/* if the file is going to stay open, then we
need to set the desired access properly */
2005-09-07 02:18:26 +04:00
desiredAccess = 0 ;
if ( oflags & FMODE_READ )
2009-02-20 07:32:45 +03:00
desiredAccess | = GENERIC_READ ; /* is this too little? */
2005-09-07 02:18:26 +04:00
if ( oflags & FMODE_WRITE ) {
desiredAccess | = GENERIC_WRITE ;
if ( ! ( oflags & FMODE_READ ) )
2008-04-29 04:06:05 +04:00
write_only = true ;
2005-04-17 02:20:36 +04:00
}
2007-06-05 22:30:44 +04:00
if ( ( oflags & ( O_CREAT | O_EXCL ) ) = = ( O_CREAT | O_EXCL ) )
2005-04-17 02:20:36 +04:00
disposition = FILE_CREATE ;
2007-06-05 22:30:44 +04:00
else if ( ( oflags & ( O_CREAT | O_TRUNC ) ) = = ( O_CREAT | O_TRUNC ) )
2005-04-17 02:20:36 +04:00
disposition = FILE_OVERWRITE_IF ;
2007-06-05 22:30:44 +04:00
else if ( ( oflags & O_CREAT ) = = O_CREAT )
2005-04-17 02:20:36 +04:00
disposition = FILE_OPEN_IF ;
2008-02-08 02:25:02 +03:00
else
2007-06-05 22:30:44 +04:00
cFYI ( 1 , ( " Create flag not set in create function " ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-05 22:30:44 +04:00
/* BB add processing to set equivalent of mode - e.g. via CreateX with
ACLs */
2005-04-17 02:20:36 +04:00
2007-06-05 22:30:44 +04:00
buf = kmalloc ( sizeof ( FILE_ALL_INFO ) , GFP_KERNEL ) ;
if ( buf = = NULL ) {
2005-04-17 02:20:36 +04:00
kfree ( full_path ) ;
FreeXid ( xid ) ;
return - ENOMEM ;
}
2008-05-10 02:28:02 +04:00
/*
* if we ' re not using unix extensions , see if we need to set
* ATTR_READONLY on the create call
*/
2009-01-19 05:38:35 +03:00
if ( ! tcon - > unix_ext & & ( mode & S_IWUGO ) = = 0 )
2008-05-10 02:28:02 +04:00
create_options | = CREATE_OPTION_READONLY ;
2007-06-05 22:30:44 +04:00
if ( cifs_sb - > tcon - > ses - > capabilities & CAP_NT_SMBS )
2009-01-19 05:38:35 +03:00
rc = CIFSSMBOpen ( xid , tcon , full_path , disposition ,
2008-05-10 02:28:02 +04:00
desiredAccess , create_options ,
2005-04-29 09:41:06 +04:00
& fileHandle , & oplock , buf , cifs_sb - > local_nls ,
cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
2006-06-07 04:18:43 +04:00
else
rc = - EIO ; /* no NT SMB support fall into legacy open below */
2007-06-05 22:30:44 +04:00
if ( rc = = - EIO ) {
2005-08-25 10:06:05 +04:00
/* old server, retry the open legacy style */
2009-01-19 05:38:35 +03:00
rc = SMBLegacyOpen ( xid , tcon , full_path , disposition ,
2008-05-10 02:28:02 +04:00
desiredAccess , create_options ,
2005-08-25 10:06:05 +04:00
& fileHandle , & oplock , buf , cifs_sb - > local_nls ,
cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
2007-06-05 22:30:44 +04:00
}
2005-04-17 02:20:36 +04:00
if ( rc ) {
2006-05-31 22:05:34 +04:00
cFYI ( 1 , ( " cifs_create returned 0x%x " , rc ) ) ;
2009-02-20 07:32:45 +03:00
goto cifs_create_out ;
}
/* If Open reported that we actually created a file
then we now have to set the mode if possible */
if ( ( tcon - > unix_ext ) & & ( oplock & CIFS_CREATE_ACTION ) ) {
struct cifs_unix_set_info_args args = {
2008-08-02 15:26:12 +04:00
. mode = mode ,
. ctime = NO_CHANGE_64 ,
. atime = NO_CHANGE_64 ,
. mtime = NO_CHANGE_64 ,
. device = 0 ,
2009-02-20 07:32:45 +03:00
} ;
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_SET_UID ) {
args . uid = ( __u64 ) current_fsuid ( ) ;
if ( inode - > i_mode & S_ISGID )
args . gid = ( __u64 ) inode - > i_gid ;
else
args . gid = ( __u64 ) current_fsgid ( ) ;
2007-06-08 18:55:14 +04:00
} else {
2009-02-20 07:32:45 +03:00
args . uid = NO_CHANGE_64 ;
args . gid = NO_CHANGE_64 ;
2005-04-17 02:20:36 +04:00
}
2009-02-20 07:32:45 +03:00
CIFSSMBUnixSetInfo ( xid , tcon , full_path , & args ,
cifs_sb - > local_nls ,
cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
} else {
/* BB implement mode setting via Windows security
descriptors e . g . */
/* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
/* Could set r/o dos attribute if mode & 0222 == 0 */
}
2005-04-17 02:20:36 +04:00
2009-02-20 07:32:45 +03:00
cifs_create_get_file_info :
/* server might mask mode so we have to query for it */
if ( tcon - > unix_ext )
rc = cifs_get_inode_info_unix ( & newinode , full_path ,
inode - > i_sb , xid ) ;
else {
rc = cifs_get_inode_info ( & newinode , full_path , buf ,
inode - > i_sb , xid , & fileHandle ) ;
if ( newinode ) {
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_DYNPERM )
newinode - > i_mode = mode ;
if ( ( oplock & CIFS_CREATE_ACTION ) & &
( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_SET_UID ) ) {
newinode - > i_uid = current_fsuid ( ) ;
if ( inode - > i_mode & S_ISGID )
newinode - > i_gid = inode - > i_gid ;
else
newinode - > i_gid = current_fsgid ( ) ;
2005-11-30 07:20:10 +03:00
}
2005-04-17 02:20:36 +04:00
}
2009-02-20 07:32:45 +03:00
}
2005-04-17 02:20:36 +04:00
2009-02-20 07:32:45 +03:00
cifs_create_set_dentry :
if ( rc = = 0 )
setup_cifs_dentry ( tcon , direntry , newinode ) ;
else
cFYI ( 1 , ( " Create worked, get_inode_info failed rc = %d " , rc ) ) ;
/* nfsd case - nfs srv does not set nd */
if ( ( nd = = NULL ) | | ( ! ( nd - > flags & LOOKUP_OPEN ) ) ) {
/* mknod case - do not leave file open */
CIFSSMBClose ( xid , tcon , fileHandle ) ;
} else if ( newinode ) {
2009-04-09 05:14:32 +04:00
cifs_fill_fileinfo ( newinode , fileHandle ,
cifs_sb - > tcon , write_only ) ;
2007-06-05 22:30:44 +04:00
}
2005-04-29 09:41:05 +04:00
cifs_create_out :
kfree ( buf ) ;
kfree ( full_path ) ;
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
return rc ;
}
2007-06-05 22:30:44 +04:00
int cifs_mknod ( struct inode * inode , struct dentry * direntry , int mode ,
dev_t device_number )
2005-04-17 02:20:36 +04:00
{
int rc = - EPERM ;
int xid ;
struct cifs_sb_info * cifs_sb ;
struct cifsTconInfo * pTcon ;
char * full_path = NULL ;
2007-07-10 05:16:18 +04:00
struct inode * newinode = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! old_valid_dev ( device_number ) )
return - EINVAL ;
xid = GetXid ( ) ;
cifs_sb = CIFS_SB ( inode - > i_sb ) ;
pTcon = cifs_sb - > tcon ;
full_path = build_path_from_dentry ( direntry ) ;
2007-06-05 22:30:44 +04:00
if ( full_path = = NULL )
2005-04-17 02:20:36 +04:00
rc = - ENOMEM ;
2007-07-19 03:21:09 +04:00
else if ( pTcon - > unix_ext ) {
2008-08-02 15:26:12 +04:00
struct cifs_unix_set_info_args args = {
2009-03-30 03:08:22 +04:00
. mode = mode & ~ current_umask ( ) ,
2008-08-02 15:26:12 +04:00
. ctime = NO_CHANGE_64 ,
. atime = NO_CHANGE_64 ,
. mtime = NO_CHANGE_64 ,
. device = device_number ,
} ;
2007-06-05 22:30:44 +04:00
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_SET_UID ) {
2008-11-14 02:38:47 +03:00
args . uid = ( __u64 ) current_fsuid ( ) ;
args . gid = ( __u64 ) current_fsgid ( ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-08-02 15:26:12 +04:00
args . uid = NO_CHANGE_64 ;
args . gid = NO_CHANGE_64 ;
2005-04-17 02:20:36 +04:00
}
2008-08-02 15:26:12 +04:00
rc = CIFSSMBUnixSetInfo ( xid , pTcon , full_path ,
& args , cifs_sb - > local_nls ,
cifs_sb - > mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
2005-04-17 02:20:36 +04:00
2007-06-05 22:30:44 +04:00
if ( ! rc ) {
2005-04-17 02:20:36 +04:00
rc = cifs_get_inode_info_unix ( & newinode , full_path ,
2007-06-05 22:30:44 +04:00
inode - > i_sb , xid ) ;
2005-08-23 07:09:43 +04:00
if ( pTcon - > nocase )
direntry - > d_op = & cifs_ci_dentry_ops ;
else
direntry - > d_op = & cifs_dentry_ops ;
2007-06-05 22:30:44 +04:00
if ( rc = = 0 )
2005-04-17 02:20:36 +04:00
d_instantiate ( direntry , newinode ) ;
}
2005-07-15 03:25:12 +04:00
} else {
2007-06-05 22:30:44 +04:00
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL ) {
2005-07-22 02:20:28 +04:00
int oplock = 0 ;
u16 fileHandle ;
2008-02-08 02:25:02 +03:00
FILE_ALL_INFO * buf ;
2005-07-15 03:25:12 +04:00
2007-06-05 22:30:44 +04:00
cFYI ( 1 , ( " sfu compat create special file " ) ) ;
2005-07-15 03:25:12 +04:00
2007-06-05 22:30:44 +04:00
buf = kmalloc ( sizeof ( FILE_ALL_INFO ) , GFP_KERNEL ) ;
if ( buf = = NULL ) {
2005-07-22 02:20:28 +04:00
kfree ( full_path ) ;
FreeXid ( xid ) ;
return - ENOMEM ;
}
rc = CIFSSMBOpen ( xid , pTcon , full_path ,
FILE_CREATE , /* fail if exists */
2007-06-05 22:30:44 +04:00
GENERIC_WRITE /* BB would
2005-07-22 02:20:28 +04:00
WRITE_OWNER | WRITE_DAC be better ? */ ,
/* Create a file and set the
file attribute to SYSTEM */
CREATE_NOT_DIR | CREATE_OPTION_SPECIAL ,
& fileHandle , & oplock , buf ,
cifs_sb - > local_nls ,
2007-06-05 22:30:44 +04:00
cifs_sb - > mnt_cifs_flags &
2005-07-22 02:20:28 +04:00
CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
2006-06-07 04:18:43 +04:00
/* BB FIXME - add handling for backlevel servers
which need legacy open and check for all
2007-06-05 22:30:44 +04:00
calls to SMBOpen for fallback to SMBLeagcyOpen */
if ( ! rc ) {
2005-07-22 02:20:28 +04:00
/* BB Do not bother to decode buf since no
2005-11-19 07:25:31 +03:00
local inode yet to put timestamps in ,
but we can reuse it safely */
2007-08-31 05:10:17 +04:00
unsigned int bytes_written ;
2005-11-19 07:25:31 +03:00
struct win_dev * pdev ;
pdev = ( struct win_dev * ) buf ;
2007-06-05 22:30:44 +04:00
if ( S_ISCHR ( mode ) ) {
2005-11-19 07:25:31 +03:00
memcpy ( pdev - > type , " IntxCHR " , 8 ) ;
pdev - > major =
cpu_to_le64 ( MAJOR ( device_number ) ) ;
2007-06-05 22:30:44 +04:00
pdev - > minor =
2005-11-19 07:25:31 +03:00
cpu_to_le64 ( MINOR ( device_number ) ) ;
rc = CIFSSMBWrite ( xid , pTcon ,
fileHandle ,
sizeof ( struct win_dev ) ,
0 , & bytes_written , ( char * ) pdev ,
NULL , 0 ) ;
2007-06-05 22:30:44 +04:00
} else if ( S_ISBLK ( mode ) ) {
2005-11-19 07:25:31 +03:00
memcpy ( pdev - > type , " IntxBLK " , 8 ) ;
pdev - > major =
cpu_to_le64 ( MAJOR ( device_number ) ) ;
pdev - > minor =
cpu_to_le64 ( MINOR ( device_number ) ) ;
rc = CIFSSMBWrite ( xid , pTcon ,
fileHandle ,
sizeof ( struct win_dev ) ,
0 , & bytes_written , ( char * ) pdev ,
NULL , 0 ) ;
} /* else if(S_ISFIFO */
2005-07-22 02:20:28 +04:00
CIFSSMBClose ( xid , pTcon , fileHandle ) ;
d_drop ( direntry ) ;
}
kfree ( buf ) ;
2005-07-15 03:25:12 +04:00
/* add code here to set EAs */
}
2005-04-17 02:20:36 +04:00
}
2005-04-29 09:41:05 +04:00
kfree ( full_path ) ;
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
return rc ;
}
struct dentry *
2007-06-05 22:30:44 +04:00
cifs_lookup ( struct inode * parent_dir_inode , struct dentry * direntry ,
struct nameidata * nd )
2005-04-17 02:20:36 +04:00
{
int xid ;
int rc = 0 ; /* to get around spurious gcc warning, set to zero here */
2009-04-09 05:14:32 +04:00
int oplock = 0 ;
int mode ;
__u16 fileHandle = 0 ;
bool posix_open = false ;
2005-04-17 02:20:36 +04:00
struct cifs_sb_info * cifs_sb ;
struct cifsTconInfo * pTcon ;
struct inode * newInode = NULL ;
char * full_path = NULL ;
2009-04-09 05:14:32 +04:00
struct file * filp ;
2005-04-17 02:20:36 +04:00
xid = GetXid ( ) ;
2008-12-03 03:57:54 +03:00
cFYI ( 1 , ( " parent inode = 0x%p name is: %s and dentry = 0x%p " ,
2005-04-17 02:20:36 +04:00
parent_dir_inode , direntry - > d_name . name , direntry ) ) ;
/* check whether path exists */
cifs_sb = CIFS_SB ( parent_dir_inode - > i_sb ) ;
pTcon = cifs_sb - > tcon ;
2006-04-21 22:18:37 +04:00
/*
* Don ' t allow the separator character in a path component .
* The VFS will not allow " / " , but " \" is allowed by posix.
*/
if ( ! ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS ) ) {
int i ;
for ( i = 0 ; i < direntry - > d_name . len ; i + + )
if ( direntry - > d_name . name [ i ] = = ' \\ ' ) {
cFYI ( 1 , ( " Invalid file name " ) ) ;
FreeXid ( xid ) ;
return ERR_PTR ( - EINVAL ) ;
}
}
2005-04-17 02:20:36 +04:00
/* can not grab the rename sem here since it would
deadlock in the cases ( beginning of sys_rename itself )
in which we already have the sb rename sem */
full_path = build_path_from_dentry ( direntry ) ;
2007-06-05 22:30:44 +04:00
if ( full_path = = NULL ) {
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
return ERR_PTR ( - ENOMEM ) ;
}
if ( direntry - > d_inode ! = NULL ) {
2008-12-03 03:57:54 +03:00
cFYI ( 1 , ( " non-NULL inode in lookup " ) ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-12-03 03:57:54 +03:00
cFYI ( 1 , ( " NULL inode in lookup " ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-03 03:57:54 +03:00
cFYI ( 1 , ( " Full path: %s inode = 0x%p " , full_path , direntry - > d_inode ) ) ;
2005-04-17 02:20:36 +04:00
2009-04-09 05:14:32 +04:00
if ( pTcon - > unix_ext ) {
if ( ! ( nd - > flags & ( LOOKUP_PARENT | LOOKUP_DIRECTORY ) ) & &
( nd - > flags & LOOKUP_OPEN ) ) {
if ( ! ( ( nd - > intent . open . flags & O_CREAT ) & &
( nd - > intent . open . flags & O_EXCL ) ) ) {
mode = nd - > intent . open . create_mode &
2009-04-15 07:09:39 +04:00
~ current_umask ( ) ;
2009-04-09 05:14:32 +04:00
rc = cifs_posix_open ( full_path , & newInode ,
parent_dir_inode - > i_sb , mode ,
nd - > intent . open . flags , & oplock ,
& fileHandle , xid ) ;
2009-04-12 22:18:40 +04:00
/*
* This code works around a bug in
* samba posix open in samba versions 3.3 .1
* and earlier where create works
* but open fails with invalid parameter .
* If either of these error codes are
* returned , follow the normal lookup .
* Otherwise , the error during posix open
* is handled .
*/
2009-04-09 05:14:32 +04:00
if ( ( rc ! = - EINVAL ) & & ( rc ! = - EOPNOTSUPP ) )
posix_open = true ;
}
}
if ( ! posix_open )
rc = cifs_get_inode_info_unix ( & newInode , full_path ,
parent_dir_inode - > i_sb , xid ) ;
} else
2005-04-17 02:20:36 +04:00
rc = cifs_get_inode_info ( & newInode , full_path , NULL ,
2009-04-09 05:14:32 +04:00
parent_dir_inode - > i_sb , xid , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ( rc = = 0 ) & & ( newInode ! = NULL ) ) {
2005-08-23 07:09:43 +04:00
if ( pTcon - > nocase )
direntry - > d_op = & cifs_ci_dentry_ops ;
else
direntry - > d_op = & cifs_dentry_ops ;
2005-04-17 02:20:36 +04:00
d_add ( direntry , newInode ) ;
2009-04-09 05:14:32 +04:00
if ( posix_open )
filp = lookup_instantiate_filp ( nd , direntry , NULL ) ;
2007-06-05 22:30:44 +04:00
/* since paths are not looked up by component - the parent
2005-11-28 19:16:13 +03:00
directories are presumed to be good here */
2005-04-17 02:20:36 +04:00
renew_parental_timestamps ( direntry ) ;
} else if ( rc = = - ENOENT ) {
rc = 0 ;
2005-11-28 19:16:13 +03:00
direntry - > d_time = jiffies ;
if ( pTcon - > nocase )
direntry - > d_op = & cifs_ci_dentry_ops ;
else
direntry - > d_op = & cifs_dentry_ops ;
2005-04-17 02:20:36 +04:00
d_add ( direntry , NULL ) ;
2007-06-05 22:30:44 +04:00
/* if it was once a directory (but how can we tell?) we could do
shrink_dcache_parent ( direntry ) ; */
2008-01-20 03:30:29 +03:00
} else if ( rc ! = - EACCES ) {
cERROR ( 1 , ( " Unexpected lookup error %d " , rc ) ) ;
/* We special case check for Access Denied - since that
is a common return code */
2005-04-17 02:20:36 +04:00
}
2005-04-29 09:41:05 +04:00
kfree ( full_path ) ;
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
return ERR_PTR ( rc ) ;
}
static int
cifs_d_revalidate ( struct dentry * direntry , struct nameidata * nd )
{
int isValid = 1 ;
if ( direntry - > d_inode ) {
2008-02-08 02:25:02 +03:00
if ( cifs_revalidate ( direntry ) )
2005-04-17 02:20:36 +04:00
return 0 ;
} else {
2005-11-28 19:16:13 +03:00
cFYI ( 1 , ( " neg dentry 0x%p name = %s " ,
direntry , direntry - > d_name . name ) ) ;
2007-06-05 22:30:44 +04:00
if ( time_after ( jiffies , direntry - > d_time + HZ ) | |
2005-11-28 19:16:13 +03:00
! lookupCacheEnabled ) {
d_drop ( direntry ) ;
isValid = 0 ;
2007-06-05 22:30:44 +04:00
}
2005-04-17 02:20:36 +04:00
}
return isValid ;
}
/* static int cifs_d_delete(struct dentry *direntry)
{
int rc = 0 ;
cFYI ( 1 , ( " In cifs d_delete, name = %s " , direntry - > d_name . name ) ) ;
return rc ;
} */
2009-02-20 08:57:07 +03:00
const struct dentry_operations cifs_dentry_ops = {
2005-04-17 02:20:36 +04:00
. d_revalidate = cifs_d_revalidate ,
2007-06-05 22:30:44 +04:00
/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
2005-04-17 02:20:36 +04:00
} ;
2005-08-23 07:09:43 +04:00
static int cifs_ci_hash ( struct dentry * dentry , struct qstr * q )
{
struct nls_table * codepage = CIFS_SB ( dentry - > d_inode - > i_sb ) - > local_nls ;
unsigned long hash ;
int i ;
hash = init_name_hash ( ) ;
for ( i = 0 ; i < q - > len ; i + + )
hash = partial_name_hash ( nls_tolower ( codepage , q - > name [ i ] ) ,
hash ) ;
q - > hash = end_name_hash ( hash ) ;
return 0 ;
}
static int cifs_ci_compare ( struct dentry * dentry , struct qstr * a ,
struct qstr * b )
{
struct nls_table * codepage = CIFS_SB ( dentry - > d_inode - > i_sb ) - > local_nls ;
if ( ( a - > len = = b - > len ) & &
( nls_strnicmp ( codepage , a - > name , b - > name , a - > len ) = = 0 ) ) {
/*
* To preserve case , don ' t let an existing negative dentry ' s
* case take precedence . If a is not a negative dentry , this
* should have no side effects
*/
2008-05-15 09:41:54 +04:00
memcpy ( ( void * ) a - > name , b - > name , a - > len ) ;
2005-08-23 07:09:43 +04:00
return 0 ;
}
return 1 ;
}
2009-02-20 08:57:07 +03:00
const struct dentry_operations cifs_ci_dentry_ops = {
2005-08-23 07:09:43 +04:00
. d_revalidate = cifs_d_revalidate ,
. d_hash = cifs_ci_hash ,
. d_compare = cifs_ci_compare ,
} ;