2005-04-17 02:20:36 +04:00
/*
* fs / cifs / readdir . c
*
* Directory search handling
2007-07-07 03:13:06 +04:00
*
2008-02-08 02:25:02 +03:00
* Copyright ( C ) International Business Machines Corp . , 2004 , 2008
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>
2006-06-01 23:41:23 +04:00
# include <linux/pagemap.h>
2005-04-17 02:20:36 +04:00
# include <linux/stat.h>
# include "cifspdu.h"
# include "cifsglob.h"
# include "cifsproto.h"
# include "cifs_unicode.h"
# include "cifs_debug.h"
# include "cifs_fs_sb.h"
# include "cifsfs.h"
2006-06-01 02:40:51 +04:00
# ifdef CONFIG_CIFS_DEBUG2
static void dump_cifs_file_struct ( struct file * file , char * label )
2005-04-17 02:20:36 +04:00
{
2007-07-07 03:13:06 +04:00
struct cifsFileInfo * cf ;
2005-04-17 02:20:36 +04:00
2007-05-01 00:13:06 +04:00
if ( file ) {
2005-04-17 02:20:36 +04:00
cf = file - > private_data ;
2007-05-01 00:13:06 +04:00
if ( cf = = NULL ) {
2007-07-07 03:13:06 +04:00
cFYI ( 1 , ( " empty cifs private file data " ) ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2008-02-08 02:25:02 +03:00
if ( cf - > invalidHandle )
2007-07-07 03:13:06 +04:00
cFYI ( 1 , ( " invalid handle " ) ) ;
2008-02-08 02:25:02 +03:00
if ( cf - > srch_inf . endOfSearch )
2007-07-07 03:13:06 +04:00
cFYI ( 1 , ( " end of search " ) ) ;
2008-02-08 02:25:02 +03:00
if ( cf - > srch_inf . emptyDir )
2007-07-07 03:13:06 +04:00
cFYI ( 1 , ( " empty dir " ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-01 02:40:51 +04:00
}
2008-02-12 23:32:36 +03:00
# else
static inline void dump_cifs_file_struct ( struct file * file , char * label )
{
}
2006-06-01 02:40:51 +04:00
# endif /* DEBUG2 */
2005-04-17 02:20:36 +04:00
/* Returns one if new inode created (which therefore needs to be hashed) */
/* Might check in the future if inode number changed so we can rehash inode */
static int construct_dentry ( struct qstr * qstring , struct file * file ,
struct inode * * ptmp_inode , struct dentry * * pnew_dentry )
{
struct dentry * tmp_dentry ;
struct cifs_sb_info * cifs_sb ;
struct cifsTconInfo * pTcon ;
int rc = 0 ;
2005-04-29 09:41:08 +04:00
cFYI ( 1 , ( " For %s " , qstring - > name ) ) ;
2006-12-08 13:36:48 +03:00
cifs_sb = CIFS_SB ( file - > f_path . dentry - > d_sb ) ;
2005-04-17 02:20:36 +04:00
pTcon = cifs_sb - > tcon ;
qstring - > hash = full_name_hash ( qstring - > name , qstring - > len ) ;
2006-12-08 13:36:48 +03:00
tmp_dentry = d_lookup ( file - > f_path . dentry , qstring ) ;
2005-04-17 02:20:36 +04:00
if ( tmp_dentry ) {
2007-07-07 03:13:06 +04:00
cFYI ( 0 , ( " existing dentry with inode 0x%p " ,
tmp_dentry - > d_inode ) ) ;
2005-04-17 02:20:36 +04:00
* ptmp_inode = tmp_dentry - > d_inode ;
/* BB overwrite old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len??*/
2007-05-01 00:13:06 +04:00
if ( * ptmp_inode = = NULL ) {
2006-12-08 13:36:48 +03:00
* ptmp_inode = new_inode ( file - > f_path . dentry - > d_sb ) ;
2007-05-01 00:13:06 +04:00
if ( * ptmp_inode = = NULL )
2005-04-17 02:20:36 +04:00
return rc ;
rc = 1 ;
}
2007-05-01 00:13:06 +04:00
if ( file - > f_path . dentry - > d_sb - > s_flags & MS_NOATIME )
2007-02-17 07:30:54 +03:00
( * ptmp_inode ) - > i_flags | = S_NOATIME | S_NOCMTIME ;
2005-04-17 02:20:36 +04:00
} else {
2006-12-08 13:36:48 +03:00
tmp_dentry = d_alloc ( file - > f_path . dentry , qstring ) ;
2007-05-01 00:13:06 +04:00
if ( tmp_dentry = = NULL ) {
2007-07-07 03:13:06 +04:00
cERROR ( 1 , ( " Failed allocating dentry " ) ) ;
2005-04-17 02:20:36 +04:00
* ptmp_inode = NULL ;
return rc ;
}
2006-12-08 13:36:48 +03:00
* ptmp_inode = new_inode ( file - > f_path . dentry - > d_sb ) ;
2005-08-23 07:09:43 +04:00
if ( pTcon - > nocase )
tmp_dentry - > d_op = & cifs_ci_dentry_ops ;
else
tmp_dentry - > d_op = & cifs_dentry_ops ;
2007-05-01 00:13:06 +04:00
if ( * ptmp_inode = = NULL )
2005-04-17 02:20:36 +04:00
return rc ;
2007-05-01 00:13:06 +04:00
if ( file - > f_path . dentry - > d_sb - > s_flags & MS_NOATIME )
2007-07-08 19:40:40 +04:00
( * ptmp_inode ) - > i_flags | = S_NOATIME | S_NOCMTIME ;
2006-09-07 02:02:22 +04:00
rc = 2 ;
2005-04-17 02:20:36 +04:00
}
tmp_dentry - > d_time = jiffies ;
* pnew_dentry = tmp_dentry ;
return rc ;
}
2007-07-07 23:25:05 +04:00
static void AdjustForTZ ( struct cifsTconInfo * tcon , struct inode * inode )
2006-10-12 04:02:32 +04:00
{
2007-05-01 00:13:06 +04:00
if ( ( tcon ) & & ( tcon - > ses ) & & ( tcon - > ses - > server ) ) {
2006-10-12 05:23:29 +04:00
inode - > i_ctime . tv_sec + = tcon - > ses - > server - > timeAdj ;
inode - > i_mtime . tv_sec + = tcon - > ses - > server - > timeAdj ;
inode - > i_atime . tv_sec + = tcon - > ses - > server - > timeAdj ;
2006-10-12 04:02:32 +04:00
}
return ;
}
2006-06-07 04:18:43 +04:00
static void fill_in_inode ( struct inode * tmp_inode , int new_buf_type ,
2007-10-12 23:10:28 +04:00
char * buf , unsigned int * pobject_type , int isNewInode )
2005-04-17 02:20:36 +04:00
{
2005-04-29 09:41:08 +04:00
loff_t local_size ;
struct timespec local_mtime ;
2005-04-17 02:20:36 +04:00
struct cifsInodeInfo * cifsInfo = CIFS_I ( tmp_inode ) ;
struct cifs_sb_info * cifs_sb = CIFS_SB ( tmp_inode - > i_sb ) ;
2006-06-07 04:18:43 +04:00
__u32 attr ;
__u64 allocation_size ;
__u64 end_of_file ;
2008-05-22 17:31:40 +04:00
umode_t default_mode ;
2005-04-17 02:20:36 +04:00
2005-04-29 09:41:08 +04:00
/* save mtime and size */
local_mtime = tmp_inode - > i_mtime ;
local_size = tmp_inode - > i_size ;
2007-05-01 00:13:06 +04:00
if ( new_buf_type ) {
2006-06-07 04:18:43 +04:00
FILE_DIRECTORY_INFO * pfindData = ( FILE_DIRECTORY_INFO * ) buf ;
attr = le32_to_cpu ( pfindData - > ExtFileAttributes ) ;
allocation_size = le64_to_cpu ( pfindData - > AllocationSize ) ;
end_of_file = le64_to_cpu ( pfindData - > EndOfFile ) ;
tmp_inode - > i_atime =
cifs_NTtimeToUnix ( le64_to_cpu ( pfindData - > LastAccessTime ) ) ;
tmp_inode - > i_mtime =
cifs_NTtimeToUnix ( le64_to_cpu ( pfindData - > LastWriteTime ) ) ;
tmp_inode - > i_ctime =
cifs_NTtimeToUnix ( le64_to_cpu ( pfindData - > ChangeTime ) ) ;
} else { /* legacy, OS2 and DOS style */
2006-09-28 07:35:57 +04:00
/* struct timespec ts;*/
2008-02-08 02:25:02 +03:00
FIND_FILE_STANDARD_INFO * pfindData =
2006-06-07 04:18:43 +04:00
( FIND_FILE_STANDARD_INFO * ) buf ;
2006-10-07 01:47:09 +04:00
tmp_inode - > i_mtime = cnvrtDosUnixTm (
2006-09-28 07:35:57 +04:00
le16_to_cpu ( pfindData - > LastWriteDate ) ,
2006-10-07 01:47:09 +04:00
le16_to_cpu ( pfindData - > LastWriteTime ) ) ;
tmp_inode - > i_atime = cnvrtDosUnixTm (
le16_to_cpu ( pfindData - > LastAccessDate ) ,
le16_to_cpu ( pfindData - > LastAccessTime ) ) ;
2007-02-06 23:43:30 +03:00
tmp_inode - > i_ctime = cnvrtDosUnixTm (
le16_to_cpu ( pfindData - > LastWriteDate ) ,
le16_to_cpu ( pfindData - > LastWriteTime ) ) ;
2006-10-12 04:02:32 +04:00
AdjustForTZ ( cifs_sb - > tcon , tmp_inode ) ;
2006-06-07 04:18:43 +04:00
attr = le16_to_cpu ( pfindData - > Attributes ) ;
allocation_size = le32_to_cpu ( pfindData - > AllocationSize ) ;
end_of_file = le32_to_cpu ( pfindData - > DataSize ) ;
}
2005-04-17 02:20:36 +04:00
/* Linux can not store file creation time unfortunately so ignore it */
2006-06-07 04:18:43 +04:00
cifsInfo - > cifsAttrs = attr ;
2007-11-09 02:10:32 +03:00
# ifdef CONFIG_CIFS_EXPERIMENTAL
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL ) {
/* get more accurate mode via ACL - so force inode refresh */
cifsInfo - > time = 0 ;
} else
# endif /* CONFIG_CIFS_EXPERIMENTAL */
cifsInfo - > time = jiffies ;
2006-06-07 04:18:43 +04:00
2005-04-17 02:20:36 +04:00
/* treat dos attribute of read-only as read-only mode bit e.g. 555? */
/* 2767 perms - indicate mandatory locking */
2007-07-07 23:25:05 +04:00
/* BB fill in uid and gid here? with help from winbind?
2005-04-17 02:20:36 +04:00
or retrieve from NTFS stream extended attribute */
if ( atomic_read ( & cifsInfo - > inUse ) = = 0 ) {
tmp_inode - > i_uid = cifs_sb - > mnt_uid ;
tmp_inode - > i_gid = cifs_sb - > mnt_gid ;
2008-05-22 17:31:40 +04:00
}
if ( attr & ATTR_DIRECTORY )
default_mode = cifs_sb - > mnt_dir_mode ;
else
default_mode = cifs_sb - > mnt_file_mode ;
/* set initial permissions */
if ( ( atomic_read ( & cifsInfo - > inUse ) = = 0 ) | |
( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_DYNPERM ) = = 0 )
tmp_inode - > i_mode = default_mode ;
else {
/* just reenable write bits if !ATTR_READONLY */
if ( ( tmp_inode - > i_mode & S_IWUGO ) = = 0 & &
( attr & ATTR_READONLY ) = = 0 )
tmp_inode - > i_mode | = ( S_IWUGO & default_mode ) ;
2005-11-18 22:31:10 +03:00
tmp_inode - > i_mode & = ~ S_IFMT ;
2005-04-17 02:20:36 +04:00
}
2008-05-22 17:31:40 +04:00
/* clear write bits if ATTR_READONLY is set */
if ( attr & ATTR_READONLY )
tmp_inode - > i_mode & = ~ S_IWUGO ;
/* set inode type */
if ( ( attr & ATTR_SYSTEM ) & &
( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL ) ) {
2005-11-18 22:31:10 +03:00
if ( end_of_file = = 0 ) {
tmp_inode - > i_mode | = S_IFIFO ;
2008-05-22 17:31:40 +04:00
* pobject_type = DT_FIFO ;
2005-11-18 22:31:10 +03:00
} else {
2008-05-22 17:31:40 +04:00
/*
* trying to get the type can be slow , so just call
* this a regular file for now , and mark for reval
*/
2007-07-07 23:25:05 +04:00
tmp_inode - > i_mode | = S_IFREG ;
2008-05-22 17:31:40 +04:00
* pobject_type = DT_REG ;
2007-07-07 23:25:05 +04:00
cifsInfo - > time = 0 ;
2005-11-18 22:31:10 +03:00
}
2005-04-17 02:20:36 +04:00
} else {
2008-05-22 17:31:40 +04:00
if ( attr & ATTR_DIRECTORY ) {
tmp_inode - > i_mode | = S_IFDIR ;
* pobject_type = DT_DIR ;
} else {
tmp_inode - > i_mode | = S_IFREG ;
* pobject_type = DT_REG ;
}
}
2005-04-17 02:20:36 +04:00
/* can not fill in nlink here as in qpathinfo version and Unx search */
2008-02-08 02:25:02 +03:00
if ( atomic_read ( & cifsInfo - > inUse ) = = 0 )
2005-04-17 02:20:36 +04:00
atomic_set ( & cifsInfo - > inUse , 1 ) ;
2007-02-26 19:46:11 +03:00
spin_lock ( & tmp_inode - > i_lock ) ;
2007-02-08 21:14:13 +03:00
if ( is_size_safe_to_change ( cifsInfo , end_of_file ) ) {
2007-07-07 23:25:05 +04:00
/* can not safely change the file size here if the
2005-04-17 02:20:36 +04:00
client is writing to it due to potential races */
i_size_write ( tmp_inode , end_of_file ) ;
/* 512 bytes (2**9) is the fake blocksize that must be used */
/* for this calculation, even though the reported blocksize is larger */
tmp_inode - > i_blocks = ( 512 - 1 + allocation_size ) > > 9 ;
}
2007-02-26 19:46:11 +03:00
spin_unlock ( & tmp_inode - > i_lock ) ;
2005-04-17 02:20:36 +04:00
if ( allocation_size < end_of_file )
cFYI ( 1 , ( " May be sparse file, allocation less than file size " ) ) ;
2006-09-27 12:50:49 +04:00
cFYI ( 1 , ( " File Size %ld and blocks %llu " ,
2006-03-26 13:37:53 +04:00
( unsigned long ) tmp_inode - > i_size ,
2006-09-27 12:50:49 +04:00
( unsigned long long ) tmp_inode - > i_blocks ) ) ;
2005-04-17 02:20:36 +04:00
if ( S_ISREG ( tmp_inode - > i_mode ) ) {
2005-04-29 09:41:08 +04:00
cFYI ( 1 , ( " File inode " ) ) ;
2005-04-17 02:20:36 +04:00
tmp_inode - > i_op = & cifs_file_inode_ops ;
2007-05-01 00:13:06 +04:00
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO ) {
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_NO_BRL )
2005-11-11 22:41:00 +03:00
tmp_inode - > i_fop = & cifs_file_direct_nobrl_ops ;
else
tmp_inode - > i_fop = & cifs_file_direct_ops ;
2007-05-01 00:13:06 +04:00
} else if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_NO_BRL )
2005-11-11 22:41:00 +03:00
tmp_inode - > i_fop = & cifs_file_nobrl_ops ;
2005-04-17 02:20:36 +04:00
else
tmp_inode - > i_fop = & cifs_file_ops ;
2006-01-09 07:12:58 +03:00
2007-05-01 00:13:06 +04:00
if ( ( cifs_sb - > tcon ) & & ( cifs_sb - > tcon - > ses ) & &
2005-09-01 08:50:37 +04:00
( cifs_sb - > tcon - > ses - > server - > maxBuf <
2006-06-01 23:41:23 +04:00
PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE ) )
tmp_inode - > i_data . a_ops = & cifs_addr_ops_smallbuf ;
else
tmp_inode - > i_data . a_ops = & cifs_addr_ops ;
2007-05-01 00:13:06 +04:00
if ( isNewInode )
2005-06-23 04:13:47 +04:00
return ; /* No sense invalidating pages for new inode
since have not started caching readahead file
data yet */
2005-04-29 09:41:08 +04:00
if ( timespec_equal ( & tmp_inode - > i_mtime , & local_mtime ) & &
( local_size = = tmp_inode - > i_size ) ) {
cFYI ( 1 , ( " inode exists but unchanged " ) ) ;
} else {
/* file may have changed on server */
cFYI ( 1 , ( " invalidate inode, readdir detected change " ) ) ;
invalidate_remote_inode ( tmp_inode ) ;
}
2005-04-17 02:20:36 +04:00
} else if ( S_ISDIR ( tmp_inode - > i_mode ) ) {
2005-04-29 09:41:08 +04:00
cFYI ( 1 , ( " Directory inode " ) ) ;
2005-04-17 02:20:36 +04:00
tmp_inode - > i_op = & cifs_dir_inode_ops ;
tmp_inode - > i_fop = & cifs_dir_ops ;
} else if ( S_ISLNK ( tmp_inode - > i_mode ) ) {
2005-04-29 09:41:08 +04:00
cFYI ( 1 , ( " Symbolic Link inode " ) ) ;
2005-04-17 02:20:36 +04:00
tmp_inode - > i_op = & cifs_symlink_inode_ops ;
} else {
2005-04-29 09:41:08 +04:00
cFYI ( 1 , ( " Init special inode " ) ) ;
2005-04-17 02:20:36 +04:00
init_special_inode ( tmp_inode , tmp_inode - > i_mode ,
tmp_inode - > i_rdev ) ;
}
}
static void unix_fill_in_inode ( struct inode * tmp_inode ,
2007-10-12 23:10:28 +04:00
FILE_UNIX_INFO * pfindData , unsigned int * pobject_type , int isNewInode )
2005-04-17 02:20:36 +04:00
{
2005-04-29 09:41:08 +04:00
loff_t local_size ;
struct timespec local_mtime ;
2005-04-17 02:20:36 +04:00
struct cifsInodeInfo * cifsInfo = CIFS_I ( tmp_inode ) ;
struct cifs_sb_info * cifs_sb = CIFS_SB ( tmp_inode - > i_sb ) ;
__u32 type = le32_to_cpu ( pfindData - > Type ) ;
__u64 num_of_bytes = le64_to_cpu ( pfindData - > NumOfBytes ) ;
__u64 end_of_file = le64_to_cpu ( pfindData - > EndOfFile ) ;
cifsInfo - > time = jiffies ;
atomic_inc ( & cifsInfo - > inUse ) ;
2005-04-29 09:41:08 +04:00
/* save mtime and size */
local_mtime = tmp_inode - > i_mtime ;
local_size = tmp_inode - > i_size ;
2005-04-17 02:20:36 +04:00
tmp_inode - > i_atime =
cifs_NTtimeToUnix ( le64_to_cpu ( pfindData - > LastAccessTime ) ) ;
tmp_inode - > i_mtime =
cifs_NTtimeToUnix ( le64_to_cpu ( pfindData - > LastModificationTime ) ) ;
tmp_inode - > i_ctime =
cifs_NTtimeToUnix ( le64_to_cpu ( pfindData - > LastStatusChange ) ) ;
tmp_inode - > i_mode = le64_to_cpu ( pfindData - > Permissions ) ;
2005-11-18 22:31:10 +03:00
/* since we set the inode type below we need to mask off type
2007-07-07 23:25:05 +04:00
to avoid strange results if bits above were corrupt */
tmp_inode - > i_mode & = ~ S_IFMT ;
2005-04-17 02:20:36 +04:00
if ( type = = UNIX_FILE ) {
* pobject_type = DT_REG ;
tmp_inode - > i_mode | = S_IFREG ;
} else if ( type = = UNIX_SYMLINK ) {
* pobject_type = DT_LNK ;
tmp_inode - > i_mode | = S_IFLNK ;
} else if ( type = = UNIX_DIR ) {
* pobject_type = DT_DIR ;
tmp_inode - > i_mode | = S_IFDIR ;
} else if ( type = = UNIX_CHARDEV ) {
* pobject_type = DT_CHR ;
tmp_inode - > i_mode | = S_IFCHR ;
tmp_inode - > i_rdev = MKDEV ( le64_to_cpu ( pfindData - > DevMajor ) ,
le64_to_cpu ( pfindData - > DevMinor ) & MINORMASK ) ;
} else if ( type = = UNIX_BLOCKDEV ) {
* pobject_type = DT_BLK ;
tmp_inode - > i_mode | = S_IFBLK ;
tmp_inode - > i_rdev = MKDEV ( le64_to_cpu ( pfindData - > DevMajor ) ,
le64_to_cpu ( pfindData - > DevMinor ) & MINORMASK ) ;
} else if ( type = = UNIX_FIFO ) {
* pobject_type = DT_FIFO ;
tmp_inode - > i_mode | = S_IFIFO ;
} else if ( type = = UNIX_SOCKET ) {
* pobject_type = DT_SOCK ;
tmp_inode - > i_mode | = S_IFSOCK ;
2005-11-18 22:31:10 +03:00
} else {
/* safest to just call it a file */
* pobject_type = DT_REG ;
tmp_inode - > i_mode | = S_IFREG ;
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " unknown inode type %d " , type ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-01 00:13:06 +04:00
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_OVERR_UID )
tmp_inode - > i_uid = cifs_sb - > mnt_uid ;
else
tmp_inode - > i_uid = le64_to_cpu ( pfindData - > Uid ) ;
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_OVERR_GID )
tmp_inode - > i_gid = cifs_sb - > mnt_gid ;
else
tmp_inode - > i_gid = le64_to_cpu ( pfindData - > Gid ) ;
2005-04-17 02:20:36 +04:00
tmp_inode - > i_nlink = le64_to_cpu ( pfindData - > Nlinks ) ;
2007-02-26 19:46:11 +03:00
spin_lock ( & tmp_inode - > i_lock ) ;
2007-02-08 21:14:13 +03:00
if ( is_size_safe_to_change ( cifsInfo , end_of_file ) ) {
2007-07-07 23:25:05 +04:00
/* can not safely change the file size here if the
2005-04-17 02:20:36 +04:00
client is writing to it due to potential races */
2007-02-08 21:14:13 +03:00
i_size_write ( tmp_inode , end_of_file ) ;
2005-04-17 02:20:36 +04:00
/* 512 bytes (2**9) is the fake blocksize that must be used */
/* for this calculation, not the real blocksize */
tmp_inode - > i_blocks = ( 512 - 1 + num_of_bytes ) > > 9 ;
}
2007-02-26 19:46:11 +03:00
spin_unlock ( & tmp_inode - > i_lock ) ;
2005-04-17 02:20:36 +04:00
if ( S_ISREG ( tmp_inode - > i_mode ) ) {
cFYI ( 1 , ( " File inode " ) ) ;
tmp_inode - > i_op = & cifs_file_inode_ops ;
2006-01-09 07:12:58 +03:00
2007-05-01 00:13:06 +04:00
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO ) {
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_NO_BRL )
2006-01-09 07:12:58 +03:00
tmp_inode - > i_fop = & cifs_file_direct_nobrl_ops ;
else
tmp_inode - > i_fop = & cifs_file_direct_ops ;
2007-05-01 00:13:06 +04:00
} else if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_NO_BRL )
2006-01-09 07:12:58 +03:00
tmp_inode - > i_fop = & cifs_file_nobrl_ops ;
2005-04-17 02:20:36 +04:00
else
tmp_inode - > i_fop = & cifs_file_ops ;
2006-01-09 07:12:58 +03:00
2007-05-01 00:13:06 +04:00
if ( ( cifs_sb - > tcon ) & & ( cifs_sb - > tcon - > ses ) & &
2007-07-07 23:25:05 +04:00
( cifs_sb - > tcon - > ses - > server - > maxBuf <
2006-06-01 23:41:23 +04:00
PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE ) )
tmp_inode - > i_data . a_ops = & cifs_addr_ops_smallbuf ;
else
tmp_inode - > i_data . a_ops = & cifs_addr_ops ;
2005-04-29 09:41:08 +04:00
2007-05-01 00:13:06 +04:00
if ( isNewInode )
2007-07-07 23:25:05 +04:00
return ; /* No sense invalidating pages for new inode
since we have not started caching readahead
file data for it yet */
2005-04-29 09:41:08 +04:00
if ( timespec_equal ( & tmp_inode - > i_mtime , & local_mtime ) & &
( local_size = = tmp_inode - > i_size ) ) {
cFYI ( 1 , ( " inode exists but unchanged " ) ) ;
} else {
/* file may have changed on server */
cFYI ( 1 , ( " invalidate inode, readdir detected change " ) ) ;
invalidate_remote_inode ( tmp_inode ) ;
}
2005-04-17 02:20:36 +04:00
} else if ( S_ISDIR ( tmp_inode - > i_mode ) ) {
cFYI ( 1 , ( " Directory inode " ) ) ;
tmp_inode - > i_op = & cifs_dir_inode_ops ;
tmp_inode - > i_fop = & cifs_dir_ops ;
} else if ( S_ISLNK ( tmp_inode - > i_mode ) ) {
cFYI ( 1 , ( " Symbolic Link inode " ) ) ;
tmp_inode - > i_op = & cifs_symlink_inode_ops ;
/* tmp_inode->i_fop = */ /* do not need to set to anything */
} else {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " Special inode " ) ) ;
2005-04-17 02:20:36 +04:00
init_special_inode ( tmp_inode , tmp_inode - > i_mode ,
tmp_inode - > i_rdev ) ;
}
}
static int initiate_cifs_search ( const int xid , struct file * file )
{
int rc = 0 ;
2007-07-08 19:40:40 +04:00
char * full_path ;
struct cifsFileInfo * cifsFile ;
2005-04-17 02:20:36 +04:00
struct cifs_sb_info * cifs_sb ;
struct cifsTconInfo * pTcon ;
2007-05-01 00:13:06 +04:00
if ( file - > private_data = = NULL ) {
2007-07-08 19:40:40 +04:00
file - > private_data =
kzalloc ( sizeof ( struct cifsFileInfo ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-01 00:13:06 +04:00
if ( file - > private_data = = NULL )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
cifsFile = file - > private_data ;
2008-04-29 04:06:05 +04:00
cifsFile - > invalidHandle = true ;
cifsFile - > srch_inf . endOfSearch = false ;
2005-04-17 02:20:36 +04:00
2006-12-08 13:36:48 +03:00
cifs_sb = CIFS_SB ( file - > f_path . dentry - > d_sb ) ;
2007-05-01 00:13:06 +04:00
if ( cifs_sb = = NULL )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
pTcon = cifs_sb - > tcon ;
2007-05-01 00:13:06 +04:00
if ( pTcon = = NULL )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-12-08 13:36:48 +03:00
full_path = build_path_from_dentry ( file - > f_path . dentry ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 02:25:02 +03:00
if ( full_path = = NULL )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2005-04-29 09:41:08 +04:00
cFYI ( 1 , ( " Full path: %s start at: %lld " , full_path , file - > f_pos ) ) ;
2005-04-17 02:20:36 +04:00
2005-04-29 09:41:04 +04:00
ffirst_retry :
2005-04-17 02:20:36 +04:00
/* test for Unix extensions */
2007-07-19 03:21:09 +04:00
/* but now check for them on the share/mount not on the SMB session */
/* if (pTcon->ses->capabilities & CAP_UNIX) { */
2008-02-08 02:25:02 +03:00
if ( pTcon - > unix_ext )
2006-06-07 04:18:43 +04:00
cifsFile - > srch_inf . info_level = SMB_FIND_FILE_UNIX ;
2008-02-08 02:25:02 +03:00
else if ( ( pTcon - > ses - > capabilities &
2006-06-07 04:18:43 +04:00
( CAP_NT_SMBS | CAP_NT_FIND ) ) = = 0 ) {
cifsFile - > srch_inf . info_level = SMB_FIND_FILE_INFO_STANDARD ;
2005-04-17 02:20:36 +04:00
} else if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM ) {
cifsFile - > srch_inf . info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO ;
} else /* not srvinos - BB fixme add check for backlevel? */ {
cifsFile - > srch_inf . info_level = SMB_FIND_FILE_DIRECTORY_INFO ;
}
2007-07-08 19:40:40 +04:00
rc = CIFSFindFirst ( xid , pTcon , full_path , cifs_sb - > local_nls ,
2005-04-29 09:41:06 +04:00
& cifsFile - > netfid , & cifsFile - > srch_inf ,
2007-07-08 19:40:40 +04:00
cifs_sb - > mnt_cifs_flags &
2005-09-16 08:47:30 +04:00
CIFS_MOUNT_MAP_SPECIAL_CHR , CIFS_DIR_SEP ( cifs_sb ) ) ;
2007-05-01 00:13:06 +04:00
if ( rc = = 0 )
2008-04-29 04:06:05 +04:00
cifsFile - > invalidHandle = false ;
2007-07-08 19:40:40 +04:00
if ( ( rc = = - EOPNOTSUPP ) & &
2005-04-29 09:41:04 +04:00
( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM ) ) {
cifs_sb - > mnt_cifs_flags & = ~ CIFS_MOUNT_SERVER_INUM ;
goto ffirst_retry ;
}
2005-04-17 02:20:36 +04:00
kfree ( full_path ) ;
return rc ;
}
/* return length of unicode string in bytes */
static int cifs_unicode_bytelen ( char * str )
{
int len ;
2007-11-06 00:46:10 +03:00
__le16 * ustr = ( __le16 * ) str ;
2005-04-17 02:20:36 +04:00
2007-07-08 19:40:40 +04:00
for ( len = 0 ; len < = PATH_MAX ; len + + ) {
2007-05-01 00:13:06 +04:00
if ( ustr [ len ] = = 0 )
2005-04-17 02:20:36 +04:00
return len < < 1 ;
}
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " Unicode string longer than PATH_MAX found " ) ) ;
2005-04-17 02:20:36 +04:00
return len < < 1 ;
}
2006-06-07 04:18:43 +04:00
static char * nxt_dir_entry ( char * old_entry , char * end_of_smb , int level )
2005-04-17 02:20:36 +04:00
{
2007-07-08 19:40:40 +04:00
char * new_entry ;
2008-02-08 02:25:02 +03:00
FILE_DIRECTORY_INFO * pDirInfo = ( FILE_DIRECTORY_INFO * ) old_entry ;
2005-04-17 02:20:36 +04:00
2007-05-01 00:13:06 +04:00
if ( level = = SMB_FIND_FILE_INFO_STANDARD ) {
2008-02-08 02:25:02 +03:00
FIND_FILE_STANDARD_INFO * pfData ;
2006-06-07 04:18:43 +04:00
pfData = ( FIND_FILE_STANDARD_INFO * ) pDirInfo ;
new_entry = old_entry + sizeof ( FIND_FILE_STANDARD_INFO ) +
pfData - > FileNameLength ;
} else
new_entry = old_entry + le32_to_cpu ( pDirInfo - > NextEntryOffset ) ;
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " new entry %p old entry %p " , new_entry , old_entry ) ) ;
2005-04-17 02:20:36 +04:00
/* validate that new_entry is not past end of SMB */
2007-05-01 00:13:06 +04:00
if ( new_entry > = end_of_smb ) {
2005-04-29 09:41:08 +04:00
cERROR ( 1 ,
( " search entry %p began after end of SMB %p old entry %p " ,
2007-07-08 19:40:40 +04:00
new_entry , end_of_smb , old_entry ) ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
2007-05-01 00:13:06 +04:00
} else if ( ( ( level = = SMB_FIND_FILE_INFO_STANDARD ) & &
2007-07-08 19:40:40 +04:00
( new_entry + sizeof ( FIND_FILE_STANDARD_INFO ) > end_of_smb ) )
| | ( ( level ! = SMB_FIND_FILE_INFO_STANDARD ) & &
2006-06-07 04:18:43 +04:00
( new_entry + sizeof ( FILE_DIRECTORY_INFO ) > end_of_smb ) ) ) {
2007-07-08 19:40:40 +04:00
cERROR ( 1 , ( " search entry %p extends after end of SMB %p " ,
2005-04-29 09:41:08 +04:00
new_entry , end_of_smb ) ) ;
return NULL ;
2007-07-08 19:40:40 +04:00
} else
2005-04-17 02:20:36 +04:00
return new_entry ;
}
# define UNICODE_DOT cpu_to_le16(0x2e)
/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
static int cifs_entry_is_dot ( char * current_entry , struct cifsFileInfo * cfile )
{
int rc = 0 ;
2007-07-08 19:40:40 +04:00
char * filename = NULL ;
int len = 0 ;
2005-04-17 02:20:36 +04:00
2007-05-01 00:13:06 +04:00
if ( cfile - > srch_inf . info_level = = SMB_FIND_FILE_UNIX ) {
2008-02-08 02:25:02 +03:00
FILE_UNIX_INFO * pFindData = ( FILE_UNIX_INFO * ) current_entry ;
2005-04-17 02:20:36 +04:00
filename = & pFindData - > FileName [ 0 ] ;
2007-05-01 00:13:06 +04:00
if ( cfile - > srch_inf . unicode ) {
2005-04-17 02:20:36 +04:00
len = cifs_unicode_bytelen ( filename ) ;
} else {
/* BB should we make this strnlen of PATH_MAX? */
len = strnlen ( filename , 5 ) ;
}
2007-05-01 00:13:06 +04:00
} else if ( cfile - > srch_inf . info_level = = SMB_FIND_FILE_DIRECTORY_INFO ) {
2008-02-08 02:25:02 +03:00
FILE_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
2007-07-08 19:40:40 +04:00
} else if ( cfile - > srch_inf . info_level = =
2006-06-07 04:18:43 +04:00
SMB_FIND_FILE_FULL_DIRECTORY_INFO ) {
2008-02-08 02:25:02 +03:00
FILE_FULL_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_FULL_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
2007-05-01 00:13:06 +04:00
} else if ( cfile - > srch_inf . info_level = =
2006-06-07 04:18:43 +04:00
SMB_FIND_FILE_ID_FULL_DIR_INFO ) {
2008-02-08 02:25:02 +03:00
SEARCH_ID_FULL_DIR_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( SEARCH_ID_FULL_DIR_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
2007-07-08 19:40:40 +04:00
} else if ( cfile - > srch_inf . info_level = =
2006-06-07 04:18:43 +04:00
SMB_FIND_FILE_BOTH_DIRECTORY_INFO ) {
2008-02-08 02:25:02 +03:00
FILE_BOTH_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_BOTH_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
2007-05-01 00:13:06 +04:00
} else if ( cfile - > srch_inf . info_level = = SMB_FIND_FILE_INFO_STANDARD ) {
2008-02-08 02:25:02 +03:00
FIND_FILE_STANDARD_INFO * pFindData =
2006-06-07 04:18:43 +04:00
( FIND_FILE_STANDARD_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
2006-08-15 17:35:48 +04:00
len = pFindData - > FileNameLength ;
2005-04-17 02:20:36 +04:00
} else {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " Unknown findfirst level %d " ,
cfile - > srch_inf . info_level ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-01 00:13:06 +04:00
if ( filename ) {
if ( cfile - > srch_inf . unicode ) {
2005-04-17 02:20:36 +04:00
__le16 * ufilename = ( __le16 * ) filename ;
2007-05-01 00:13:06 +04:00
if ( len = = 2 ) {
2005-04-17 02:20:36 +04:00
/* check for . */
2007-05-01 00:13:06 +04:00
if ( ufilename [ 0 ] = = UNICODE_DOT )
2005-04-17 02:20:36 +04:00
rc = 1 ;
2007-05-01 00:13:06 +04:00
} else if ( len = = 4 ) {
2005-04-17 02:20:36 +04:00
/* check for .. */
2007-05-01 00:13:06 +04:00
if ( ( ufilename [ 0 ] = = UNICODE_DOT )
2007-07-08 19:40:40 +04:00
& & ( ufilename [ 1 ] = = UNICODE_DOT ) )
2005-04-17 02:20:36 +04:00
rc = 2 ;
}
} else /* ASCII */ {
2007-05-01 00:13:06 +04:00
if ( len = = 1 ) {
2007-07-08 19:40:40 +04:00
if ( filename [ 0 ] = = ' . ' )
2005-04-17 02:20:36 +04:00
rc = 1 ;
2007-05-01 00:13:06 +04:00
} else if ( len = = 2 ) {
2007-07-07 23:25:05 +04:00
if ( ( filename [ 0 ] = = ' . ' ) & & ( filename [ 1 ] = = ' . ' ) )
2005-04-17 02:20:36 +04:00
rc = 2 ;
}
}
}
return rc ;
}
2005-09-16 08:47:30 +04:00
/* Check if directory that we are searching has changed so we can decide
whether we can use the cached search results from the previous search */
2007-07-08 19:40:40 +04:00
static int is_dir_changed ( struct file * file )
2005-09-16 08:47:30 +04:00
{
2007-04-02 22:47:20 +04:00
struct inode * inode = file - > f_path . dentry - > d_inode ;
struct cifsInodeInfo * cifsInfo = CIFS_I ( inode ) ;
2005-09-16 08:47:30 +04:00
2007-04-02 22:47:20 +04:00
if ( cifsInfo - > time = = 0 )
2005-09-16 08:47:30 +04:00
return 1 ; /* directory was changed, perhaps due to unlink */
else
return 0 ;
}
2005-04-17 02:20:36 +04:00
/* find the corresponding entry in the search */
/* Note that the SMB server returns search entries for . and .. which
complicates logic here if we choose to parse for them and we do not
assume that they are located in the findfirst return buffer . */
/* We start counting in the buffer with entry 2 and increment for every
entry ( do not increment for . or . . entry ) */
static int find_cifs_entry ( const int xid , struct cifsTconInfo * pTcon ,
2007-07-08 19:40:40 +04:00
struct file * file , char * * ppCurrentEntry , int * num_to_ret )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
int pos_in_buf = 0 ;
loff_t first_entry_in_buffer ;
loff_t index_to_find = file - > f_pos ;
2007-07-08 19:40:40 +04:00
struct cifsFileInfo * cifsFile = file - > private_data ;
2005-04-17 02:20:36 +04:00
/* check if index in the buffer */
2007-07-13 04:33:32 +04:00
2007-07-08 19:40:40 +04:00
if ( ( cifsFile = = NULL ) | | ( ppCurrentEntry = = NULL ) | |
2005-09-16 08:47:30 +04:00
( num_to_ret = = NULL ) )
2005-04-17 02:20:36 +04:00
return - ENOENT ;
2007-07-13 04:33:32 +04:00
2005-04-17 02:20:36 +04:00
* ppCurrentEntry = NULL ;
2007-07-08 19:40:40 +04:00
first_entry_in_buffer =
cifsFile - > srch_inf . index_of_last_entry -
2005-04-17 02:20:36 +04:00
cifsFile - > srch_inf . entries_in_buffer ;
2006-04-22 19:53:05 +04:00
/* if first entry in buf is zero then is first buffer
in search response data which means it is likely . and . .
will be in this buffer , although some servers do not return
. and . . for the root of a drive and for those we need
to start two entries earlier */
2006-06-01 02:40:51 +04:00
dump_cifs_file_struct ( file , " In fce " ) ;
2007-07-08 19:40:40 +04:00
if ( ( ( index_to_find < cifsFile - > srch_inf . index_of_last_entry ) & &
is_dir_changed ( file ) ) | |
2005-09-16 08:47:30 +04:00
( index_to_find < first_entry_in_buffer ) ) {
2005-04-17 02:20:36 +04:00
/* close and restart search */
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " search backing up - close and restart search " ) ) ;
2008-05-14 01:39:32 +04:00
if ( ! cifsFile - > srch_inf . endOfSearch & &
! cifsFile - > invalidHandle ) {
cifsFile - > invalidHandle = true ;
CIFSFindClose ( xid , pTcon , cifsFile - > netfid ) ;
}
2007-05-01 00:13:06 +04:00
if ( cifsFile - > srch_inf . ntwrk_buf_start ) {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " freeing SMB ff cache buf on search rewind " ) ) ;
2007-05-01 00:13:06 +04:00
if ( cifsFile - > srch_inf . smallBuf )
2006-02-28 06:45:48 +03:00
cifs_small_buf_release ( cifsFile - > srch_inf .
ntwrk_buf_start ) ;
else
cifs_buf_release ( cifsFile - > srch_inf .
ntwrk_buf_start ) ;
2008-07-24 18:48:33 +04:00
cifsFile - > srch_inf . ntwrk_buf_start = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-07-08 19:40:40 +04:00
rc = initiate_cifs_search ( xid , file ) ;
2007-05-01 00:13:06 +04:00
if ( rc ) {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " error %d reinitiating a search on rewind " ,
rc ) ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
}
2007-07-08 19:40:40 +04:00
while ( ( index_to_find > = cifsFile - > srch_inf . index_of_last_entry ) & &
2008-04-29 04:06:05 +04:00
( rc = = 0 ) & & ! cifsFile - > srch_inf . endOfSearch ) {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " calling findnext2 " ) ) ;
2007-07-08 19:40:40 +04:00
rc = CIFSFindNext ( xid , pTcon , cifsFile - > netfid ,
2005-06-23 04:13:47 +04:00
& cifsFile - > srch_inf ) ;
2007-05-01 00:13:06 +04:00
if ( rc )
2005-04-17 02:20:36 +04:00
return - ENOENT ;
}
2007-05-01 00:13:06 +04:00
if ( index_to_find < cifsFile - > srch_inf . index_of_last_entry ) {
2005-04-17 02:20:36 +04:00
/* we found the buffer that contains the entry */
/* scan and find it */
int i ;
2007-07-08 19:40:40 +04:00
char * current_entry ;
char * end_of_smb = cifsFile - > srch_inf . ntwrk_buf_start +
2005-04-17 02:20:36 +04:00
smbCalcSize ( ( struct smb_hdr * )
cifsFile - > srch_inf . ntwrk_buf_start ) ;
2006-04-22 19:53:05 +04:00
current_entry = cifsFile - > srch_inf . srch_entries_start ;
2005-04-17 02:20:36 +04:00
first_entry_in_buffer = cifsFile - > srch_inf . index_of_last_entry
- cifsFile - > srch_inf . entries_in_buffer ;
pos_in_buf = index_to_find - first_entry_in_buffer ;
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " found entry - pos_in_buf %d " , pos_in_buf ) ) ;
2006-06-07 04:18:43 +04:00
2008-02-08 02:25:02 +03:00
for ( i = 0 ; ( i < ( pos_in_buf ) ) & & ( current_entry ! = NULL ) ; i + + ) {
2005-06-23 04:13:47 +04:00
/* go entry by entry figuring out which is first */
2007-07-08 19:40:40 +04:00
current_entry = nxt_dir_entry ( current_entry , end_of_smb ,
2006-06-07 04:18:43 +04:00
cifsFile - > srch_inf . info_level ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-07 23:25:05 +04:00
if ( ( current_entry = = NULL ) & & ( i < pos_in_buf ) ) {
2005-04-17 02:20:36 +04:00
/* BB fixme - check if we should flag this error */
2007-07-08 19:40:40 +04:00
cERROR ( 1 , ( " reached end of buf searching for pos in buf "
2005-04-17 02:20:36 +04:00
" %d index to find %lld rc %d " ,
2007-07-08 19:40:40 +04:00
pos_in_buf , index_to_find , rc ) ) ;
2005-04-17 02:20:36 +04:00
}
rc = 0 ;
* ppCurrentEntry = current_entry ;
} else {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " index not in buffer - could not findnext into it " ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-07-07 23:25:05 +04:00
if ( pos_in_buf > = cifsFile - > srch_inf . entries_in_buffer ) {
cFYI ( 1 , ( " can not return entries pos_in_buf beyond last " ) ) ;
2005-04-17 02:20:36 +04:00
* num_to_ret = 0 ;
} else
* num_to_ret = cifsFile - > srch_inf . entries_in_buffer - pos_in_buf ;
return rc ;
}
/* inode num, inode type and filename returned */
static int cifs_get_name_from_search_buf ( struct qstr * pqst ,
char * current_entry , __u16 level , unsigned int unicode ,
2007-07-08 19:40:40 +04:00
struct cifs_sb_info * cifs_sb , int max_len , ino_t * pinum )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
unsigned int len = 0 ;
2007-07-08 19:40:40 +04:00
char * filename ;
struct nls_table * nlt = cifs_sb - > local_nls ;
2005-04-17 02:20:36 +04:00
* pinum = 0 ;
2007-07-07 23:25:05 +04:00
if ( level = = SMB_FIND_FILE_UNIX ) {
2007-07-08 19:40:40 +04:00
FILE_UNIX_INFO * pFindData = ( FILE_UNIX_INFO * ) current_entry ;
2005-04-17 02:20:36 +04:00
filename = & pFindData - > FileName [ 0 ] ;
2007-07-07 23:25:05 +04:00
if ( unicode ) {
2005-04-17 02:20:36 +04:00
len = cifs_unicode_bytelen ( filename ) ;
} else {
/* BB should we make this strnlen of PATH_MAX? */
len = strnlen ( filename , PATH_MAX ) ;
}
2007-07-08 19:40:40 +04:00
/* BB fixme - hash low and high 32 bits if not 64 bit arch BB */
2007-07-07 23:25:05 +04:00
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM )
2005-04-17 02:20:36 +04:00
* pinum = pFindData - > UniqueId ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_DIRECTORY_INFO ) {
2007-07-08 19:40:40 +04:00
FILE_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_FULL_DIRECTORY_INFO ) {
2007-07-08 19:40:40 +04:00
FILE_FULL_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_FULL_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_ID_FULL_DIR_INFO ) {
2007-07-08 19:40:40 +04:00
SEARCH_ID_FULL_DIR_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( SEARCH_ID_FULL_DIR_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
* pinum = pFindData - > UniqueId ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_BOTH_DIRECTORY_INFO ) {
2007-07-08 19:40:40 +04:00
FILE_BOTH_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_BOTH_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_INFO_STANDARD ) {
2008-02-08 02:25:02 +03:00
FIND_FILE_STANDARD_INFO * pFindData =
2006-06-07 04:18:43 +04:00
( FIND_FILE_STANDARD_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
/* one byte length, no name conversion */
len = ( unsigned int ) pFindData - > FileNameLength ;
2005-04-17 02:20:36 +04:00
} else {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " Unknown findfirst level %d " , level ) ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2006-06-07 04:18:43 +04:00
2007-07-07 23:25:05 +04:00
if ( len > max_len ) {
2007-07-08 19:40:40 +04:00
cERROR ( 1 , ( " bad search response length %d past smb end " , len ) ) ;
2006-06-07 04:18:43 +04:00
return - EINVAL ;
}
2007-07-07 23:25:05 +04:00
if ( unicode ) {
2005-04-17 02:20:36 +04:00
/* BB fixme - test with long names */
/* Note converted filename can be longer than in unicode */
2007-07-07 23:25:05 +04:00
if ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR )
2005-04-29 09:41:05 +04:00
pqst - > len = cifs_convertUCSpath ( ( char * ) pqst - > name ,
( __le16 * ) filename , len / 2 , nlt ) ;
else
pqst - > len = cifs_strfromUCS_le ( ( char * ) pqst - > name ,
2007-07-08 19:40:40 +04:00
( __le16 * ) filename , len / 2 , nlt ) ;
2005-04-17 02:20:36 +04:00
} else {
pqst - > name = filename ;
pqst - > len = len ;
}
2007-07-08 19:40:40 +04:00
pqst - > hash = full_name_hash ( pqst - > name , pqst - > len ) ;
2007-07-07 23:25:05 +04:00
/* cFYI(1, ("filldir on %s",pqst->name)); */
2005-04-17 02:20:36 +04:00
return rc ;
}
static int cifs_filldir ( char * pfindEntry , struct file * file ,
2006-06-07 04:18:43 +04:00
filldir_t filldir , void * direntry , char * scratch_buf , int max_len )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
struct qstr qstring ;
2007-07-08 19:40:40 +04:00
struct cifsFileInfo * pCifsF ;
2007-10-12 23:10:28 +04:00
unsigned int obj_type ;
2005-04-17 02:20:36 +04:00
ino_t inum ;
2007-07-08 19:40:40 +04:00
struct cifs_sb_info * cifs_sb ;
2005-04-17 02:20:36 +04:00
struct inode * tmp_inode ;
struct dentry * tmp_dentry ;
/* get filename and len into qstring */
/* get dentry */
/* decide whether to create and populate ionde */
2007-07-07 23:25:05 +04:00
if ( ( direntry = = NULL ) | | ( file = = NULL ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
pCifsF = file - > private_data ;
2007-07-13 04:33:32 +04:00
2007-07-07 23:25:05 +04:00
if ( ( scratch_buf = = NULL ) | | ( pfindEntry = = NULL ) | | ( pCifsF = = NULL ) )
2005-04-17 02:20:36 +04:00
return - ENOENT ;
2007-07-08 19:40:40 +04:00
rc = cifs_entry_is_dot ( pfindEntry , pCifsF ) ;
2006-04-22 19:53:05 +04:00
/* skip . and .. since we added them first */
2007-07-07 23:25:05 +04:00
if ( rc ! = 0 )
2006-04-22 19:53:05 +04:00
return 0 ;
2006-12-08 13:36:48 +03:00
cifs_sb = CIFS_SB ( file - > f_path . dentry - > d_sb ) ;
2005-04-17 02:20:36 +04:00
qstring . name = scratch_buf ;
2007-07-08 19:40:40 +04:00
rc = cifs_get_name_from_search_buf ( & qstring , pfindEntry ,
2005-04-17 02:20:36 +04:00
pCifsF - > srch_inf . info_level ,
2007-07-08 19:40:40 +04:00
pCifsF - > srch_inf . unicode , cifs_sb ,
2006-06-07 04:18:43 +04:00
max_len ,
2005-04-17 02:20:36 +04:00
& inum /* returned */ ) ;
2007-07-07 23:25:05 +04:00
if ( rc )
2005-04-17 02:20:36 +04:00
return rc ;
2007-07-08 19:40:40 +04:00
rc = construct_dentry ( & qstring , file , & tmp_inode , & tmp_dentry ) ;
2007-07-07 23:25:05 +04:00
if ( ( tmp_inode = = NULL ) | | ( tmp_dentry = = NULL ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2007-07-07 23:25:05 +04:00
if ( rc ) {
2005-04-17 02:20:36 +04:00
/* inode created, we need to hash it with right inode number */
2007-07-07 23:25:05 +04:00
if ( inum ! = 0 ) {
2007-07-08 19:40:40 +04:00
/* BB fixme - hash the 2 32 quantities bits together if
* necessary BB */
2005-04-17 02:20:36 +04:00
tmp_inode - > i_ino = inum ;
}
insert_inode_hash ( tmp_inode ) ;
}
2005-04-29 09:41:08 +04:00
/* we pass in rc below, indicating whether it is a new inode,
so we can figure out whether to invalidate the inode cached
data if the file has changed */
2007-07-07 23:25:05 +04:00
if ( pCifsF - > srch_inf . info_level = = SMB_FIND_FILE_UNIX )
2005-04-29 09:41:08 +04:00
unix_fill_in_inode ( tmp_inode ,
2006-06-07 04:18:43 +04:00
( FILE_UNIX_INFO * ) pfindEntry ,
& obj_type , rc ) ;
2007-07-07 23:25:05 +04:00
else if ( pCifsF - > srch_inf . info_level = = SMB_FIND_FILE_INFO_STANDARD )
2006-06-07 04:18:43 +04:00
fill_in_inode ( tmp_inode , 0 /* old level 1 buffer type */ ,
pfindEntry , & obj_type , rc ) ;
else
fill_in_inode ( tmp_inode , 1 /* NT */ , pfindEntry , & obj_type , rc ) ;
2006-09-07 02:02:22 +04:00
2007-07-07 23:25:05 +04:00
if ( rc ) /* new inode - needs to be tied to dentry */ {
2006-09-07 02:02:22 +04:00
d_instantiate ( tmp_dentry , tmp_inode ) ;
2007-07-07 23:25:05 +04:00
if ( rc = = 2 )
2006-09-07 02:02:22 +04:00
d_rehash ( tmp_dentry ) ;
}
2007-07-13 04:33:32 +04:00
2007-07-08 19:40:40 +04:00
rc = filldir ( direntry , qstring . name , qstring . len , file - > f_pos ,
tmp_inode - > i_ino , obj_type ) ;
2007-07-07 23:25:05 +04:00
if ( rc ) {
cFYI ( 1 , ( " filldir rc = %d " , rc ) ) ;
2006-10-31 00:42:57 +03:00
/* we can not return filldir errors to the caller
since they are " normal " when the stat blocksize
is too small - we return remapped error instead */
rc = - EOVERFLOW ;
2005-04-17 02:20:36 +04:00
}
dput ( tmp_dentry ) ;
return rc ;
}
static int cifs_save_resume_key ( const char * current_entry ,
struct cifsFileInfo * cifsFile )
{
int rc = 0 ;
unsigned int len = 0 ;
__u16 level ;
2007-07-08 19:40:40 +04:00
char * filename ;
2005-04-17 02:20:36 +04:00
2007-07-07 23:25:05 +04:00
if ( ( cifsFile = = NULL ) | | ( current_entry = = NULL ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
level = cifsFile - > srch_inf . info_level ;
2007-07-07 23:25:05 +04:00
if ( level = = SMB_FIND_FILE_UNIX ) {
2008-02-08 02:25:02 +03:00
FILE_UNIX_INFO * pFindData = ( FILE_UNIX_INFO * ) current_entry ;
2005-04-17 02:20:36 +04:00
filename = & pFindData - > FileName [ 0 ] ;
2007-07-07 23:25:05 +04:00
if ( cifsFile - > srch_inf . unicode ) {
2005-04-17 02:20:36 +04:00
len = cifs_unicode_bytelen ( filename ) ;
} else {
/* BB should we make this strnlen of PATH_MAX? */
len = strnlen ( filename , PATH_MAX ) ;
}
cifsFile - > srch_inf . resume_key = pFindData - > ResumeKey ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_DIRECTORY_INFO ) {
2007-07-08 19:40:40 +04:00
FILE_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
cifsFile - > srch_inf . resume_key = pFindData - > FileIndex ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_FULL_DIRECTORY_INFO ) {
2007-07-08 19:40:40 +04:00
FILE_FULL_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_FULL_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
cifsFile - > srch_inf . resume_key = pFindData - > FileIndex ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_ID_FULL_DIR_INFO ) {
2007-07-08 19:40:40 +04:00
SEARCH_ID_FULL_DIR_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( SEARCH_ID_FULL_DIR_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
cifsFile - > srch_inf . resume_key = pFindData - > FileIndex ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_BOTH_DIRECTORY_INFO ) {
2007-07-13 04:33:32 +04:00
FILE_BOTH_DIRECTORY_INFO * pFindData =
2005-04-17 02:20:36 +04:00
( FILE_BOTH_DIRECTORY_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
len = le32_to_cpu ( pFindData - > FileNameLength ) ;
cifsFile - > srch_inf . resume_key = pFindData - > FileIndex ;
2007-07-07 23:25:05 +04:00
} else if ( level = = SMB_FIND_FILE_INFO_STANDARD ) {
2007-07-08 19:40:40 +04:00
FIND_FILE_STANDARD_INFO * pFindData =
2006-06-07 04:18:43 +04:00
( FIND_FILE_STANDARD_INFO * ) current_entry ;
filename = & pFindData - > FileName [ 0 ] ;
/* one byte length, no name conversion */
len = ( unsigned int ) pFindData - > FileNameLength ;
2006-10-01 23:59:41 +04:00
cifsFile - > srch_inf . resume_key = pFindData - > ResumeKey ;
2005-04-17 02:20:36 +04:00
} else {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " Unknown findfirst level %d " , level ) ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
cifsFile - > srch_inf . resume_name_len = len ;
cifsFile - > srch_inf . presume_name = filename ;
return rc ;
}
int cifs_readdir ( struct file * file , void * direntry , filldir_t filldir )
{
int rc = 0 ;
2007-07-08 19:40:40 +04:00
int xid , i ;
2005-04-17 02:20:36 +04:00
struct cifs_sb_info * cifs_sb ;
struct cifsTconInfo * pTcon ;
struct cifsFileInfo * cifsFile = NULL ;
2007-07-08 19:40:40 +04:00
char * current_entry ;
2005-04-17 02:20:36 +04:00
int num_to_fill = 0 ;
2007-07-08 19:40:40 +04:00
char * tmp_buf = NULL ;
2007-07-13 04:33:32 +04:00
char * end_of_smb ;
2006-06-07 04:18:43 +04:00
int max_len ;
2005-04-17 02:20:36 +04:00
xid = GetXid ( ) ;
2006-12-08 13:36:48 +03:00
cifs_sb = CIFS_SB ( file - > f_path . dentry - > d_sb ) ;
2005-04-17 02:20:36 +04:00
pTcon = cifs_sb - > tcon ;
2007-07-07 23:25:05 +04:00
if ( pTcon = = NULL )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
switch ( ( int ) file - > f_pos ) {
case 0 :
2006-04-22 19:53:05 +04:00
if ( filldir ( direntry , " . " , 1 , file - > f_pos ,
2006-12-08 13:36:48 +03:00
file - > f_path . dentry - > d_inode - > i_ino , DT_DIR ) < 0 ) {
2006-04-22 19:53:05 +04:00
cERROR ( 1 , ( " Filldir for current dir failed " ) ) ;
2005-04-17 02:20:36 +04:00
rc = - ENOMEM ;
break ;
}
2006-04-22 19:53:05 +04:00
file - > f_pos + + ;
2005-04-17 02:20:36 +04:00
case 1 :
2006-04-22 19:53:05 +04:00
if ( filldir ( direntry , " .. " , 2 , file - > f_pos ,
2006-12-08 13:36:48 +03:00
file - > f_path . dentry - > d_parent - > d_inode - > i_ino , DT_DIR ) < 0 ) {
2006-05-31 22:05:34 +04:00
cERROR ( 1 , ( " Filldir for parent dir failed " ) ) ;
2005-04-17 02:20:36 +04:00
rc = - ENOMEM ;
break ;
}
2006-04-22 19:53:05 +04:00
file - > f_pos + + ;
default :
2007-07-08 19:40:40 +04:00
/* 1) If search is active,
is in current search buffer ?
2005-04-17 02:20:36 +04:00
if it before then restart search
if after then keep searching till find it */
2007-07-07 23:25:05 +04:00
if ( file - > private_data = = NULL ) {
2007-07-08 19:40:40 +04:00
rc = initiate_cifs_search ( xid , file ) ;
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " initiate cifs search rc %d " , rc ) ) ;
if ( rc ) {
2005-04-17 02:20:36 +04:00
FreeXid ( xid ) ;
return rc ;
}
}
2007-07-07 23:25:05 +04:00
if ( file - > private_data = = NULL ) {
2005-04-17 02:20:36 +04:00
rc = - EINVAL ;
FreeXid ( xid ) ;
return rc ;
}
cifsFile = file - > private_data ;
if ( cifsFile - > srch_inf . endOfSearch ) {
2007-07-07 23:25:05 +04:00
if ( cifsFile - > srch_inf . emptyDir ) {
2005-04-17 02:20:36 +04:00
cFYI ( 1 , ( " End of search, empty dir " ) ) ;
rc = 0 ;
break ;
}
} /* else {
2008-04-29 04:06:05 +04:00
cifsFile - > invalidHandle = true ;
2005-04-17 02:20:36 +04:00
CIFSFindClose ( xid , pTcon , cifsFile - > netfid ) ;
2008-05-23 21:38:32 +04:00
} */
2005-04-17 02:20:36 +04:00
2007-07-08 19:40:40 +04:00
rc = find_cifs_entry ( xid , pTcon , file ,
& current_entry , & num_to_fill ) ;
2007-07-07 23:25:05 +04:00
if ( rc ) {
cFYI ( 1 , ( " fce error %d " , rc ) ) ;
2005-04-17 02:20:36 +04:00
goto rddir2_exit ;
} else if ( current_entry ! = NULL ) {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " entry %lld found " , file - > f_pos ) ) ;
2005-04-17 02:20:36 +04:00
} else {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " could not find entry " ) ) ;
2005-04-17 02:20:36 +04:00
goto rddir2_exit ;
}
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " loop through %d times filling dir for net buf %p " ,
num_to_fill , cifsFile - > srch_inf . ntwrk_buf_start ) ) ;
2006-06-07 04:18:43 +04:00
max_len = smbCalcSize ( ( struct smb_hdr * )
cifsFile - > srch_inf . ntwrk_buf_start ) ;
end_of_smb = cifsFile - > srch_inf . ntwrk_buf_start + max_len ;
2005-04-29 09:41:05 +04:00
/* To be safe - for UCS to UTF-8 with strings loaded
with the rare long characters alloc more to account for
such multibyte target UTF - 8 characters . cifs_unicode . c ,
which actually does the conversion , has the same limit */
tmp_buf = kmalloc ( ( 2 * NAME_MAX ) + 4 , GFP_KERNEL ) ;
2007-07-07 23:25:05 +04:00
for ( i = 0 ; ( i < num_to_fill ) & & ( rc = = 0 ) ; i + + ) {
if ( current_entry = = NULL ) {
2005-04-17 02:20:36 +04:00
/* evaluate whether this case is an error */
2007-08-31 02:09:15 +04:00
cERROR ( 1 , ( " past SMB end, num to fill %d i %d " ,
2005-04-17 02:20:36 +04:00
num_to_fill , i ) ) ;
break ;
}
2006-04-22 19:53:05 +04:00
/* if buggy server returns . and .. late do
we want to check for that here ? */
2006-06-07 04:18:43 +04:00
rc = cifs_filldir ( current_entry , file ,
filldir , direntry , tmp_buf , max_len ) ;
2007-07-07 23:25:05 +04:00
if ( rc = = - EOVERFLOW ) {
2006-10-31 00:42:57 +03:00
rc = 0 ;
break ;
}
2005-04-17 02:20:36 +04:00
file - > f_pos + + ;
2007-07-07 23:25:05 +04:00
if ( file - > f_pos = =
2006-06-01 02:40:51 +04:00
cifsFile - > srch_inf . index_of_last_entry ) {
2007-07-07 23:25:05 +04:00
cFYI ( 1 , ( " last entry in buf at pos %lld %s " ,
file - > f_pos , tmp_buf ) ) ;
cifs_save_resume_key ( current_entry , cifsFile ) ;
2005-04-17 02:20:36 +04:00
break ;
2007-07-07 23:25:05 +04:00
} else
current_entry =
2006-06-07 04:18:43 +04:00
nxt_dir_entry ( current_entry , end_of_smb ,
cifsFile - > srch_inf . info_level ) ;
2005-04-17 02:20:36 +04:00
}
kfree ( tmp_buf ) ;
break ;
} /* end switch */
rddir2_exit :
FreeXid ( xid ) ;
return rc ;
}