2005-04-17 02:20:36 +04:00
/*
* proc . 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/types.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include <linux/file.h>
# include <linux/stat.h>
# include <linux/fcntl.h>
# include <linux/dcache.h>
# include <linux/dirent.h>
# include <linux/nls.h>
# include <linux/smp_lock.h>
# include <linux/net.h>
# include <linux/vfs.h>
# include <linux/smb_fs.h>
# include <linux/smbno.h>
# include <linux/smb_mount.h>
# include <net/sock.h>
# include <asm/string.h>
# include <asm/div64.h>
# include "smb_debug.h"
# include "proto.h"
# include "request.h"
/* Features. Undefine if they cause problems, this should perhaps be a
config option . */
# define SMBFS_POSIX_UNLINK 1
/* Allow smb_retry to be interrupted. */
# define SMB_RETRY_INTR
# define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
# define SMB_CMD(packet) (*(packet+8))
# define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1))
# define SMB_DIRINFO_SIZE 43
# define SMB_STATUS_SIZE 21
# define SMB_ST_BLKSIZE (PAGE_SIZE)
# define SMB_ST_BLKSHIFT (PAGE_SHIFT)
static struct smb_ops smb_ops_core ;
static struct smb_ops smb_ops_os2 ;
static struct smb_ops smb_ops_win95 ;
static struct smb_ops smb_ops_winNT ;
static struct smb_ops smb_ops_unix ;
static struct smb_ops smb_ops_null ;
static void
smb_init_dirent ( struct smb_sb_info * server , struct smb_fattr * fattr ) ;
static void
smb_finish_dirent ( struct smb_sb_info * server , struct smb_fattr * fattr ) ;
static int
smb_proc_getattr_core ( struct smb_sb_info * server , struct dentry * dir ,
struct smb_fattr * fattr ) ;
static int
smb_proc_getattr_ff ( struct smb_sb_info * server , struct dentry * dentry ,
struct smb_fattr * fattr ) ;
static int
smb_proc_setattr_core ( struct smb_sb_info * server , struct dentry * dentry ,
u16 attr ) ;
static int
smb_proc_setattr_ext ( struct smb_sb_info * server ,
struct inode * inode , struct smb_fattr * fattr ) ;
static int
smb_proc_query_cifsunix ( struct smb_sb_info * server ) ;
static void
install_ops ( struct smb_ops * dst , struct smb_ops * src ) ;
static void
str_upper ( char * name , int len )
{
while ( len - - )
{
if ( * name > = ' a ' & & * name < = ' z ' )
* name - = ( ' a ' - ' A ' ) ;
name + + ;
}
}
#if 0
static void
str_lower ( char * name , int len )
{
while ( len - - )
{
if ( * name > = ' A ' & & * name < = ' Z ' )
* name + = ( ' a ' - ' A ' ) ;
name + + ;
}
}
# endif
/* reverse a string inline. This is used by the dircache walking routines */
static void reverse_string ( char * buf , int len )
{
char c ;
char * end = buf + len - 1 ;
while ( buf < end ) {
c = * buf ;
* ( buf + + ) = * end ;
* ( end - - ) = c ;
}
}
/* no conversion, just a wrapper for memcpy. */
static int convert_memcpy ( unsigned char * output , int olen ,
const unsigned char * input , int ilen ,
struct nls_table * nls_from ,
struct nls_table * nls_to )
{
if ( olen < ilen )
return - ENAMETOOLONG ;
memcpy ( output , input , ilen ) ;
return ilen ;
}
static inline int write_char ( unsigned char ch , char * output , int olen )
{
if ( olen < 4 )
return - ENAMETOOLONG ;
sprintf ( output , " :x%02x " , ch ) ;
return 4 ;
}
static inline int write_unichar ( wchar_t ch , char * output , int olen )
{
if ( olen < 5 )
return - ENAMETOOLONG ;
sprintf ( output , " :%04x " , ch ) ;
return 5 ;
}
/* convert from one "codepage" to another (possibly being utf8). */
static int convert_cp ( unsigned char * output , int olen ,
const unsigned char * input , int ilen ,
struct nls_table * nls_from ,
struct nls_table * nls_to )
{
int len = 0 ;
int n ;
wchar_t ch ;
while ( ilen > 0 ) {
/* convert by changing to unicode and back to the new cp */
n = nls_from - > char2uni ( input , ilen , & ch ) ;
if ( n = = - EINVAL ) {
ilen - - ;
n = write_char ( * input + + , output , olen ) ;
if ( n < 0 )
goto fail ;
output + = n ;
olen - = n ;
len + = n ;
continue ;
} else if ( n < 0 )
goto fail ;
input + = n ;
ilen - = n ;
n = nls_to - > uni2char ( ch , output , olen ) ;
if ( n = = - EINVAL )
n = write_unichar ( ch , output , olen ) ;
if ( n < 0 )
goto fail ;
output + = n ;
olen - = n ;
len + = n ;
}
return len ;
fail :
return n ;
}
/* ----------------------------------------------------------- */
/*
* nls_unicode
*
* This encodes / decodes little endian unicode format
*/
static int uni2char ( wchar_t uni , unsigned char * out , int boundlen )
{
if ( boundlen < 2 )
return - EINVAL ;
* out + + = uni & 0xff ;
* out + + = uni > > 8 ;
return 2 ;
}
static int char2uni ( const unsigned char * rawstring , int boundlen , wchar_t * uni )
{
if ( boundlen < 2 )
return - EINVAL ;
* uni = ( rawstring [ 1 ] < < 8 ) | rawstring [ 0 ] ;
return 2 ;
}
static struct nls_table unicode_table = {
. charset = " unicode " ,
. uni2char = uni2char ,
. char2uni = char2uni ,
} ;
/* ----------------------------------------------------------- */
static int setcodepage ( struct nls_table * * p , char * name )
{
struct nls_table * nls ;
if ( ! name | | ! * name ) {
nls = NULL ;
} else if ( ( nls = load_nls ( name ) ) = = NULL ) {
printk ( KERN_ERR " smbfs: failed to load nls '%s' \n " , name ) ;
return - EINVAL ;
}
/* if already set, unload the previous one. */
if ( * p & & * p ! = & unicode_table )
unload_nls ( * p ) ;
* p = nls ;
return 0 ;
}
/* Handles all changes to codepage settings. */
int smb_setcodepage ( struct smb_sb_info * server , struct smb_nls_codepage * cp )
{
int n = 0 ;
smb_lock_server ( server ) ;
/* Don't load any nls_* at all, if no remote is requested */
if ( ! * cp - > remote_name )
goto out ;
/* local */
n = setcodepage ( & server - > local_nls , cp - > local_name ) ;
if ( n ! = 0 )
goto out ;
/* remote */
if ( ! strcmp ( cp - > remote_name , " unicode " ) ) {
server - > remote_nls = & unicode_table ;
} else {
n = setcodepage ( & server - > remote_nls , cp - > remote_name ) ;
if ( n ! = 0 )
setcodepage ( & server - > local_nls , NULL ) ;
}
out :
if ( server - > local_nls ! = NULL & & server - > remote_nls ! = NULL )
server - > ops - > convert = convert_cp ;
else
server - > ops - > convert = convert_memcpy ;
smb_unlock_server ( server ) ;
return n ;
}
/*****************************************************************************/
/* */
/* Encoding/Decoding section */
/* */
/*****************************************************************************/
static __u8 *
smb_encode_smb_length ( __u8 * p , __u32 len )
{
* p = 0 ;
* ( p + 1 ) = 0 ;
* ( p + 2 ) = ( len & 0xFF00 ) > > 8 ;
* ( p + 3 ) = ( len & 0xFF ) ;
if ( len > 0xFFFF )
{
* ( p + 1 ) = 1 ;
}
return p + 4 ;
}
/*
* smb_build_path : build the path to entry and name storing it in buf .
* The path returned will have the trailing ' \0 ' .
*/
static int smb_build_path ( struct smb_sb_info * server , unsigned char * buf ,
int maxlen ,
struct dentry * entry , struct qstr * name )
{
unsigned char * path = buf ;
int len ;
int unicode = ( server - > mnt - > flags & SMB_MOUNT_UNICODE ) ! = 0 ;
if ( maxlen < ( 2 < < unicode ) )
return - ENAMETOOLONG ;
if ( maxlen > SMB_MAXPATHLEN + 1 )
maxlen = SMB_MAXPATHLEN + 1 ;
if ( entry = = NULL )
goto test_name_and_out ;
/*
* If IS_ROOT , we have to do no walking at all .
*/
if ( IS_ROOT ( entry ) & & ! name ) {
* path + + = ' \\ ' ;
if ( unicode ) * path + + = ' \0 ' ;
* path + + = ' \0 ' ;
if ( unicode ) * path + + = ' \0 ' ;
return path - buf ;
}
/*
* Build the path string walking the tree backward from end to ROOT
* and store it in reversed order [ see reverse_string ( ) ]
*/
dget ( entry ) ;
spin_lock ( & entry - > d_lock ) ;
while ( ! IS_ROOT ( entry ) ) {
struct dentry * parent ;
if ( maxlen < ( 3 < < unicode ) ) {
spin_unlock ( & entry - > d_lock ) ;
dput ( entry ) ;
return - ENAMETOOLONG ;
}
len = server - > ops - > convert ( path , maxlen - 2 ,
entry - > d_name . name , entry - > d_name . len ,
server - > local_nls , server - > remote_nls ) ;
if ( len < 0 ) {
spin_unlock ( & entry - > d_lock ) ;
dput ( entry ) ;
return len ;
}
reverse_string ( path , len ) ;
path + = len ;
if ( unicode ) {
/* Note: reverse order */
* path + + = ' \0 ' ;
maxlen - - ;
}
* path + + = ' \\ ' ;
maxlen - = len + 1 ;
parent = entry - > d_parent ;
dget ( parent ) ;
spin_unlock ( & entry - > d_lock ) ;
dput ( entry ) ;
entry = parent ;
spin_lock ( & entry - > d_lock ) ;
}
spin_unlock ( & entry - > d_lock ) ;
dput ( entry ) ;
reverse_string ( buf , path - buf ) ;
/* maxlen has space for at least one char */
test_name_and_out :
if ( name ) {
if ( maxlen < ( 3 < < unicode ) )
return - ENAMETOOLONG ;
* path + + = ' \\ ' ;
if ( unicode ) {
* path + + = ' \0 ' ;
maxlen - - ;
}
len = server - > ops - > convert ( path , maxlen - 2 ,
name - > name , name - > len ,
server - > local_nls , server - > remote_nls ) ;
if ( len < 0 )
return len ;
path + = len ;
maxlen - = len + 1 ;
}
/* maxlen has space for at least one char */
* path + + = ' \0 ' ;
if ( unicode ) * path + + = ' \0 ' ;
return path - buf ;
}
static int smb_encode_path ( struct smb_sb_info * server , char * buf , int maxlen ,
struct dentry * dir , struct qstr * name )
{
int result ;
result = smb_build_path ( server , buf , maxlen , dir , name ) ;
if ( result < 0 )
goto out ;
if ( server - > opt . protocol < = SMB_PROTOCOL_COREPLUS )
str_upper ( buf , result ) ;
out :
return result ;
}
/* encode_path for non-trans2 request SMBs */
static int smb_simple_encode_path ( struct smb_request * req , char * * p ,
struct dentry * entry , struct qstr * name )
{
struct smb_sb_info * server = req - > rq_server ;
char * s = * p ;
int res ;
int maxlen = ( ( char * ) req - > rq_buffer + req - > rq_bufsize ) - s ;
int unicode = ( server - > mnt - > flags & SMB_MOUNT_UNICODE ) ;
if ( ! maxlen )
return - ENAMETOOLONG ;
* s + + = 4 ; /* ASCII data format */
/*
* SMB Unicode strings must be 16 bit aligned relative the start of the
* packet . If they are not they must be padded with 0.
*/
if ( unicode ) {
int align = s - ( char * ) req - > rq_buffer ;
if ( ! ( align & 1 ) ) {
* s + + = ' \0 ' ;
maxlen - - ;
}
}
res = smb_encode_path ( server , s , maxlen - 1 , entry , name ) ;
if ( res < 0 )
return res ;
* p = s + res ;
return 0 ;
}
/* The following are taken directly from msdos-fs */
/* Linear day numbers of the respective 1sts in non-leap years. */
static int day_n [ ] =
{ 0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 , 0 , 0 , 0 , 0 } ;
/* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
static time_t
utc2local ( struct smb_sb_info * server , time_t time )
{
return time - server - > opt . serverzone * 60 ;
}
static time_t
local2utc ( struct smb_sb_info * server , time_t time )
{
return time + server - > opt . serverzone * 60 ;
}
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
static time_t
date_dos2unix ( struct smb_sb_info * server , __u16 date , __u16 time )
{
int month , year ;
time_t secs ;
/* first subtract and mask after that... Otherwise, if
date = = 0 , bad things happen */
month = ( ( date > > 5 ) - 1 ) & 15 ;
year = date > > 9 ;
secs = ( time & 31 ) * 2 + 60 * ( ( time > > 5 ) & 63 ) + ( time > > 11 ) * 3600 + 86400 *
( ( date & 31 ) - 1 + day_n [ month ] + ( year / 4 ) + year * 365 - ( ( year & 3 ) = = 0 & &
month < 2 ? 1 : 0 ) + 3653 ) ;
/* days since 1.1.70 plus 80's leap day */
return local2utc ( server , secs ) ;
}
/* Convert linear UNIX date to a MS-DOS time/date pair. */
static void
date_unix2dos ( struct smb_sb_info * server ,
int unix_date , __u16 * date , __u16 * time )
{
int day , year , nl_day , month ;
unix_date = utc2local ( server , unix_date ) ;
if ( unix_date < 315532800 )
unix_date = 315532800 ;
* time = ( unix_date % 60 ) / 2 +
( ( ( unix_date / 60 ) % 60 ) < < 5 ) +
( ( ( unix_date / 3600 ) % 24 ) < < 11 ) ;
day = unix_date / 86400 - 3652 ;
year = day / 365 ;
if ( ( year + 3 ) / 4 + 365 * year > day )
year - - ;
day - = ( year + 3 ) / 4 + 365 * year ;
if ( day = = 59 & & ! ( year & 3 ) ) {
nl_day = day ;
month = 2 ;
} else {
nl_day = ( year & 3 ) | | day < = 59 ? day : day - 1 ;
for ( month = 0 ; month < 12 ; month + + )
if ( day_n [ month ] > nl_day )
break ;
}
* date = nl_day - day_n [ month - 1 ] + 1 + ( month < < 5 ) + ( year < < 9 ) ;
}
/* The following are taken from fs/ntfs/util.c */
# define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)
/*
* Convert the NT UTC ( based 1601 - 01 - 01 , in hundred nanosecond units )
* into Unix UTC ( based 1970 - 01 - 01 , in seconds ) .
*/
static struct timespec
smb_ntutc2unixutc ( u64 ntutc )
{
struct timespec ts ;
/* FIXME: what about the timezone difference? */
/* Subtract the NTFS time offset, then convert to 1s intervals. */
u64 t = ntutc - NTFS_TIME_OFFSET ;
ts . tv_nsec = do_div ( t , 10000000 ) * 100 ;
ts . tv_sec = t ;
return ts ;
}
/* Convert the Unix UTC into NT time */
static u64
smb_unixutc2ntutc ( struct timespec ts )
{
/* Note: timezone conversion is probably wrong. */
/* return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET; */
return ( ( u64 ) ts . tv_sec ) * 10000000 + ts . tv_nsec / 100 + NTFS_TIME_OFFSET ;
}
# define MAX_FILE_MODE 6
static mode_t file_mode [ ] = {
S_IFREG , S_IFDIR , S_IFLNK , S_IFCHR , S_IFBLK , S_IFIFO , S_IFSOCK
} ;
static int smb_filetype_to_mode ( u32 filetype )
{
if ( filetype > MAX_FILE_MODE ) {
PARANOIA ( " Filetype out of range: %d \n " , filetype ) ;
return S_IFREG ;
}
return file_mode [ filetype ] ;
}
static u32 smb_filetype_from_mode ( int mode )
{
if ( S_ISREG ( mode ) )
return UNIX_TYPE_FILE ;
if ( S_ISDIR ( mode ) )
return UNIX_TYPE_DIR ;
if ( S_ISLNK ( mode ) )
return UNIX_TYPE_SYMLINK ;
if ( S_ISCHR ( mode ) )
return UNIX_TYPE_CHARDEV ;
if ( S_ISBLK ( mode ) )
return UNIX_TYPE_BLKDEV ;
if ( S_ISFIFO ( mode ) )
return UNIX_TYPE_FIFO ;
if ( S_ISSOCK ( mode ) )
return UNIX_TYPE_SOCKET ;
return UNIX_TYPE_UNKNOWN ;
}
/*****************************************************************************/
/* */
/* Support section. */
/* */
/*****************************************************************************/
__u32
smb_len ( __u8 * p )
{
return ( ( * ( p + 1 ) & 0x1 ) < < 16L ) | ( * ( p + 2 ) < < 8L ) | * ( p + 3 ) ;
}
static __u16
smb_bcc ( __u8 * packet )
{
int pos = SMB_HEADER_LEN + SMB_WCT ( packet ) * sizeof ( __u16 ) ;
return WVAL ( packet , pos ) ;
}
/* smb_valid_packet: We check if packet fulfills the basic
requirements of a smb packet */
static int
smb_valid_packet ( __u8 * packet )
{
return ( packet [ 4 ] = = 0xff
& & packet [ 5 ] = = ' S '
& & packet [ 6 ] = = ' M '
& & packet [ 7 ] = = ' B '
& & ( smb_len ( packet ) + 4 = = SMB_HEADER_LEN
+ SMB_WCT ( packet ) * 2 + smb_bcc ( packet ) ) ) ;
}
/* smb_verify: We check if we got the answer we expected, and if we
got enough data . If bcc = = - 1 , we don ' t care . */
static int
smb_verify ( __u8 * packet , int command , int wct , int bcc )
{
if ( SMB_CMD ( packet ) ! = command )
goto bad_command ;
if ( SMB_WCT ( packet ) < wct )
goto bad_wct ;
if ( bcc ! = - 1 & & smb_bcc ( packet ) < bcc )
goto bad_bcc ;
return 0 ;
bad_command :
printk ( KERN_ERR " smb_verify: command=%x, SMB_CMD=%x?? \n " ,
command , SMB_CMD ( packet ) ) ;
goto fail ;
bad_wct :
printk ( KERN_ERR " smb_verify: command=%x, wct=%d, SMB_WCT=%d?? \n " ,
command , wct , SMB_WCT ( packet ) ) ;
goto fail ;
bad_bcc :
printk ( KERN_ERR " smb_verify: command=%x, bcc=%d, SMB_BCC=%d?? \n " ,
command , bcc , smb_bcc ( packet ) ) ;
fail :
return - EIO ;
}
/*
* Returns the maximum read or write size for the " payload " . Making all of the
* packet fit within the negotiated max_xmit size .
*
* N . B . Since this value is usually computed before locking the server ,
* the server ' s packet size must never be decreased !
*/
static inline int
smb_get_xmitsize ( struct smb_sb_info * server , int overhead )
{
return server - > opt . max_xmit - overhead ;
}
/*
* Calculate the maximum read size
*/
int
smb_get_rsize ( struct smb_sb_info * server )
{
/* readX has 12 parameters, read has 5 */
int overhead = SMB_HEADER_LEN + 12 * sizeof ( __u16 ) + 2 + 1 + 2 ;
int size = smb_get_xmitsize ( server , overhead ) ;
VERBOSE ( " xmit=%d, size=%d \n " , server - > opt . max_xmit , size ) ;
return size ;
}
/*
* Calculate the maximum write size
*/
int
smb_get_wsize ( struct smb_sb_info * server )
{
/* writeX has 14 parameters, write has 5 */
int overhead = SMB_HEADER_LEN + 14 * sizeof ( __u16 ) + 2 + 1 + 2 ;
int size = smb_get_xmitsize ( server , overhead ) ;
VERBOSE ( " xmit=%d, size=%d \n " , server - > opt . max_xmit , size ) ;
return size ;
}
/*
* Convert SMB error codes to - E . . . errno values .
*/
int
smb_errno ( struct smb_request * req )
{
int errcls = req - > rq_rcls ;
int error = req - > rq_err ;
char * class = " Unknown " ;
VERBOSE ( " errcls %d code %d from command 0x%x \n " ,
errcls , error , SMB_CMD ( req - > rq_header ) ) ;
if ( errcls = = ERRDOS ) {
switch ( error ) {
case ERRbadfunc :
return - EINVAL ;
case ERRbadfile :
case ERRbadpath :
return - ENOENT ;
case ERRnofids :
return - EMFILE ;
case ERRnoaccess :
return - EACCES ;
case ERRbadfid :
return - EBADF ;
case ERRbadmcb :
return - EREMOTEIO ;
case ERRnomem :
return - ENOMEM ;
case ERRbadmem :
return - EFAULT ;
case ERRbadenv :
case ERRbadformat :
return - EREMOTEIO ;
case ERRbadaccess :
return - EACCES ;
case ERRbaddata :
return - E2BIG ;
case ERRbaddrive :
return - ENXIO ;
case ERRremcd :
return - EREMOTEIO ;
case ERRdiffdevice :
return - EXDEV ;
case ERRnofiles :
return - ENOENT ;
case ERRbadshare :
return - ETXTBSY ;
case ERRlock :
return - EDEADLK ;
case ERRfilexists :
return - EEXIST ;
case ERROR_INVALID_PARAMETER :
return - EINVAL ;
case ERROR_DISK_FULL :
return - ENOSPC ;
case ERROR_INVALID_NAME :
return - ENOENT ;
case ERROR_DIR_NOT_EMPTY :
return - ENOTEMPTY ;
case ERROR_NOT_LOCKED :
return - ENOLCK ;
case ERROR_ALREADY_EXISTS :
return - EEXIST ;
default :
class = " ERRDOS " ;
goto err_unknown ;
}
} else if ( errcls = = ERRSRV ) {
switch ( error ) {
/* N.B. This is wrong ... EIO ? */
case ERRerror :
return - ENFILE ;
case ERRbadpw :
return - EINVAL ;
case ERRbadtype :
case ERRtimeout :
return - EIO ;
case ERRaccess :
return - EACCES ;
/*
* This is a fatal error , as it means the " tree ID "
* for this connection is no longer valid . We map
* to a special error code and get a new connection .
*/
case ERRinvnid :
return - EBADSLT ;
default :
class = " ERRSRV " ;
goto err_unknown ;
}
} else if ( errcls = = ERRHRD ) {
switch ( error ) {
case ERRnowrite :
return - EROFS ;
case ERRbadunit :
return - ENODEV ;
case ERRnotready :
return - EUCLEAN ;
case ERRbadcmd :
case ERRdata :
return - EIO ;
case ERRbadreq :
return - ERANGE ;
case ERRbadshare :
return - ETXTBSY ;
case ERRlock :
return - EDEADLK ;
case ERRdiskfull :
return - ENOSPC ;
default :
class = " ERRHRD " ;
goto err_unknown ;
}
} else if ( errcls = = ERRCMD ) {
class = " ERRCMD " ;
} else if ( errcls = = SUCCESS ) {
return 0 ; /* This is the only valid 0 return */
}
err_unknown :
printk ( KERN_ERR " smb_errno: class %s, code %d from command 0x%x \n " ,
class , error , SMB_CMD ( req - > rq_header ) ) ;
return - EIO ;
}
/* smb_request_ok: We expect the server to be locked. Then we do the
request and check the answer completely . When smb_request_ok
returns 0 , you can be quite sure that everything went well . When
the answer is < = 0 , the returned number is a valid unix errno . */
static int
smb_request_ok ( struct smb_request * req , int command , int wct , int bcc )
{
int result ;
req - > rq_resp_wct = wct ;
req - > rq_resp_bcc = bcc ;
result = smb_add_request ( req ) ;
if ( result ! = 0 ) {
DEBUG1 ( " smb_request failed \n " ) ;
goto out ;
}
if ( smb_valid_packet ( req - > rq_header ) ! = 0 ) {
PARANOIA ( " invalid packet! \n " ) ;
goto out ;
}
result = smb_verify ( req - > rq_header , command , wct , bcc ) ;
out :
return result ;
}
/*
* This implements the NEWCONN ioctl . It installs the server pid ,
* sets server - > state to CONN_VALID , and wakes up the waiting process .
*/
int
smb_newconn ( struct smb_sb_info * server , struct smb_conn_opt * opt )
{
struct file * filp ;
struct sock * sk ;
int error ;
VERBOSE ( " fd=%d, pid=%d \n " , opt - > fd , current - > pid ) ;
smb_lock_server ( server ) ;
/*
* Make sure we don ' t already have a valid connection . . .
*/
error = - EINVAL ;
if ( server - > state = = CONN_VALID )
goto out ;
error = - EACCES ;
if ( current - > uid ! = server - > mnt - > mounted_uid & &
! capable ( CAP_SYS_ADMIN ) )
goto out ;
error = - EBADF ;
filp = fget ( opt - > fd ) ;
if ( ! filp )
goto out ;
2006-12-08 13:37:39 +03:00
if ( ! smb_valid_socket ( filp - > f_path . dentry - > d_inode ) )
2005-04-17 02:20:36 +04:00
goto out_putf ;
server - > sock_file = filp ;
2006-12-13 11:35:10 +03:00
server - > conn_pid = get_pid ( task_pid ( current ) ) ;
2005-04-17 02:20:36 +04:00
server - > opt = * opt ;
server - > generation + = 1 ;
server - > state = CONN_VALID ;
error = 0 ;
if ( server - > conn_error ) {
/*
* conn_error is the returncode we originally decided to
* drop the old connection on . This message should be positive
* and not make people ask questions on why smbfs is printing
* error messages . . .
*/
printk ( KERN_INFO " SMB connection re-established (%d) \n " ,
server - > conn_error ) ;
server - > conn_error = 0 ;
}
/*
* Store the server in sock user_data ( Only used by sunrpc )
*/
2006-12-08 13:37:39 +03:00
sk = SOCKET_I ( filp - > f_path . dentry - > d_inode ) - > sk ;
2005-04-17 02:20:36 +04:00
sk - > sk_user_data = server ;
/* chain into the data_ready callback */
server - > data_ready = xchg ( & sk - > sk_data_ready , smb_data_ready ) ;
/* check if we have an old smbmount that uses seconds for the
serverzone */
if ( server - > opt . serverzone > 12 * 60 | | server - > opt . serverzone < - 12 * 60 )
server - > opt . serverzone / = 60 ;
/* now that we have an established connection we can detect the server
type and enable bug workarounds */
if ( server - > opt . protocol < SMB_PROTOCOL_LANMAN2 )
install_ops ( server - > ops , & smb_ops_core ) ;
else if ( server - > opt . protocol = = SMB_PROTOCOL_LANMAN2 )
install_ops ( server - > ops , & smb_ops_os2 ) ;
else if ( server - > opt . protocol = = SMB_PROTOCOL_NT1 & &
( server - > opt . max_xmit < 0x1000 ) & &
! ( server - > opt . capabilities & SMB_CAP_NT_SMBS ) ) {
/* FIXME: can we kill the WIN95 flag now? */
server - > mnt - > flags | = SMB_MOUNT_WIN95 ;
VERBOSE ( " detected WIN95 server \n " ) ;
install_ops ( server - > ops , & smb_ops_win95 ) ;
} else {
/*
* Samba has max_xmit 65535
* NT4spX has max_xmit 4536 ( or something like that )
* win2k has . . .
*/
VERBOSE ( " detected NT1 (Samba, NT4/5) server \n " ) ;
install_ops ( server - > ops , & smb_ops_winNT ) ;
}
/* FIXME: the win9x code wants to modify these ... (seek/trunc bug) */
if ( server - > mnt - > flags & SMB_MOUNT_OLDATTR ) {
server - > ops - > getattr = smb_proc_getattr_core ;
} else if ( server - > mnt - > flags & SMB_MOUNT_DIRATTR ) {
server - > ops - > getattr = smb_proc_getattr_ff ;
}
/* Decode server capabilities */
if ( server - > opt . capabilities & SMB_CAP_LARGE_FILES ) {
/* Should be ok to set this now, as no one can access the
mount until the connection has been established . */
SB_of ( server ) - > s_maxbytes = ~ 0ULL > > 1 ;
VERBOSE ( " LFS enabled \n " ) ;
}
if ( server - > opt . capabilities & SMB_CAP_UNICODE ) {
server - > mnt - > flags | = SMB_MOUNT_UNICODE ;
VERBOSE ( " Unicode enabled \n " ) ;
} else {
server - > mnt - > flags & = ~ SMB_MOUNT_UNICODE ;
}
#if 0
/* flags we may test for other patches ... */
if ( server - > opt . capabilities & SMB_CAP_LARGE_READX ) {
VERBOSE ( " Large reads enabled \n " ) ;
}
if ( server - > opt . capabilities & SMB_CAP_LARGE_WRITEX ) {
VERBOSE ( " Large writes enabled \n " ) ;
}
# endif
if ( server - > opt . capabilities & SMB_CAP_UNIX ) {
struct inode * inode ;
VERBOSE ( " Using UNIX CIFS extensions \n " ) ;
install_ops ( server - > ops , & smb_ops_unix ) ;
inode = SB_of ( server ) - > s_root - > d_inode ;
if ( inode )
inode - > i_op = & smb_dir_inode_operations_unix ;
}
VERBOSE ( " protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x \n " ,
2006-12-13 11:35:10 +03:00
server - > opt . protocol , server - > opt . max_xmit ,
pid_nr ( server - > conn_pid ) , server - > opt . capabilities ) ;
2005-04-17 02:20:36 +04:00
/* FIXME: this really should be done by smbmount. */
if ( server - > opt . max_xmit > SMB_MAX_PACKET_SIZE ) {
server - > opt . max_xmit = SMB_MAX_PACKET_SIZE ;
}
smb_unlock_server ( server ) ;
smbiod_wake_up ( ) ;
if ( server - > opt . capabilities & SMB_CAP_UNIX )
smb_proc_query_cifsunix ( server ) ;
server - > conn_complete + + ;
wake_up_interruptible_all ( & server - > conn_wq ) ;
return error ;
out :
smb_unlock_server ( server ) ;
smbiod_wake_up ( ) ;
return error ;
out_putf :
fput ( filp ) ;
goto out ;
}
/* smb_setup_header: We completely set up the packet. You only have to
insert the command - specific fields */
__u8 *
smb_setup_header ( struct smb_request * req , __u8 command , __u16 wct , __u16 bcc )
{
__u32 xmit_len = SMB_HEADER_LEN + wct * sizeof ( __u16 ) + bcc + 2 ;
__u8 * p = req - > rq_header ;
struct smb_sb_info * server = req - > rq_server ;
p = smb_encode_smb_length ( p , xmit_len - 4 ) ;
* p + + = 0xff ;
* p + + = ' S ' ;
* p + + = ' M ' ;
* p + + = ' B ' ;
* p + + = command ;
memset ( p , ' \0 ' , 19 ) ;
p + = 19 ;
p + = 8 ;
if ( server - > opt . protocol > SMB_PROTOCOL_CORE ) {
int flags = SMB_FLAGS_CASELESS_PATHNAMES ;
int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS |
SMB_FLAGS2_EXTENDED_ATTRIBUTES ; /* EA? not really ... */
* ( req - > rq_header + smb_flg ) = flags ;
if ( server - > mnt - > flags & SMB_MOUNT_UNICODE )
flags2 | = SMB_FLAGS2_UNICODE_STRINGS ;
WSET ( req - > rq_header , smb_flg2 , flags2 ) ;
}
* p + + = wct ; /* wct */
p + = 2 * wct ;
WSET ( p , 0 , bcc ) ;
/* Include the header in the data to send */
req - > rq_iovlen = 1 ;
req - > rq_iov [ 0 ] . iov_base = req - > rq_header ;
req - > rq_iov [ 0 ] . iov_len = xmit_len - bcc ;
return req - > rq_buffer ;
}
static void
smb_setup_bcc ( struct smb_request * req , __u8 * p )
{
u16 bcc = p - req - > rq_buffer ;
u8 * pbcc = req - > rq_header + SMB_HEADER_LEN + 2 * SMB_WCT ( req - > rq_header ) ;
WSET ( pbcc , 0 , bcc ) ;
smb_encode_smb_length ( req - > rq_header , SMB_HEADER_LEN +
2 * SMB_WCT ( req - > rq_header ) - 2 + bcc ) ;
/* Include the "bytes" in the data to send */
req - > rq_iovlen = 2 ;
req - > rq_iov [ 1 ] . iov_base = req - > rq_buffer ;
req - > rq_iov [ 1 ] . iov_len = bcc ;
}
static int
smb_proc_seek ( struct smb_sb_info * server , __u16 fileid ,
__u16 mode , off_t offset )
{
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
smb_setup_header ( req , SMBlseek , 4 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , fileid ) ;
WSET ( req - > rq_header , smb_vwv1 , mode ) ;
DSET ( req - > rq_header , smb_vwv2 , offset ) ;
req - > rq_flags | = SMB_REQ_NORETRY ;
result = smb_request_ok ( req , SMBlseek , 2 , 0 ) ;
if ( result < 0 ) {
result = 0 ;
goto out_free ;
}
result = DVAL ( req - > rq_header , smb_vwv0 ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
static int
smb_proc_open ( struct smb_sb_info * server , struct dentry * dentry , int wish )
{
struct inode * ino = dentry - > d_inode ;
struct smb_inode_info * ei = SMB_I ( ino ) ;
int mode , read_write = 0x42 , read_only = 0x40 ;
int res ;
char * p ;
struct smb_request * req ;
/*
* Attempt to open r / w , unless there are no write privileges .
*/
mode = read_write ;
if ( ! ( ino - > i_mode & ( S_IWUSR | S_IWGRP | S_IWOTH ) ) )
mode = read_only ;
#if 0
/* FIXME: why is this code not in? below we fix it so that a caller
wanting RO doesn ' t get RW . smb_revalidate_inode does some
optimization based on access mode . tail - f needs it to be correct .
We must open rw since we don ' t do the open if called a second time
with different ' wish ' . Is that not supported by smb servers ? */
if ( ! ( wish & ( O_WRONLY | O_RDWR ) ) )
mode = read_only ;
# endif
res = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
retry :
p = smb_setup_header ( req , SMBopen , 2 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , mode ) ;
WSET ( req - > rq_header , smb_vwv1 , aSYSTEM | aHIDDEN | aDIR ) ;
res = smb_simple_encode_path ( req , & p , dentry , NULL ) ;
if ( res < 0 )
goto out_free ;
smb_setup_bcc ( req , p ) ;
res = smb_request_ok ( req , SMBopen , 7 , 0 ) ;
if ( res ! = 0 ) {
if ( mode = = read_write & &
( res = = - EACCES | | res = = - ETXTBSY | | res = = - EROFS ) )
{
VERBOSE ( " %s/%s R/W failed, error=%d, retrying R/O \n " ,
DENTRY_PATH ( dentry ) , res ) ;
mode = read_only ;
req - > rq_flags = 0 ;
goto retry ;
}
goto out_free ;
}
/* We should now have data in vwv[0..6]. */
ei - > fileid = WVAL ( req - > rq_header , smb_vwv0 ) ;
ei - > attr = WVAL ( req - > rq_header , smb_vwv1 ) ;
/* smb_vwv2 has mtime */
/* smb_vwv4 has size */
ei - > access = ( WVAL ( req - > rq_header , smb_vwv6 ) & SMB_ACCMASK ) ;
ei - > open = server - > generation ;
out_free :
smb_rput ( req ) ;
out :
return res ;
}
/*
* Make sure the file is open , and check that the access
* is compatible with the desired access .
*/
int
smb_open ( struct dentry * dentry , int wish )
{
struct inode * inode = dentry - > d_inode ;
int result ;
__u16 access ;
result = - ENOENT ;
if ( ! inode ) {
printk ( KERN_ERR " smb_open: no inode for dentry %s/%s \n " ,
DENTRY_PATH ( dentry ) ) ;
goto out ;
}
if ( ! smb_is_open ( inode ) ) {
struct smb_sb_info * server = server_from_inode ( inode ) ;
result = 0 ;
if ( ! smb_is_open ( inode ) )
result = smb_proc_open ( server , dentry , wish ) ;
if ( result )
goto out ;
/*
* A successful open means the path is still valid . . .
*/
smb_renew_times ( dentry ) ;
}
/*
* Check whether the access is compatible with the desired mode .
*/
result = 0 ;
access = SMB_I ( inode ) - > access ;
if ( access ! = wish & & access ! = SMB_O_RDWR ) {
PARANOIA ( " %s/%s access denied, access=%x, wish=%x \n " ,
DENTRY_PATH ( dentry ) , access , wish ) ;
result = - EACCES ;
}
out :
return result ;
}
static int
smb_proc_close ( struct smb_sb_info * server , __u16 fileid , __u32 mtime )
{
struct smb_request * req ;
int result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
smb_setup_header ( req , SMBclose , 3 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , fileid ) ;
DSET ( req - > rq_header , smb_vwv1 , utc2local ( server , mtime ) ) ;
req - > rq_flags | = SMB_REQ_NORETRY ;
result = smb_request_ok ( req , SMBclose , 0 , 0 ) ;
smb_rput ( req ) ;
out :
return result ;
}
/*
* Win NT 4.0 has an apparent bug in that it fails to update the
* modify time when writing to a file . As a workaround , we update
* both modify and access time locally , and post the times to the
* server when closing the file .
*/
static int
smb_proc_close_inode ( struct smb_sb_info * server , struct inode * ino )
{
struct smb_inode_info * ei = SMB_I ( ino ) ;
int result = 0 ;
if ( smb_is_open ( ino ) )
{
/*
* We clear the open flag in advance , in case another
* process observes the value while we block below .
*/
ei - > open = 0 ;
/*
* Kludge alert : SMB timestamps are accurate only to
* two seconds . . . round the times to avoid needless
* cache invalidations !
*/
if ( ino - > i_mtime . tv_sec & 1 ) {
ino - > i_mtime . tv_sec - - ;
ino - > i_mtime . tv_nsec = 0 ;
}
if ( ino - > i_atime . tv_sec & 1 ) {
ino - > i_atime . tv_sec - - ;
ino - > i_atime . tv_nsec = 0 ;
}
/*
* If the file is open with write permissions ,
* update the time stamps to sync mtime and atime .
*/
if ( ( server - > opt . capabilities & SMB_CAP_UNIX ) = = 0 & &
( server - > opt . protocol > = SMB_PROTOCOL_LANMAN2 ) & &
! ( ei - > access = = SMB_O_RDONLY ) )
{
struct smb_fattr fattr ;
smb_get_inode_attr ( ino , & fattr ) ;
smb_proc_setattr_ext ( server , ino , & fattr ) ;
}
result = smb_proc_close ( server , ei - > fileid , ino - > i_mtime . tv_sec ) ;
/*
* Force a revalidation after closing . . . some servers
* don ' t post the size until the file has been closed .
*/
if ( server - > opt . protocol < SMB_PROTOCOL_NT1 )
ei - > oldmtime = 0 ;
ei - > closed = jiffies ;
}
return result ;
}
int
smb_close ( struct inode * ino )
{
int result = 0 ;
if ( smb_is_open ( ino ) ) {
struct smb_sb_info * server = server_from_inode ( ino ) ;
result = smb_proc_close_inode ( server , ino ) ;
}
return result ;
}
/*
* This is used to close a file following a failed instantiate .
* Since we don ' t have an inode , we can ' t use any of the above .
*/
int
smb_close_fileid ( struct dentry * dentry , __u16 fileid )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
int result ;
result = smb_proc_close ( server , fileid , get_seconds ( ) ) ;
return result ;
}
/* In smb_proc_read and smb_proc_write we do not retry, because the
file - id would not be valid after a reconnection . */
static void
smb_proc_read_data ( struct smb_request * req )
{
req - > rq_iov [ 0 ] . iov_base = req - > rq_buffer ;
req - > rq_iov [ 0 ] . iov_len = 3 ;
req - > rq_iov [ 1 ] . iov_base = req - > rq_page ;
req - > rq_iov [ 1 ] . iov_len = req - > rq_rsize ;
req - > rq_iovlen = 2 ;
req - > rq_rlen = smb_len ( req - > rq_header ) + 4 - req - > rq_bytes_recvd ;
}
static int
smb_proc_read ( struct inode * inode , loff_t offset , int count , char * data )
{
struct smb_sb_info * server = server_from_inode ( inode ) ;
__u16 returned_count , data_len ;
unsigned char * buf ;
int result ;
struct smb_request * req ;
u8 rbuf [ 4 ] ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
smb_setup_header ( req , SMBread , 5 , 0 ) ;
buf = req - > rq_header ;
WSET ( buf , smb_vwv0 , SMB_I ( inode ) - > fileid ) ;
WSET ( buf , smb_vwv1 , count ) ;
DSET ( buf , smb_vwv2 , offset ) ;
WSET ( buf , smb_vwv4 , 0 ) ;
req - > rq_page = data ;
req - > rq_rsize = count ;
req - > rq_callback = smb_proc_read_data ;
req - > rq_buffer = rbuf ;
req - > rq_flags | = SMB_REQ_NORETRY | SMB_REQ_STATIC ;
result = smb_request_ok ( req , SMBread , 5 , - 1 ) ;
if ( result < 0 )
goto out_free ;
returned_count = WVAL ( req - > rq_header , smb_vwv0 ) ;
data_len = WVAL ( rbuf , 1 ) ;
if ( returned_count ! = data_len ) {
printk ( KERN_NOTICE " smb_proc_read: returned != data_len \n " ) ;
printk ( KERN_NOTICE " smb_proc_read: ret_c=%d, data_len=%d \n " ,
returned_count , data_len ) ;
}
result = data_len ;
out_free :
smb_rput ( req ) ;
out :
VERBOSE ( " ino=%ld, fileid=%d, count=%d, result=%d \n " ,
inode - > i_ino , SMB_I ( inode ) - > fileid , count , result ) ;
return result ;
}
static int
smb_proc_write ( struct inode * inode , loff_t offset , int count , const char * data )
{
struct smb_sb_info * server = server_from_inode ( inode ) ;
int result ;
u16 fileid = SMB_I ( inode ) - > fileid ;
u8 buf [ 4 ] ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
VERBOSE ( " ino=%ld, fileid=%d, count=%d@%Ld \n " ,
inode - > i_ino , fileid , count , offset ) ;
smb_setup_header ( req , SMBwrite , 5 , count + 3 ) ;
WSET ( req - > rq_header , smb_vwv0 , fileid ) ;
WSET ( req - > rq_header , smb_vwv1 , count ) ;
DSET ( req - > rq_header , smb_vwv2 , offset ) ;
WSET ( req - > rq_header , smb_vwv4 , 0 ) ;
buf [ 0 ] = 1 ;
WSET ( buf , 1 , count ) ; /* yes, again ... */
req - > rq_iov [ 1 ] . iov_base = buf ;
req - > rq_iov [ 1 ] . iov_len = 3 ;
req - > rq_iov [ 2 ] . iov_base = ( char * ) data ;
req - > rq_iov [ 2 ] . iov_len = count ;
req - > rq_iovlen = 3 ;
req - > rq_flags | = SMB_REQ_NORETRY ;
result = smb_request_ok ( req , SMBwrite , 1 , 0 ) ;
if ( result > = 0 )
result = WVAL ( req - > rq_header , smb_vwv0 ) ;
smb_rput ( req ) ;
out :
return result ;
}
/*
* In smb_proc_readX and smb_proc_writeX we do not retry , because the
* file - id would not be valid after a reconnection .
*/
# define SMB_READX_MAX_PAD 64
static void
smb_proc_readX_data ( struct smb_request * req )
{
/* header length, excluding the netbios length (-4) */
int hdrlen = SMB_HEADER_LEN + req - > rq_resp_wct * 2 - 2 ;
int data_off = WVAL ( req - > rq_header , smb_vwv6 ) ;
/*
* Some genius made the padding to the data bytes arbitrary .
* So we must first calculate the amount of padding used by the server .
*/
data_off - = hdrlen ;
if ( data_off > SMB_READX_MAX_PAD | | data_off < 0 ) {
PARANOIA ( " offset is larger than SMB_READX_MAX_PAD or negative! \n " ) ;
PARANOIA ( " %d > %d || %d < 0 \n " , data_off , SMB_READX_MAX_PAD , data_off ) ;
req - > rq_rlen = req - > rq_bufsize + 1 ;
return ;
}
req - > rq_iov [ 0 ] . iov_base = req - > rq_buffer ;
req - > rq_iov [ 0 ] . iov_len = data_off ;
req - > rq_iov [ 1 ] . iov_base = req - > rq_page ;
req - > rq_iov [ 1 ] . iov_len = req - > rq_rsize ;
req - > rq_iovlen = 2 ;
req - > rq_rlen = smb_len ( req - > rq_header ) + 4 - req - > rq_bytes_recvd ;
}
static int
smb_proc_readX ( struct inode * inode , loff_t offset , int count , char * data )
{
struct smb_sb_info * server = server_from_inode ( inode ) ;
unsigned char * buf ;
int result ;
struct smb_request * req ;
static char pad [ SMB_READX_MAX_PAD ] ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
smb_setup_header ( req , SMBreadX , 12 , 0 ) ;
buf = req - > rq_header ;
WSET ( buf , smb_vwv0 , 0x00ff ) ;
WSET ( buf , smb_vwv1 , 0 ) ;
WSET ( buf , smb_vwv2 , SMB_I ( inode ) - > fileid ) ;
DSET ( buf , smb_vwv3 , ( u32 ) offset ) ; /* low 32 bits */
WSET ( buf , smb_vwv5 , count ) ;
WSET ( buf , smb_vwv6 , 0 ) ;
DSET ( buf , smb_vwv7 , 0 ) ;
WSET ( buf , smb_vwv9 , 0 ) ;
DSET ( buf , smb_vwv10 , ( u32 ) ( offset > > 32 ) ) ; /* high 32 bits */
WSET ( buf , smb_vwv11 , 0 ) ;
req - > rq_page = data ;
req - > rq_rsize = count ;
req - > rq_callback = smb_proc_readX_data ;
req - > rq_buffer = pad ;
req - > rq_bufsize = SMB_READX_MAX_PAD ;
req - > rq_flags | = SMB_REQ_STATIC | SMB_REQ_NORETRY ;
result = smb_request_ok ( req , SMBreadX , 12 , - 1 ) ;
if ( result < 0 )
goto out_free ;
result = WVAL ( req - > rq_header , smb_vwv5 ) ;
out_free :
smb_rput ( req ) ;
out :
VERBOSE ( " ino=%ld, fileid=%d, count=%d, result=%d \n " ,
inode - > i_ino , SMB_I ( inode ) - > fileid , count , result ) ;
return result ;
}
static int
smb_proc_writeX ( struct inode * inode , loff_t offset , int count , const char * data )
{
struct smb_sb_info * server = server_from_inode ( inode ) ;
int result ;
u8 * p ;
static u8 pad [ 4 ] ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
VERBOSE ( " ino=%ld, fileid=%d, count=%d@%Ld \n " ,
inode - > i_ino , SMB_I ( inode ) - > fileid , count , offset ) ;
p = smb_setup_header ( req , SMBwriteX , 14 , count + 1 ) ;
WSET ( req - > rq_header , smb_vwv0 , 0x00ff ) ;
WSET ( req - > rq_header , smb_vwv1 , 0 ) ;
WSET ( req - > rq_header , smb_vwv2 , SMB_I ( inode ) - > fileid ) ;
DSET ( req - > rq_header , smb_vwv3 , ( u32 ) offset ) ; /* low 32 bits */
DSET ( req - > rq_header , smb_vwv5 , 0 ) ;
WSET ( req - > rq_header , smb_vwv7 , 0 ) ; /* write mode */
WSET ( req - > rq_header , smb_vwv8 , 0 ) ;
WSET ( req - > rq_header , smb_vwv9 , 0 ) ;
WSET ( req - > rq_header , smb_vwv10 , count ) ; /* data length */
WSET ( req - > rq_header , smb_vwv11 , smb_vwv12 + 2 + 1 ) ;
DSET ( req - > rq_header , smb_vwv12 , ( u32 ) ( offset > > 32 ) ) ;
req - > rq_iov [ 1 ] . iov_base = pad ;
req - > rq_iov [ 1 ] . iov_len = 1 ;
req - > rq_iov [ 2 ] . iov_base = ( char * ) data ;
req - > rq_iov [ 2 ] . iov_len = count ;
req - > rq_iovlen = 3 ;
req - > rq_flags | = SMB_REQ_NORETRY ;
result = smb_request_ok ( req , SMBwriteX , 6 , 0 ) ;
if ( result > = 0 )
result = WVAL ( req - > rq_header , smb_vwv2 ) ;
smb_rput ( req ) ;
out :
return result ;
}
int
smb_proc_create ( struct dentry * dentry , __u16 attr , time_t ctime , __u16 * fileid )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
char * p ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
p = smb_setup_header ( req , SMBcreate , 3 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , attr ) ;
DSET ( req - > rq_header , smb_vwv1 , utc2local ( server , ctime ) ) ;
result = smb_simple_encode_path ( req , & p , dentry , NULL ) ;
if ( result < 0 )
goto out_free ;
smb_setup_bcc ( req , p ) ;
result = smb_request_ok ( req , SMBcreate , 1 , 0 ) ;
if ( result < 0 )
goto out_free ;
* fileid = WVAL ( req - > rq_header , smb_vwv0 ) ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
int
smb_proc_mv ( struct dentry * old_dentry , struct dentry * new_dentry )
{
struct smb_sb_info * server = server_from_dentry ( old_dentry ) ;
char * p ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
p = smb_setup_header ( req , SMBmv , 1 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , aSYSTEM | aHIDDEN | aDIR ) ;
result = smb_simple_encode_path ( req , & p , old_dentry , NULL ) ;
if ( result < 0 )
goto out_free ;
result = smb_simple_encode_path ( req , & p , new_dentry , NULL ) ;
if ( result < 0 )
goto out_free ;
smb_setup_bcc ( req , p ) ;
if ( ( result = smb_request_ok ( req , SMBmv , 0 , 0 ) ) < 0 )
goto out_free ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
/*
* Code common to mkdir and rmdir .
*/
static int
smb_proc_generic_command ( struct dentry * dentry , __u8 command )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
char * p ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
p = smb_setup_header ( req , command , 0 , 0 ) ;
result = smb_simple_encode_path ( req , & p , dentry , NULL ) ;
if ( result < 0 )
goto out_free ;
smb_setup_bcc ( req , p ) ;
result = smb_request_ok ( req , command , 0 , 0 ) ;
if ( result < 0 )
goto out_free ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
int
smb_proc_mkdir ( struct dentry * dentry )
{
return smb_proc_generic_command ( dentry , SMBmkdir ) ;
}
int
smb_proc_rmdir ( struct dentry * dentry )
{
return smb_proc_generic_command ( dentry , SMBrmdir ) ;
}
# if SMBFS_POSIX_UNLINK
/*
* Removes readonly attribute from a file . Used by unlink to give posix
* semantics .
*/
static int
smb_set_rw ( struct dentry * dentry , struct smb_sb_info * server )
{
int result ;
struct smb_fattr fattr ;
/* FIXME: cifsUE should allow removing a readonly file. */
/* first get current attribute */
smb_init_dirent ( server , & fattr ) ;
result = server - > ops - > getattr ( server , dentry , & fattr ) ;
smb_finish_dirent ( server , & fattr ) ;
if ( result < 0 )
return result ;
/* if RONLY attribute is set, remove it */
if ( fattr . attr & aRONLY ) { /* read only attribute is set */
fattr . attr & = ~ aRONLY ;
result = smb_proc_setattr_core ( server , dentry , fattr . attr ) ;
}
return result ;
}
# endif
int
smb_proc_unlink ( struct dentry * dentry )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
int flag = 0 ;
char * p ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
retry :
p = smb_setup_header ( req , SMBunlink , 1 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , aSYSTEM | aHIDDEN ) ;
result = smb_simple_encode_path ( req , & p , dentry , NULL ) ;
if ( result < 0 )
goto out_free ;
smb_setup_bcc ( req , p ) ;
if ( ( result = smb_request_ok ( req , SMBunlink , 0 , 0 ) ) < 0 ) {
# if SMBFS_POSIX_UNLINK
if ( result = = - EACCES & & ! flag ) {
/* Posix semantics is for the read-only state
of a file to be ignored in unlink ( ) . In the
SMB world a unlink ( ) is refused on a
read - only file . To make things easier for
unix users we try to override the files
permission if the unlink fails with the
right error .
This introduces a race condition that could
lead to a file being written by someone who
shouldn ' t have access , but as far as I can
tell that is unavoidable */
/* remove RONLY attribute and try again */
result = smb_set_rw ( dentry , server ) ;
if ( result = = 0 ) {
flag = 1 ;
req - > rq_flags = 0 ;
goto retry ;
}
}
# endif
goto out_free ;
}
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
int
smb_proc_flush ( struct smb_sb_info * server , __u16 fileid )
{
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
smb_setup_header ( req , SMBflush , 1 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , fileid ) ;
req - > rq_flags | = SMB_REQ_NORETRY ;
result = smb_request_ok ( req , SMBflush , 0 , 0 ) ;
smb_rput ( req ) ;
out :
return result ;
}
static int
smb_proc_trunc32 ( struct inode * inode , loff_t length )
{
/*
* Writing 0 bytes is old - SMB magic for truncating files .
* MAX_NON_LFS should prevent this from being called with a too
* large offset .
*/
return smb_proc_write ( inode , length , 0 , NULL ) ;
}
static int
smb_proc_trunc64 ( struct inode * inode , loff_t length )
{
struct smb_sb_info * server = server_from_inode ( inode ) ;
int result ;
char * param ;
char * data ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 14 ) ) )
goto out ;
param = req - > rq_buffer ;
data = req - > rq_buffer + 6 ;
/* FIXME: must we also set allocation size? winNT seems to do that */
WSET ( param , 0 , SMB_I ( inode ) - > fileid ) ;
WSET ( param , 2 , SMB_SET_FILE_END_OF_FILE_INFO ) ;
WSET ( param , 4 , 0 ) ;
LSET ( data , 0 , length ) ;
req - > rq_trans2_command = TRANSACT2_SETFILEINFO ;
req - > rq_ldata = 8 ;
req - > rq_data = data ;
req - > rq_lparm = 6 ;
req - > rq_parm = param ;
req - > rq_flags | = SMB_REQ_NORETRY ;
result = smb_add_request ( req ) ;
if ( result < 0 )
goto out_free ;
result = 0 ;
if ( req - > rq_rcls ! = 0 )
result = smb_errno ( req ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
static int
smb_proc_trunc95 ( struct inode * inode , loff_t length )
{
struct smb_sb_info * server = server_from_inode ( inode ) ;
int result = smb_proc_trunc32 ( inode , length ) ;
/*
* win9x doesn ' t appear to update the size immediately .
* It will return the old file size after the truncate ,
* confusing smbfs . So we force an update .
*
* FIXME : is this still necessary ?
*/
smb_proc_flush ( server , SMB_I ( inode ) - > fileid ) ;
return result ;
}
static void
smb_init_dirent ( struct smb_sb_info * server , struct smb_fattr * fattr )
{
memset ( fattr , 0 , sizeof ( * fattr ) ) ;
fattr - > f_nlink = 1 ;
fattr - > f_uid = server - > mnt - > uid ;
fattr - > f_gid = server - > mnt - > gid ;
fattr - > f_unix = 0 ;
}
static void
smb_finish_dirent ( struct smb_sb_info * server , struct smb_fattr * fattr )
{
if ( fattr - > f_unix )
return ;
fattr - > f_mode = server - > mnt - > file_mode ;
if ( fattr - > attr & aDIR ) {
fattr - > f_mode = server - > mnt - > dir_mode ;
fattr - > f_size = SMB_ST_BLKSIZE ;
}
/* Check the read-only flag */
if ( fattr - > attr & aRONLY )
fattr - > f_mode & = ~ ( S_IWUSR | S_IWGRP | S_IWOTH ) ;
/* How many 512 byte blocks do we need for this file? */
fattr - > f_blocks = 0 ;
if ( fattr - > f_size ! = 0 )
fattr - > f_blocks = 1 + ( ( fattr - > f_size - 1 ) > > 9 ) ;
return ;
}
void
smb_init_root_dirent ( struct smb_sb_info * server , struct smb_fattr * fattr ,
struct super_block * sb )
{
smb_init_dirent ( server , fattr ) ;
fattr - > attr = aDIR ;
fattr - > f_ino = 2 ; /* traditional root inode number */
fattr - > f_mtime = current_fs_time ( sb ) ;
smb_finish_dirent ( server , fattr ) ;
}
/*
* Decode a dirent for old protocols
*
* qname is filled with the decoded , and possibly translated , name .
* fattr receives decoded attributes
*
* Bugs Noted :
* ( 1 ) Pathworks servers may pad the name with extra spaces .
*/
static char *
smb_decode_short_dirent ( struct smb_sb_info * server , char * p ,
struct qstr * qname , struct smb_fattr * fattr ,
unsigned char * name_buf )
{
int len ;
/*
* SMB doesn ' t have a concept of inode numbers . . .
*/
smb_init_dirent ( server , fattr ) ;
fattr - > f_ino = 0 ; /* FIXME: do we need this? */
p + = SMB_STATUS_SIZE ; /* reserved (search_status) */
fattr - > attr = * p ;
fattr - > f_mtime . tv_sec = date_dos2unix ( server , WVAL ( p , 3 ) , WVAL ( p , 1 ) ) ;
fattr - > f_mtime . tv_nsec = 0 ;
fattr - > f_size = DVAL ( p , 5 ) ;
fattr - > f_ctime = fattr - > f_mtime ;
fattr - > f_atime = fattr - > f_mtime ;
qname - > name = p + 9 ;
len = strnlen ( qname - > name , 12 ) ;
/*
* Trim trailing blanks for Pathworks servers
*/
while ( len > 2 & & qname - > name [ len - 1 ] = = ' ' )
len - - ;
smb_finish_dirent ( server , fattr ) ;
#if 0
/* FIXME: These only work for ascii chars, and recent smbmount doesn't
allow the flag to be set anyway . It kills const . Remove ? */
switch ( server - > opt . case_handling ) {
case SMB_CASE_UPPER :
str_upper ( entry - > name , len ) ;
break ;
case SMB_CASE_LOWER :
str_lower ( entry - > name , len ) ;
break ;
default :
break ;
}
# endif
qname - > len = 0 ;
len = server - > ops - > convert ( name_buf , SMB_MAXNAMELEN ,
qname - > name , len ,
server - > remote_nls , server - > local_nls ) ;
if ( len > 0 ) {
qname - > len = len ;
qname - > name = name_buf ;
DEBUG1 ( " len=%d, name=%.*s \n " , qname - > len , qname - > len , qname - > name ) ;
}
return p + 22 ;
}
/*
* This routine is used to read in directory entries from the network .
* Note that it is for short directory name seeks , i . e . : protocol <
* SMB_PROTOCOL_LANMAN2
*/
static int
smb_proc_readdir_short ( struct file * filp , void * dirent , filldir_t filldir ,
struct smb_cache_control * ctl )
{
2006-12-08 13:37:39 +03:00
struct dentry * dir = filp - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
struct smb_sb_info * server = server_from_dentry ( dir ) ;
struct qstr qname ;
struct smb_fattr fattr ;
char * p ;
int result ;
int i , first , entries_seen , entries ;
int entries_asked = ( server - > opt . max_xmit - 100 ) / SMB_DIRINFO_SIZE ;
__u16 bcc ;
__u16 count ;
char status [ SMB_STATUS_SIZE ] ;
static struct qstr mask = {
. name = " *.* " ,
. len = 3 ,
} ;
unsigned char * last_status ;
struct smb_request * req ;
unsigned char * name_buf ;
VERBOSE ( " %s/%s \n " , DENTRY_PATH ( dir ) ) ;
lock_kernel ( ) ;
result = - ENOMEM ;
if ( ! ( name_buf = kmalloc ( SMB_MAXNAMELEN , GFP_KERNEL ) ) )
goto out ;
first = 1 ;
entries = 0 ;
entries_seen = 2 ; /* implicit . and .. */
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , server - > opt . max_xmit ) ) )
goto out_name ;
while ( 1 ) {
p = smb_setup_header ( req , SMBsearch , 2 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , entries_asked ) ;
WSET ( req - > rq_header , smb_vwv1 , aDIR ) ;
if ( first = = 1 ) {
result = smb_simple_encode_path ( req , & p , dir , & mask ) ;
if ( result < 0 )
goto out_free ;
if ( p + 3 > ( char * ) req - > rq_buffer + req - > rq_bufsize ) {
result = - ENAMETOOLONG ;
goto out_free ;
}
* p + + = 5 ;
WSET ( p , 0 , 0 ) ;
p + = 2 ;
first = 0 ;
} else {
if ( p + 5 + SMB_STATUS_SIZE >
( char * ) req - > rq_buffer + req - > rq_bufsize ) {
result = - ENAMETOOLONG ;
goto out_free ;
}
* p + + = 4 ;
* p + + = 0 ;
* p + + = 5 ;
WSET ( p , 0 , SMB_STATUS_SIZE ) ;
p + = 2 ;
memcpy ( p , status , SMB_STATUS_SIZE ) ;
p + = SMB_STATUS_SIZE ;
}
smb_setup_bcc ( req , p ) ;
result = smb_request_ok ( req , SMBsearch , 1 , - 1 ) ;
if ( result < 0 ) {
if ( ( req - > rq_rcls = = ERRDOS ) & &
( req - > rq_err = = ERRnofiles ) )
break ;
goto out_free ;
}
count = WVAL ( req - > rq_header , smb_vwv0 ) ;
if ( count < = 0 )
break ;
result = - EIO ;
bcc = smb_bcc ( req - > rq_header ) ;
if ( bcc ! = count * SMB_DIRINFO_SIZE + 3 )
goto out_free ;
p = req - > rq_buffer + 3 ;
/* Make sure the response fits in the buffer. Fixed sized
entries means we don ' t have to check in the decode loop . */
last_status = req - > rq_buffer + 3 + ( count - 1 ) * SMB_DIRINFO_SIZE ;
if ( last_status + SMB_DIRINFO_SIZE > =
req - > rq_buffer + req - > rq_bufsize ) {
printk ( KERN_ERR " smb_proc_readdir_short: "
" last dir entry outside buffer! "
" %d@%p %d@%p \n " , SMB_DIRINFO_SIZE , last_status ,
req - > rq_bufsize , req - > rq_buffer ) ;
goto out_free ;
}
/* Read the last entry into the status field. */
memcpy ( status , last_status , SMB_STATUS_SIZE ) ;
/* Now we are ready to parse smb directory entries. */
for ( i = 0 ; i < count ; i + + ) {
p = smb_decode_short_dirent ( server , p ,
& qname , & fattr , name_buf ) ;
if ( qname . len = = 0 )
continue ;
if ( entries_seen = = 2 & & qname . name [ 0 ] = = ' . ' ) {
if ( qname . len = = 1 )
continue ;
if ( qname . name [ 1 ] = = ' . ' & & qname . len = = 2 )
continue ;
}
if ( ! smb_fill_cache ( filp , dirent , filldir , ctl ,
& qname , & fattr ) )
; /* stop reading? */
entries_seen + + ;
}
}
result = entries ;
out_free :
smb_rput ( req ) ;
out_name :
kfree ( name_buf ) ;
out :
unlock_kernel ( ) ;
return result ;
}
static void smb_decode_unix_basic ( struct smb_fattr * fattr , struct smb_sb_info * server , char * p )
{
u64 size , disk_bytes ;
/* FIXME: verify nls support. all is sent as utf8? */
fattr - > f_unix = 1 ;
fattr - > f_mode = 0 ;
/* FIXME: use the uniqueID from the remote instead? */
/* 0 L file size in bytes */
/* 8 L file size on disk in bytes (block count) */
/* 40 L uid */
/* 48 L gid */
/* 56 W file type */
/* 60 L devmajor */
/* 68 L devminor */
/* 76 L unique ID (inode) */
/* 84 L permissions */
/* 92 L link count */
size = LVAL ( p , 0 ) ;
disk_bytes = LVAL ( p , 8 ) ;
/*
* Some samba versions round up on - disk byte usage
* to 1 MB boundaries , making it useless . When seeing
* that , use the size instead .
*/
if ( ! ( disk_bytes & 0xfffff ) )
disk_bytes = size + 511 ;
fattr - > f_size = size ;
fattr - > f_blocks = disk_bytes > > 9 ;
fattr - > f_ctime = smb_ntutc2unixutc ( LVAL ( p , 16 ) ) ;
fattr - > f_atime = smb_ntutc2unixutc ( LVAL ( p , 24 ) ) ;
fattr - > f_mtime = smb_ntutc2unixutc ( LVAL ( p , 32 ) ) ;
if ( server - > mnt - > flags & SMB_MOUNT_UID )
fattr - > f_uid = server - > mnt - > uid ;
else
fattr - > f_uid = LVAL ( p , 40 ) ;
if ( server - > mnt - > flags & SMB_MOUNT_GID )
fattr - > f_gid = server - > mnt - > gid ;
else
fattr - > f_gid = LVAL ( p , 48 ) ;
fattr - > f_mode | = smb_filetype_to_mode ( WVAL ( p , 56 ) ) ;
if ( S_ISBLK ( fattr - > f_mode ) | | S_ISCHR ( fattr - > f_mode ) ) {
__u64 major = LVAL ( p , 60 ) ;
__u64 minor = LVAL ( p , 68 ) ;
fattr - > f_rdev = MKDEV ( major & 0xffffffff , minor & 0xffffffff ) ;
if ( MAJOR ( fattr - > f_rdev ) ! = ( major & 0xffffffff ) | |
MINOR ( fattr - > f_rdev ) ! = ( minor & 0xffffffff ) )
fattr - > f_rdev = 0 ;
}
fattr - > f_mode | = LVAL ( p , 84 ) ;
if ( ( server - > mnt - > flags & SMB_MOUNT_DMODE ) & &
( S_ISDIR ( fattr - > f_mode ) ) )
fattr - > f_mode = ( server - > mnt - > dir_mode & S_IRWXUGO ) | S_IFDIR ;
else if ( ( server - > mnt - > flags & SMB_MOUNT_FMODE ) & &
! ( S_ISDIR ( fattr - > f_mode ) ) )
fattr - > f_mode = ( server - > mnt - > file_mode & S_IRWXUGO ) |
( fattr - > f_mode & S_IFMT ) ;
}
/*
* Interpret a long filename structure using the specified info level :
* level 1 for anything below NT1 protocol
* level 260 for NT1 protocol
*
* qname is filled with the decoded , and possibly translated , name
* fattr receives decoded attributes .
*
* Bugs Noted :
* ( 1 ) Win NT 4.0 appends a null byte to names and counts it in the length !
*/
static char *
smb_decode_long_dirent ( struct smb_sb_info * server , char * p , int level ,
struct qstr * qname , struct smb_fattr * fattr ,
unsigned char * name_buf )
{
char * result ;
unsigned int len = 0 ;
int n ;
__u16 date , time ;
int unicode = ( server - > mnt - > flags & SMB_MOUNT_UNICODE ) ;
/*
* SMB doesn ' t have a concept of inode numbers . . .
*/
smb_init_dirent ( server , fattr ) ;
fattr - > f_ino = 0 ; /* FIXME: do we need this? */
switch ( level ) {
case 1 :
len = * ( ( unsigned char * ) p + 22 ) ;
qname - > name = p + 23 ;
result = p + 24 + len ;
date = WVAL ( p , 0 ) ;
time = WVAL ( p , 2 ) ;
fattr - > f_ctime . tv_sec = date_dos2unix ( server , date , time ) ;
fattr - > f_ctime . tv_nsec = 0 ;
date = WVAL ( p , 4 ) ;
time = WVAL ( p , 6 ) ;
fattr - > f_atime . tv_sec = date_dos2unix ( server , date , time ) ;
fattr - > f_atime . tv_nsec = 0 ;
date = WVAL ( p , 8 ) ;
time = WVAL ( p , 10 ) ;
fattr - > f_mtime . tv_sec = date_dos2unix ( server , date , time ) ;
fattr - > f_mtime . tv_nsec = 0 ;
fattr - > f_size = DVAL ( p , 12 ) ;
/* ULONG allocation size */
fattr - > attr = WVAL ( p , 20 ) ;
VERBOSE ( " info 1 at %p, len=%d, name=%.*s \n " ,
p , len , len , qname - > name ) ;
break ;
case 260 :
result = p + WVAL ( p , 0 ) ;
len = DVAL ( p , 60 ) ;
if ( len > 255 ) len = 255 ;
/* NT4 null terminates, unless we are using unicode ... */
qname - > name = p + 94 ;
if ( ! unicode & & len & & qname - > name [ len - 1 ] = = ' \0 ' )
len - - ;
fattr - > f_ctime = smb_ntutc2unixutc ( LVAL ( p , 8 ) ) ;
fattr - > f_atime = smb_ntutc2unixutc ( LVAL ( p , 16 ) ) ;
fattr - > f_mtime = smb_ntutc2unixutc ( LVAL ( p , 24 ) ) ;
/* change time (32) */
fattr - > f_size = LVAL ( p , 40 ) ;
/* alloc size (48) */
fattr - > attr = DVAL ( p , 56 ) ;
VERBOSE ( " info 260 at %p, len=%d, name=%.*s \n " ,
p , len , len , qname - > name ) ;
break ;
case SMB_FIND_FILE_UNIX :
result = p + WVAL ( p , 0 ) ;
qname - > name = p + 108 ;
len = strlen ( qname - > name ) ;
/* FIXME: should we check the length?? */
p + = 8 ;
smb_decode_unix_basic ( fattr , server , p ) ;
VERBOSE ( " info SMB_FIND_FILE_UNIX at %p, len=%d, name=%.*s \n " ,
p , len , len , qname - > name ) ;
break ;
default :
PARANOIA ( " Unknown info level %d \n " , level ) ;
result = p + WVAL ( p , 0 ) ;
goto out ;
}
smb_finish_dirent ( server , fattr ) ;
#if 0
/* FIXME: These only work for ascii chars, and recent smbmount doesn't
allow the flag to be set anyway . Remove ? */
switch ( server - > opt . case_handling ) {
case SMB_CASE_UPPER :
str_upper ( qname - > name , len ) ;
break ;
case SMB_CASE_LOWER :
str_lower ( qname - > name , len ) ;
break ;
default :
break ;
}
# endif
qname - > len = 0 ;
n = server - > ops - > convert ( name_buf , SMB_MAXNAMELEN ,
qname - > name , len ,
server - > remote_nls , server - > local_nls ) ;
if ( n > 0 ) {
qname - > len = n ;
qname - > name = name_buf ;
}
out :
return result ;
}
/* findfirst/findnext flags */
# define SMB_CLOSE_AFTER_FIRST (1<<0)
# define SMB_CLOSE_IF_END (1<<1)
# define SMB_REQUIRE_RESUME_KEY (1<<2)
# define SMB_CONTINUE_BIT (1<<3)
/*
* Note : samba - 2.0 .7 ( at least ) has a very similar routine , cli_list , in
* source / libsmb / clilist . c . When looking for smb bugs in the readdir code ,
* go there for advise .
*
* Bugs Noted :
* ( 1 ) When using Info Level 1 Win NT 4.0 truncates directory listings
* for certain patterns of names and / or lengths . The breakage pattern
* is completely reproducible and can be toggled by the creation of a
* single file . ( E . g . echo hi > foo breaks , rm - f foo works . )
*/
static int
smb_proc_readdir_long ( struct file * filp , void * dirent , filldir_t filldir ,
struct smb_cache_control * ctl )
{
2006-12-08 13:37:39 +03:00
struct dentry * dir = filp - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
struct smb_sb_info * server = server_from_dentry ( dir ) ;
struct qstr qname ;
struct smb_fattr fattr ;
unsigned char * p , * lastname ;
char * mask , * param ;
__u16 command ;
int first , entries_seen ;
/* Both NT and OS/2 accept info level 1 (but see note below). */
int info_level = 260 ;
const int max_matches = 512 ;
unsigned int ff_searchcount = 0 ;
unsigned int ff_eos = 0 ;
unsigned int ff_lastname = 0 ;
unsigned int ff_dir_handle = 0 ;
unsigned int loop_count = 0 ;
unsigned int mask_len , i ;
int result ;
struct smb_request * req ;
unsigned char * name_buf ;
static struct qstr star = {
. name = " * " ,
. len = 1 ,
} ;
lock_kernel ( ) ;
/*
* We always prefer unix style . Use info level 1 for older
* servers that don ' t do 260.
*/
if ( server - > opt . capabilities & SMB_CAP_UNIX )
info_level = SMB_FIND_FILE_UNIX ;
else if ( server - > opt . protocol < SMB_PROTOCOL_NT1 )
info_level = 1 ;
result = - ENOMEM ;
if ( ! ( name_buf = kmalloc ( SMB_MAXNAMELEN + 2 , GFP_KERNEL ) ) )
goto out ;
if ( ! ( req = smb_alloc_request ( server , server - > opt . max_xmit ) ) )
goto out_name ;
param = req - > rq_buffer ;
/*
* Encode the initial path
*/
mask = param + 12 ;
result = smb_encode_path ( server , mask , SMB_MAXPATHLEN + 1 , dir , & star ) ;
if ( result < = 0 )
goto out_free ;
mask_len = result - 1 ; /* mask_len is strlen, not #bytes */
result = 0 ;
first = 1 ;
VERBOSE ( " starting mask_len=%d, mask=%s \n " , mask_len , mask ) ;
entries_seen = 2 ;
ff_eos = 0 ;
while ( ff_eos = = 0 ) {
loop_count + = 1 ;
if ( loop_count > 10 ) {
printk ( KERN_WARNING " smb_proc_readdir_long: "
" Looping in FIND_NEXT?? \n " ) ;
result = - EIO ;
break ;
}
if ( first ! = 0 ) {
command = TRANSACT2_FINDFIRST ;
WSET ( param , 0 , aSYSTEM | aHIDDEN | aDIR ) ;
WSET ( param , 2 , max_matches ) ; /* max count */
WSET ( param , 4 , SMB_CLOSE_IF_END ) ;
WSET ( param , 6 , info_level ) ;
DSET ( param , 8 , 0 ) ;
} else {
command = TRANSACT2_FINDNEXT ;
VERBOSE ( " handle=0x%X, lastname=%d, mask=%.*s \n " ,
ff_dir_handle , ff_lastname , mask_len , mask ) ;
WSET ( param , 0 , ff_dir_handle ) ; /* search handle */
WSET ( param , 2 , max_matches ) ; /* max count */
WSET ( param , 4 , info_level ) ;
DSET ( param , 6 , 0 ) ;
WSET ( param , 10 , SMB_CONTINUE_BIT | SMB_CLOSE_IF_END ) ;
}
req - > rq_trans2_command = command ;
req - > rq_ldata = 0 ;
req - > rq_data = NULL ;
req - > rq_lparm = 12 + mask_len + 1 ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
if ( result < 0 ) {
PARANOIA ( " error=%d, breaking \n " , result ) ;
break ;
}
if ( req - > rq_rcls = = ERRSRV & & req - > rq_err = = ERRerror ) {
/* a damn Win95 bug - sometimes it clags if you
ask it too fast */
2005-09-10 11:27:23 +04:00
schedule_timeout_interruptible ( msecs_to_jiffies ( 200 ) ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
if ( req - > rq_rcls ! = 0 ) {
result = smb_errno ( req ) ;
PARANOIA ( " name=%s, result=%d, rcls=%d, err=%d \n " ,
mask , result , req - > rq_rcls , req - > rq_err ) ;
break ;
}
/* parse out some important return info */
if ( first ! = 0 ) {
ff_dir_handle = WVAL ( req - > rq_parm , 0 ) ;
ff_searchcount = WVAL ( req - > rq_parm , 2 ) ;
ff_eos = WVAL ( req - > rq_parm , 4 ) ;
ff_lastname = WVAL ( req - > rq_parm , 8 ) ;
} else {
ff_searchcount = WVAL ( req - > rq_parm , 0 ) ;
ff_eos = WVAL ( req - > rq_parm , 2 ) ;
ff_lastname = WVAL ( req - > rq_parm , 6 ) ;
}
if ( ff_searchcount = = 0 )
break ;
/* Now we are ready to parse smb directory entries. */
/* point to the data bytes */
p = req - > rq_data ;
for ( i = 0 ; i < ff_searchcount ; i + + ) {
/* make sure we stay within the buffer */
if ( p > = req - > rq_data + req - > rq_ldata ) {
printk ( KERN_ERR " smb_proc_readdir_long: "
" dirent pointer outside buffer! "
" %p %d@%p \n " ,
p , req - > rq_ldata , req - > rq_data ) ;
result = - EIO ; /* always a comm. error? */
goto out_free ;
}
p = smb_decode_long_dirent ( server , p , info_level ,
& qname , & fattr , name_buf ) ;
/* ignore . and .. from the server */
if ( entries_seen = = 2 & & qname . name [ 0 ] = = ' . ' ) {
if ( qname . len = = 1 )
continue ;
if ( qname . name [ 1 ] = = ' . ' & & qname . len = = 2 )
continue ;
}
if ( ! smb_fill_cache ( filp , dirent , filldir , ctl ,
& qname , & fattr ) )
; /* stop reading? */
entries_seen + + ;
}
VERBOSE ( " received %d entries, eos=%d \n " , ff_searchcount , ff_eos ) ;
/*
* We might need the lastname for continuations .
*
* Note that some servers ( win95 ? ) point to the filename and
* others ( NT4 , Samba using NT1 ) to the dir entry . We assume
* here that those who do not point to a filename do not need
* this info to continue the listing .
*
* OS / 2 needs this and talks infolevel 1.
* NetApps want lastname with infolevel 260.
* win2k want lastname with infolevel 260 , and points to
* the record not to the name .
* Samba + CifsUnixExt doesn ' t need lastname .
*
* Both are happy if we return the data they point to . So we do .
* ( FIXME : above is not true with win2k )
*/
mask_len = 0 ;
if ( info_level ! = SMB_FIND_FILE_UNIX & &
ff_lastname > 0 & & ff_lastname < req - > rq_ldata ) {
lastname = req - > rq_data + ff_lastname ;
switch ( info_level ) {
case 260 :
mask_len = req - > rq_ldata - ff_lastname ;
break ;
case 1 :
/* lastname points to a length byte */
mask_len = * lastname + + ;
if ( ff_lastname + 1 + mask_len > req - > rq_ldata )
mask_len = req - > rq_ldata - ff_lastname - 1 ;
break ;
}
/*
* Update the mask string for the next message .
*/
if ( mask_len > 255 )
mask_len = 255 ;
if ( mask_len )
strncpy ( mask , lastname , mask_len ) ;
}
mask_len = strnlen ( mask , mask_len ) ;
VERBOSE ( " new mask, len=%d@%d of %d, mask=%.*s \n " ,
mask_len , ff_lastname , req - > rq_ldata , mask_len , mask ) ;
first = 0 ;
loop_count = 0 ;
}
out_free :
smb_rput ( req ) ;
out_name :
kfree ( name_buf ) ;
out :
unlock_kernel ( ) ;
return result ;
}
/*
* This version uses the trans2 TRANSACT2_FINDFIRST message
* to get the attribute data .
*
* Bugs Noted :
*/
static int
smb_proc_getattr_ff ( struct smb_sb_info * server , struct dentry * dentry ,
struct smb_fattr * fattr )
{
char * param , * mask ;
__u16 date , time ;
int mask_len , result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
param = req - > rq_buffer ;
mask = param + 12 ;
mask_len = smb_encode_path ( server , mask , SMB_MAXPATHLEN + 1 , dentry , NULL ) ;
if ( mask_len < 0 ) {
result = mask_len ;
goto out_free ;
}
VERBOSE ( " name=%s, len=%d \n " , mask , mask_len ) ;
WSET ( param , 0 , aSYSTEM | aHIDDEN | aDIR ) ;
WSET ( param , 2 , 1 ) ; /* max count */
WSET ( param , 4 , 1 ) ; /* close after this call */
WSET ( param , 6 , 1 ) ; /* info_level */
DSET ( param , 8 , 0 ) ;
req - > rq_trans2_command = TRANSACT2_FINDFIRST ;
req - > rq_ldata = 0 ;
req - > rq_data = NULL ;
req - > rq_lparm = 12 + mask_len ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
if ( result < 0 )
goto out_free ;
if ( req - > rq_rcls ! = 0 ) {
result = smb_errno ( req ) ;
# ifdef SMBFS_PARANOIA
if ( result ! = - ENOENT )
PARANOIA ( " error for %s, rcls=%d, err=%d \n " ,
mask , req - > rq_rcls , req - > rq_err ) ;
# endif
goto out_free ;
}
/* Make sure we got enough data ... */
result = - EINVAL ;
if ( req - > rq_ldata < 22 | | WVAL ( req - > rq_parm , 2 ) ! = 1 ) {
PARANOIA ( " bad result for %s, len=%d, count=%d \n " ,
mask , req - > rq_ldata , WVAL ( req - > rq_parm , 2 ) ) ;
goto out_free ;
}
/*
* Decode the response into the fattr . . .
*/
date = WVAL ( req - > rq_data , 0 ) ;
time = WVAL ( req - > rq_data , 2 ) ;
fattr - > f_ctime . tv_sec = date_dos2unix ( server , date , time ) ;
fattr - > f_ctime . tv_nsec = 0 ;
date = WVAL ( req - > rq_data , 4 ) ;
time = WVAL ( req - > rq_data , 6 ) ;
fattr - > f_atime . tv_sec = date_dos2unix ( server , date , time ) ;
fattr - > f_atime . tv_nsec = 0 ;
date = WVAL ( req - > rq_data , 8 ) ;
time = WVAL ( req - > rq_data , 10 ) ;
fattr - > f_mtime . tv_sec = date_dos2unix ( server , date , time ) ;
fattr - > f_mtime . tv_nsec = 0 ;
VERBOSE ( " name=%s, date=%x, time=%x, mtime=%ld \n " ,
mask , date , time , fattr - > f_mtime ) ;
fattr - > f_size = DVAL ( req - > rq_data , 12 ) ;
/* ULONG allocation size */
fattr - > attr = WVAL ( req - > rq_data , 20 ) ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
static int
smb_proc_getattr_core ( struct smb_sb_info * server , struct dentry * dir ,
struct smb_fattr * fattr )
{
int result ;
char * p ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
p = smb_setup_header ( req , SMBgetatr , 0 , 0 ) ;
result = smb_simple_encode_path ( req , & p , dir , NULL ) ;
if ( result < 0 )
goto out_free ;
smb_setup_bcc ( req , p ) ;
if ( ( result = smb_request_ok ( req , SMBgetatr , 10 , 0 ) ) < 0 )
goto out_free ;
fattr - > attr = WVAL ( req - > rq_header , smb_vwv0 ) ;
fattr - > f_mtime . tv_sec = local2utc ( server , DVAL ( req - > rq_header , smb_vwv1 ) ) ;
fattr - > f_mtime . tv_nsec = 0 ;
fattr - > f_size = DVAL ( req - > rq_header , smb_vwv3 ) ;
fattr - > f_ctime = fattr - > f_mtime ;
fattr - > f_atime = fattr - > f_mtime ;
# ifdef SMBFS_DEBUG_TIMESTAMP
printk ( " getattr_core: %s/%s, mtime=%ld \n " ,
DENTRY_PATH ( dir ) , fattr - > f_mtime ) ;
# endif
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
/*
* Bugs Noted :
* ( 1 ) Win 95 swaps the date and time fields in the standard info level .
*/
static int
smb_proc_getattr_trans2 ( struct smb_sb_info * server , struct dentry * dir ,
struct smb_request * req , int infolevel )
{
char * p , * param ;
int result ;
param = req - > rq_buffer ;
WSET ( param , 0 , infolevel ) ;
DSET ( param , 2 , 0 ) ;
result = smb_encode_path ( server , param + 6 , SMB_MAXPATHLEN + 1 , dir , NULL ) ;
if ( result < 0 )
goto out ;
p = param + 6 + result ;
req - > rq_trans2_command = TRANSACT2_QPATHINFO ;
req - > rq_ldata = 0 ;
req - > rq_data = NULL ;
req - > rq_lparm = p - param ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
if ( result < 0 )
goto out ;
if ( req - > rq_rcls ! = 0 ) {
VERBOSE ( " for %s: result=%d, rcls=%d, err=%d \n " ,
& param [ 6 ] , result , req - > rq_rcls , req - > rq_err ) ;
result = smb_errno ( req ) ;
goto out ;
}
result = - ENOENT ;
if ( req - > rq_ldata < 22 ) {
PARANOIA ( " not enough data for %s, len=%d \n " ,
& param [ 6 ] , req - > rq_ldata ) ;
goto out ;
}
result = 0 ;
out :
return result ;
}
static int
smb_proc_getattr_trans2_std ( struct smb_sb_info * server , struct dentry * dir ,
struct smb_fattr * attr )
{
u16 date , time ;
int off_date = 0 , off_time = 2 ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
result = smb_proc_getattr_trans2 ( server , dir , req , SMB_INFO_STANDARD ) ;
if ( result < 0 )
goto out_free ;
/*
* Kludge alert : Win 95 swaps the date and time field ,
* contrary to the CIFS docs and Win NT practice .
*/
if ( server - > mnt - > flags & SMB_MOUNT_WIN95 ) {
off_date = 2 ;
off_time = 0 ;
}
date = WVAL ( req - > rq_data , off_date ) ;
time = WVAL ( req - > rq_data , off_time ) ;
attr - > f_ctime . tv_sec = date_dos2unix ( server , date , time ) ;
attr - > f_ctime . tv_nsec = 0 ;
date = WVAL ( req - > rq_data , 4 + off_date ) ;
time = WVAL ( req - > rq_data , 4 + off_time ) ;
attr - > f_atime . tv_sec = date_dos2unix ( server , date , time ) ;
attr - > f_atime . tv_nsec = 0 ;
date = WVAL ( req - > rq_data , 8 + off_date ) ;
time = WVAL ( req - > rq_data , 8 + off_time ) ;
attr - > f_mtime . tv_sec = date_dos2unix ( server , date , time ) ;
attr - > f_mtime . tv_nsec = 0 ;
# ifdef SMBFS_DEBUG_TIMESTAMP
printk ( KERN_DEBUG " getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld \n " ,
DENTRY_PATH ( dir ) , date , time , attr - > f_mtime ) ;
# endif
attr - > f_size = DVAL ( req - > rq_data , 12 ) ;
attr - > attr = WVAL ( req - > rq_data , 20 ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
static int
smb_proc_getattr_trans2_all ( struct smb_sb_info * server , struct dentry * dir ,
struct smb_fattr * attr )
{
struct smb_request * req ;
int result ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
result = smb_proc_getattr_trans2 ( server , dir , req ,
SMB_QUERY_FILE_ALL_INFO ) ;
if ( result < 0 )
goto out_free ;
attr - > f_ctime = smb_ntutc2unixutc ( LVAL ( req - > rq_data , 0 ) ) ;
attr - > f_atime = smb_ntutc2unixutc ( LVAL ( req - > rq_data , 8 ) ) ;
attr - > f_mtime = smb_ntutc2unixutc ( LVAL ( req - > rq_data , 16 ) ) ;
/* change (24) */
attr - > attr = WVAL ( req - > rq_data , 32 ) ;
/* pad? (34) */
/* allocated size (40) */
attr - > f_size = LVAL ( req - > rq_data , 48 ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
static int
smb_proc_getattr_unix ( struct smb_sb_info * server , struct dentry * dir ,
struct smb_fattr * attr )
{
struct smb_request * req ;
int result ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
result = smb_proc_getattr_trans2 ( server , dir , req ,
SMB_QUERY_FILE_UNIX_BASIC ) ;
if ( result < 0 )
goto out_free ;
smb_decode_unix_basic ( attr , server , req - > rq_data ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
static int
smb_proc_getattr_95 ( struct smb_sb_info * server , struct dentry * dir ,
struct smb_fattr * attr )
{
struct inode * inode = dir - > d_inode ;
int result ;
/* FIXME: why not use the "all" version? */
result = smb_proc_getattr_trans2_std ( server , dir , attr ) ;
if ( result < 0 )
goto out ;
/*
* None of the getattr versions here can make win9x return the right
* filesize if there are changes made to an open file .
* A seek - to - end does return the right size , but we only need to do
* that on files we have written .
*/
if ( inode & & SMB_I ( inode ) - > flags & SMB_F_LOCALWRITE & &
smb_is_open ( inode ) )
{
__u16 fileid = SMB_I ( inode ) - > fileid ;
attr - > f_size = smb_proc_seek ( server , fileid , 2 , 0 ) ;
}
out :
return result ;
}
static int
smb_proc_ops_wait ( struct smb_sb_info * server )
{
int result ;
result = wait_event_interruptible_timeout ( server - > conn_wq ,
server - > conn_complete , 30 * HZ ) ;
if ( ! result | | signal_pending ( current ) )
return - EIO ;
return 0 ;
}
static int
smb_proc_getattr_null ( struct smb_sb_info * server , struct dentry * dir ,
struct smb_fattr * fattr )
{
int result ;
if ( smb_proc_ops_wait ( server ) < 0 )
return - EIO ;
smb_init_dirent ( server , fattr ) ;
result = server - > ops - > getattr ( server , dir , fattr ) ;
smb_finish_dirent ( server , fattr ) ;
return result ;
}
static int
smb_proc_readdir_null ( struct file * filp , void * dirent , filldir_t filldir ,
struct smb_cache_control * ctl )
{
2006-12-08 13:37:39 +03:00
struct smb_sb_info * server = server_from_dentry ( filp - > f_path . dentry ) ;
2005-04-17 02:20:36 +04:00
if ( smb_proc_ops_wait ( server ) < 0 )
return - EIO ;
return server - > ops - > readdir ( filp , dirent , filldir , ctl ) ;
}
int
smb_proc_getattr ( struct dentry * dir , struct smb_fattr * fattr )
{
struct smb_sb_info * server = server_from_dentry ( dir ) ;
int result ;
smb_init_dirent ( server , fattr ) ;
result = server - > ops - > getattr ( server , dir , fattr ) ;
smb_finish_dirent ( server , fattr ) ;
return result ;
}
/*
* Because of bugs in the core protocol , we use this only to set
* attributes . See smb_proc_settime ( ) below for timestamp handling .
*
* Bugs Noted :
* ( 1 ) If mtime is non - zero , both Win 3.1 and Win 95 fail
* with an undocumented error ( ERRDOS code 50 ) . Setting
* mtime to 0 allows the attributes to be set .
* ( 2 ) The extra parameters following the name string aren ' t
* in the CIFS docs , but seem to be necessary for operation .
*/
static int
smb_proc_setattr_core ( struct smb_sb_info * server , struct dentry * dentry ,
__u16 attr )
{
char * p ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
p = smb_setup_header ( req , SMBsetatr , 8 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , attr ) ;
DSET ( req - > rq_header , smb_vwv1 , 0 ) ; /* mtime */
WSET ( req - > rq_header , smb_vwv3 , 0 ) ; /* reserved values */
WSET ( req - > rq_header , smb_vwv4 , 0 ) ;
WSET ( req - > rq_header , smb_vwv5 , 0 ) ;
WSET ( req - > rq_header , smb_vwv6 , 0 ) ;
WSET ( req - > rq_header , smb_vwv7 , 0 ) ;
result = smb_simple_encode_path ( req , & p , dentry , NULL ) ;
if ( result < 0 )
goto out_free ;
if ( p + 2 > ( char * ) req - > rq_buffer + req - > rq_bufsize ) {
result = - ENAMETOOLONG ;
goto out_free ;
}
* p + + = 4 ;
* p + + = 0 ;
smb_setup_bcc ( req , p ) ;
result = smb_request_ok ( req , SMBsetatr , 0 , 0 ) ;
if ( result < 0 )
goto out_free ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
/*
* Because of bugs in the trans2 setattr messages , we must set
* attributes and timestamps separately . The core SMBsetatr
* message seems to be the only reliable way to set attributes .
*/
int
smb_proc_setattr ( struct dentry * dir , struct smb_fattr * fattr )
{
struct smb_sb_info * server = server_from_dentry ( dir ) ;
int result ;
VERBOSE ( " setting %s/%s, open=%d \n " ,
DENTRY_PATH ( dir ) , smb_is_open ( dir - > d_inode ) ) ;
result = smb_proc_setattr_core ( server , dir , fattr - > attr ) ;
return result ;
}
/*
* Sets the timestamps for an file open with write permissions .
*/
static int
smb_proc_setattr_ext ( struct smb_sb_info * server ,
struct inode * inode , struct smb_fattr * fattr )
{
__u16 date , time ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
smb_setup_header ( req , SMBsetattrE , 7 , 0 ) ;
WSET ( req - > rq_header , smb_vwv0 , SMB_I ( inode ) - > fileid ) ;
/* We don't change the creation time */
WSET ( req - > rq_header , smb_vwv1 , 0 ) ;
WSET ( req - > rq_header , smb_vwv2 , 0 ) ;
date_unix2dos ( server , fattr - > f_atime . tv_sec , & date , & time ) ;
WSET ( req - > rq_header , smb_vwv3 , date ) ;
WSET ( req - > rq_header , smb_vwv4 , time ) ;
date_unix2dos ( server , fattr - > f_mtime . tv_sec , & date , & time ) ;
WSET ( req - > rq_header , smb_vwv5 , date ) ;
WSET ( req - > rq_header , smb_vwv6 , time ) ;
# ifdef SMBFS_DEBUG_TIMESTAMP
printk ( KERN_DEBUG " smb_proc_setattr_ext: date=%d, time=%d, mtime=%ld \n " ,
date , time , fattr - > f_mtime ) ;
# endif
req - > rq_flags | = SMB_REQ_NORETRY ;
result = smb_request_ok ( req , SMBsetattrE , 0 , 0 ) ;
if ( result < 0 )
goto out_free ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
/*
* Bugs Noted :
* ( 1 ) The TRANSACT2_SETPATHINFO message under Win NT 4.0 doesn ' t
* set the file ' s attribute flags .
*/
static int
smb_proc_setattr_trans2 ( struct smb_sb_info * server ,
struct dentry * dir , struct smb_fattr * fattr )
{
__u16 date , time ;
char * p , * param ;
int result ;
char data [ 26 ] ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
param = req - > rq_buffer ;
WSET ( param , 0 , 1 ) ; /* Info level SMB_INFO_STANDARD */
DSET ( param , 2 , 0 ) ;
result = smb_encode_path ( server , param + 6 , SMB_MAXPATHLEN + 1 , dir , NULL ) ;
if ( result < 0 )
goto out_free ;
p = param + 6 + result ;
WSET ( data , 0 , 0 ) ; /* creation time */
WSET ( data , 2 , 0 ) ;
date_unix2dos ( server , fattr - > f_atime . tv_sec , & date , & time ) ;
WSET ( data , 4 , date ) ;
WSET ( data , 6 , time ) ;
date_unix2dos ( server , fattr - > f_mtime . tv_sec , & date , & time ) ;
WSET ( data , 8 , date ) ;
WSET ( data , 10 , time ) ;
# ifdef SMBFS_DEBUG_TIMESTAMP
printk ( KERN_DEBUG " setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld \n " ,
DENTRY_PATH ( dir ) , date , time , fattr - > f_mtime ) ;
# endif
DSET ( data , 12 , 0 ) ; /* size */
DSET ( data , 16 , 0 ) ; /* blksize */
WSET ( data , 20 , 0 ) ; /* attr */
DSET ( data , 22 , 0 ) ; /* ULONG EA size */
req - > rq_trans2_command = TRANSACT2_SETPATHINFO ;
req - > rq_ldata = 26 ;
req - > rq_data = data ;
req - > rq_lparm = p - param ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
if ( result < 0 )
goto out_free ;
result = 0 ;
if ( req - > rq_rcls ! = 0 )
result = smb_errno ( req ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
/*
* ATTR_MODE 0x001
* ATTR_UID 0x002
* ATTR_GID 0x004
* ATTR_SIZE 0x008
* ATTR_ATIME 0x010
* ATTR_MTIME 0x020
* ATTR_CTIME 0x040
* ATTR_ATIME_SET 0x080
* ATTR_MTIME_SET 0x100
* ATTR_FORCE 0x200
* ATTR_ATTR_FLAG 0x400
*
* major / minor should only be set by mknod .
*/
int
smb_proc_setattr_unix ( struct dentry * d , struct iattr * attr ,
unsigned int major , unsigned int minor )
{
struct smb_sb_info * server = server_from_dentry ( d ) ;
u64 nttime ;
char * p , * param ;
int result ;
char data [ 100 ] ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
param = req - > rq_buffer ;
DEBUG1 ( " valid flags = 0x%04x \n " , attr - > ia_valid ) ;
WSET ( param , 0 , SMB_SET_FILE_UNIX_BASIC ) ;
DSET ( param , 2 , 0 ) ;
result = smb_encode_path ( server , param + 6 , SMB_MAXPATHLEN + 1 , d , NULL ) ;
if ( result < 0 )
goto out_free ;
p = param + 6 + result ;
/* 0 L file size in bytes */
/* 8 L file size on disk in bytes (block count) */
/* 40 L uid */
/* 48 L gid */
/* 56 W file type enum */
/* 60 L devmajor */
/* 68 L devminor */
/* 76 L unique ID (inode) */
/* 84 L permissions */
/* 92 L link count */
LSET ( data , 0 , SMB_SIZE_NO_CHANGE ) ;
LSET ( data , 8 , SMB_SIZE_NO_CHANGE ) ;
LSET ( data , 16 , SMB_TIME_NO_CHANGE ) ;
LSET ( data , 24 , SMB_TIME_NO_CHANGE ) ;
LSET ( data , 32 , SMB_TIME_NO_CHANGE ) ;
LSET ( data , 40 , SMB_UID_NO_CHANGE ) ;
LSET ( data , 48 , SMB_GID_NO_CHANGE ) ;
2006-01-08 12:04:50 +03:00
DSET ( data , 56 , smb_filetype_from_mode ( attr - > ia_mode ) ) ;
2005-04-17 02:20:36 +04:00
LSET ( data , 60 , major ) ;
LSET ( data , 68 , minor ) ;
LSET ( data , 76 , 0 ) ;
LSET ( data , 84 , SMB_MODE_NO_CHANGE ) ;
LSET ( data , 92 , 0 ) ;
if ( attr - > ia_valid & ATTR_SIZE ) {
LSET ( data , 0 , attr - > ia_size ) ;
LSET ( data , 8 , 0 ) ; /* can't set anyway */
}
/*
* FIXME : check the conversion function it the correct one
*
* we can ' t set ctime but we might as well pass this to the server
* and let it ignore it .
*/
if ( attr - > ia_valid & ATTR_CTIME ) {
nttime = smb_unixutc2ntutc ( attr - > ia_ctime ) ;
LSET ( data , 16 , nttime ) ;
}
if ( attr - > ia_valid & ATTR_ATIME ) {
nttime = smb_unixutc2ntutc ( attr - > ia_atime ) ;
LSET ( data , 24 , nttime ) ;
}
if ( attr - > ia_valid & ATTR_MTIME ) {
nttime = smb_unixutc2ntutc ( attr - > ia_mtime ) ;
LSET ( data , 32 , nttime ) ;
}
if ( attr - > ia_valid & ATTR_UID ) {
LSET ( data , 40 , attr - > ia_uid ) ;
}
if ( attr - > ia_valid & ATTR_GID ) {
LSET ( data , 48 , attr - > ia_gid ) ;
}
if ( attr - > ia_valid & ATTR_MODE ) {
LSET ( data , 84 , attr - > ia_mode ) ;
}
req - > rq_trans2_command = TRANSACT2_SETPATHINFO ;
req - > rq_ldata = 100 ;
req - > rq_data = data ;
req - > rq_lparm = p - param ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
/*
* Set the modify and access timestamps for a file .
*
* Incredibly enough , in all of SMB there is no message to allow
* setting both attributes and timestamps at once .
*
* Bugs Noted :
* ( 1 ) Win 95 doesn ' t support the TRANSACT2_SETFILEINFO message
* with info level 1 ( INFO_STANDARD ) .
* ( 2 ) Win 95 seems not to support setting directory timestamps .
* ( 3 ) Under the core protocol apparently the only way to set the
* timestamp is to open and close the file .
*/
int
smb_proc_settime ( struct dentry * dentry , struct smb_fattr * fattr )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
struct inode * inode = dentry - > d_inode ;
int result ;
VERBOSE ( " setting %s/%s, open=%d \n " ,
DENTRY_PATH ( dentry ) , smb_is_open ( inode ) ) ;
/* setting the time on a Win95 server fails (tridge) */
if ( server - > opt . protocol > = SMB_PROTOCOL_LANMAN2 & &
! ( server - > mnt - > flags & SMB_MOUNT_WIN95 ) ) {
if ( smb_is_open ( inode ) & & SMB_I ( inode ) - > access ! = SMB_O_RDONLY )
result = smb_proc_setattr_ext ( server , inode , fattr ) ;
else
result = smb_proc_setattr_trans2 ( server , dentry , fattr ) ;
} else {
/*
* Fail silently on directories . . . timestamp can ' t be set ?
*/
result = 0 ;
if ( S_ISREG ( inode - > i_mode ) ) {
/*
* Set the mtime by opening and closing the file .
* Note that the file is opened read - only , but this
* still allows us to set the date ( tridge )
*/
result = - EACCES ;
if ( ! smb_is_open ( inode ) )
smb_proc_open ( server , dentry , SMB_O_RDONLY ) ;
if ( smb_is_open ( inode ) ) {
inode - > i_mtime = fattr - > f_mtime ;
result = smb_proc_close_inode ( server , inode ) ;
}
}
}
return result ;
}
int
2006-06-23 13:02:58 +04:00
smb_proc_dskattr ( struct dentry * dentry , struct kstatfs * attr )
2005-04-17 02:20:36 +04:00
{
2006-06-23 13:02:58 +04:00
struct smb_sb_info * server = SMB_SB ( dentry - > d_sb ) ;
2005-04-17 02:20:36 +04:00
int result ;
char * p ;
long unit ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 0 ) ) )
goto out ;
smb_setup_header ( req , SMBdskattr , 0 , 0 ) ;
if ( ( result = smb_request_ok ( req , SMBdskattr , 5 , 0 ) ) < 0 )
goto out_free ;
p = SMB_VWV ( req - > rq_header ) ;
unit = ( WVAL ( p , 2 ) * WVAL ( p , 4 ) ) > > SMB_ST_BLKSHIFT ;
attr - > f_blocks = WVAL ( p , 0 ) * unit ;
attr - > f_bsize = SMB_ST_BLKSIZE ;
attr - > f_bavail = attr - > f_bfree = WVAL ( p , 6 ) * unit ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
int
smb_proc_read_link ( struct smb_sb_info * server , struct dentry * d ,
char * buffer , int len )
{
char * p , * param ;
int result ;
struct smb_request * req ;
DEBUG1 ( " readlink of %s/%s \n " , DENTRY_PATH ( d ) ) ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
param = req - > rq_buffer ;
WSET ( param , 0 , SMB_QUERY_FILE_UNIX_LINK ) ;
DSET ( param , 2 , 0 ) ;
result = smb_encode_path ( server , param + 6 , SMB_MAXPATHLEN + 1 , d , NULL ) ;
if ( result < 0 )
goto out_free ;
p = param + 6 + result ;
req - > rq_trans2_command = TRANSACT2_QPATHINFO ;
req - > rq_ldata = 0 ;
req - > rq_data = NULL ;
req - > rq_lparm = p - param ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
if ( result < 0 )
goto out_free ;
DEBUG1 ( " for %s: result=%d, rcls=%d, err=%d \n " ,
& param [ 6 ] , result , req - > rq_rcls , req - > rq_err ) ;
/* copy data up to the \0 or buffer length */
result = len ;
if ( req - > rq_ldata < len )
result = req - > rq_ldata ;
strncpy ( buffer , req - > rq_data , result ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
/*
* Create a symlink object called dentry which points to oldpath .
* Samba does not permit dangling links but returns a suitable error message .
*/
int
smb_proc_symlink ( struct smb_sb_info * server , struct dentry * d ,
const char * oldpath )
{
char * p , * param ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
param = req - > rq_buffer ;
WSET ( param , 0 , SMB_SET_FILE_UNIX_LINK ) ;
DSET ( param , 2 , 0 ) ;
result = smb_encode_path ( server , param + 6 , SMB_MAXPATHLEN + 1 , d , NULL ) ;
if ( result < 0 )
goto out_free ;
p = param + 6 + result ;
req - > rq_trans2_command = TRANSACT2_SETPATHINFO ;
req - > rq_ldata = strlen ( oldpath ) + 1 ;
req - > rq_data = ( char * ) oldpath ;
req - > rq_lparm = p - param ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
if ( result < 0 )
goto out_free ;
DEBUG1 ( " for %s: result=%d, rcls=%d, err=%d \n " ,
& param [ 6 ] , result , req - > rq_rcls , req - > rq_err ) ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
/*
* Create a hard link object called new_dentry which points to dentry .
*/
int
smb_proc_link ( struct smb_sb_info * server , struct dentry * dentry ,
struct dentry * new_dentry )
{
char * p , * param ;
int result ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , PAGE_SIZE ) ) )
goto out ;
param = req - > rq_buffer ;
WSET ( param , 0 , SMB_SET_FILE_UNIX_HLINK ) ;
DSET ( param , 2 , 0 ) ;
result = smb_encode_path ( server , param + 6 , SMB_MAXPATHLEN + 1 ,
new_dentry , NULL ) ;
if ( result < 0 )
goto out_free ;
p = param + 6 + result ;
/* Grr, pointless separation of parameters and data ... */
req - > rq_data = p ;
req - > rq_ldata = smb_encode_path ( server , p , SMB_MAXPATHLEN + 1 ,
dentry , NULL ) ;
req - > rq_trans2_command = TRANSACT2_SETPATHINFO ;
req - > rq_lparm = p - param ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
if ( result < 0 )
goto out_free ;
DEBUG1 ( " for %s: result=%d, rcls=%d, err=%d \n " ,
& param [ 6 ] , result , req - > rq_rcls , req - > rq_err ) ;
result = 0 ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
static int
smb_proc_query_cifsunix ( struct smb_sb_info * server )
{
int result ;
int major , minor ;
u64 caps ;
char param [ 2 ] ;
struct smb_request * req ;
result = - ENOMEM ;
if ( ! ( req = smb_alloc_request ( server , 100 ) ) )
goto out ;
WSET ( param , 0 , SMB_QUERY_CIFS_UNIX_INFO ) ;
req - > rq_trans2_command = TRANSACT2_QFSINFO ;
req - > rq_ldata = 0 ;
req - > rq_data = NULL ;
req - > rq_lparm = 2 ;
req - > rq_parm = param ;
req - > rq_flags = 0 ;
result = smb_add_request ( req ) ;
if ( result < 0 )
goto out_free ;
if ( req - > rq_ldata < 12 ) {
PARANOIA ( " Not enough data \n " ) ;
goto out_free ;
}
major = WVAL ( req - > rq_data , 0 ) ;
minor = WVAL ( req - > rq_data , 2 ) ;
DEBUG1 ( " Server implements CIFS Extensions for UNIX systems v%d.%d \n " ,
major , minor ) ;
/* FIXME: verify that we are ok with this major/minor? */
caps = LVAL ( req - > rq_data , 4 ) ;
DEBUG1 ( " Server capabilities 0x%016llx \n " , caps ) ;
out_free :
smb_rput ( req ) ;
out :
return result ;
}
static void
install_ops ( struct smb_ops * dst , struct smb_ops * src )
{
memcpy ( dst , src , sizeof ( void * ) * SMB_OPS_NUM_STATIC ) ;
}
/* < LANMAN2 */
static struct smb_ops smb_ops_core =
{
. read = smb_proc_read ,
. write = smb_proc_write ,
. readdir = smb_proc_readdir_short ,
. getattr = smb_proc_getattr_core ,
. truncate = smb_proc_trunc32 ,
} ;
/* LANMAN2, OS/2, others? */
static struct smb_ops smb_ops_os2 =
{
. read = smb_proc_read ,
. write = smb_proc_write ,
. readdir = smb_proc_readdir_long ,
. getattr = smb_proc_getattr_trans2_std ,
. truncate = smb_proc_trunc32 ,
} ;
/* Win95, and possibly some NetApp versions too */
static struct smb_ops smb_ops_win95 =
{
. read = smb_proc_read , /* does not support 12word readX */
. write = smb_proc_write ,
. readdir = smb_proc_readdir_long ,
. getattr = smb_proc_getattr_95 ,
. truncate = smb_proc_trunc95 ,
} ;
/* Samba, NT4 and NT5 */
static struct smb_ops smb_ops_winNT =
{
. read = smb_proc_readX ,
. write = smb_proc_writeX ,
. readdir = smb_proc_readdir_long ,
. getattr = smb_proc_getattr_trans2_all ,
. truncate = smb_proc_trunc64 ,
} ;
/* Samba w/ unix extensions. Others? */
static struct smb_ops smb_ops_unix =
{
. read = smb_proc_readX ,
. write = smb_proc_writeX ,
. readdir = smb_proc_readdir_long ,
. getattr = smb_proc_getattr_unix ,
/* FIXME: core/ext/time setattr needs to be cleaned up! */
/* .setattr = smb_proc_setattr_unix, */
. truncate = smb_proc_trunc64 ,
} ;
/* Place holder until real ops are in place */
static struct smb_ops smb_ops_null =
{
. readdir = smb_proc_readdir_null ,
. getattr = smb_proc_getattr_null ,
} ;
void smb_install_null_ops ( struct smb_ops * ops )
{
install_ops ( ops , & smb_ops_null ) ;
}