diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/AUTHORS | 4 | ||||
-rw-r--r-- | fs/cifs/CHANGES | 32 | ||||
-rw-r--r-- | fs/cifs/README | 7 | ||||
-rw-r--r-- | fs/cifs/cifs_debug.c | 38 | ||||
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 38 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 85 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 166 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 29 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 505 | ||||
-rw-r--r-- | fs/cifs/connect.c | 143 | ||||
-rw-r--r-- | fs/cifs/dir.c | 108 | ||||
-rw-r--r-- | fs/cifs/fcntl.c | 8 | ||||
-rw-r--r-- | fs/cifs/file.c | 90 | ||||
-rw-r--r-- | fs/cifs/inode.c | 38 | ||||
-rw-r--r-- | fs/cifs/link.c | 5 | ||||
-rw-r--r-- | fs/cifs/misc.c | 90 | ||||
-rw-r--r-- | fs/cifs/netmisc.c | 4 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 49 | ||||
-rw-r--r-- | fs/cifs/transport.c | 227 |
21 files changed, 1378 insertions, 294 deletions
diff --git a/fs/cifs/AUTHORS b/fs/cifs/AUTHORS index 72fdc10dfdd..8848e4dfa02 100644 --- a/fs/cifs/AUTHORS +++ b/fs/cifs/AUTHORS @@ -32,6 +32,10 @@ Domen Puncer Jesper Juhl (in particular for lots of whitespace/formatting cleanup) Vince Negri and Dave Stahl (for finding an important caching bug) Adrian Bunk (kcalloc cleanups) +Miklos Szeredi +Kazeon team for various fixes especially for 2.4 version. +Asser Ferno (Change Notify support) +Shaggy (Dave Kleikamp) for inumerable small fs suggestions and some good cleanup Test case and Bug Report contributors ------------------------------------- diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 3196d4c4eed..6c73f020ef4 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,8 +1,24 @@ +Version 1.36 +------------ +Add support for mounting to older pre-CIFS servers such as Windows9x and ME. +For these older servers, add option for passing netbios name of server in +on mount (servernetbiosname). Add suspend support for power management, to +avoid cifsd thread preventing software suspend from working. +Add mount option for disabling the default behavior of sending byte range lock +requests to the server (necessary for certain applications which break with +mandatory lock behavior such as Evolution), and also mount option for +requesting case insensitive matching for path based requests (requesting +case sensitive is the default). + Version 1.35 ------------ Add writepage performance improvements. Fix path name conversions for long filenames on mounts which were done with "mapchars" mount option -specified. +specified. Ensure multiplex ids do not collide. Fix case in which +rmmod can oops if done soon after last unmount. Fix truncated +search (readdir) output when resume filename was a long filename. +Fix filename conversion when mapchars mount option was specified and +filename was a long filename. Version 1.34 ------------ @@ -11,7 +27,7 @@ Do not oops if root user kills cifs oplock kernel thread or kills the cifsd thread (NB: killing the cifs kernel threads is not recommended, unmount and rmmod cifs will kill them when they are no longer needed). Fix readdir to ASCII servers (ie older servers -which do not support Unicode) and also require asterik. +which do not support Unicode) and also require asterisk. Fix out of memory case in which data could be written one page off in the page cache. @@ -101,7 +117,7 @@ improperly zeroed buffer in CIFS Unix extensions set times call. Version 1.25 ------------ -Fix internationlization problem in cifs readdir with filenames that map to +Fix internationalization problem in cifs readdir with filenames that map to longer UTF8 strings than the string on the wire was in Unicode. Add workaround for readdir to netapp servers. Fix search rewind (seek into readdir to return non-consecutive entries). Do not do readdir when server negotiates @@ -276,7 +292,7 @@ Fix caching problem when files opened by multiple clients in which page cache could contain stale data, and write through did not occur often enough while file was still open when read ahead (read oplock) not allowed. Treat "sep=" when first mount option -as an overrride of comma as the default separator between mount +as an override of comma as the default separator between mount options. Version 1.01 @@ -286,7 +302,7 @@ Allow passwords longer than 16 bytes. Allow null password string. Version 1.00 ------------ Gracefully clean up failed mounts when attempting to mount to servers such as -Windows 98 that terminate tcp sessions during prototocol negotiation. Handle +Windows 98 that terminate tcp sessions during protocol negotiation. Handle embedded commas in mount parsing of passwords. Version 0.99 @@ -295,7 +311,7 @@ Invalidate local inode cached pages on oplock break and when last file instance is closed so that the client does not continue using stale local copy rather than later modified server copy of file. Do not reconnect when server drops the tcp session prematurely before negotiate -protocol response. Fix oops in roepen_file when dentry freed. Allow +protocol response. Fix oops in reopen_file when dentry freed. Allow the support for CIFS Unix Extensions to be disabled via proc interface. Version 0.98 @@ -637,7 +653,7 @@ versions of 2.4 kernel (now builds and works again on kernels at least as early Version 0.41 ------------ Various minor fixes for Connectathon Posix "basic" file i/o test suite. Directory caching fixed so hardlinked -files now return the correct rumber of links on fstat as they are repeatedly linked and unlinked. +files now return the correct number of links on fstat as they are repeatedly linked and unlinked. Version 0.40 ------------ @@ -704,7 +720,7 @@ session) and cleaned them up and made them more consistent with other cifs functions. 7) Server support for Unix extensions is now fully detected and FindFirst is implemented both ways -(with or without Unix exentions) but FindNext and QueryPathInfo with the Unix extensions are not completed, +(with or without Unix extensions) but FindNext and QueryPathInfo with the Unix extensions are not completed, nor is the symlink support using the Unix extensions 8) Started adding the readlink and follow_link code diff --git a/fs/cifs/README b/fs/cifs/README index 34b0cf7111f..3b610d08dc1 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -407,6 +407,13 @@ A partial list of the supported mount options follows: This has no effect if the server does not support Unicode on the wire. nomapchars Do not translate any of these seven characters (default). + nocase Request case insensitive path name matching (case + sensitive is the default if the server suports it). + nobrl Do not send byte range lock requests to the server. + This is necessary for certain applications that break + with cifs style mandatory byte range locks (and most + cifs servers do not yet support requesting advisory + byte range locks). remount remount the share (often used to change from ro to rw mounts or vice versa) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 4061e43471c..f4c6544468a 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -254,35 +254,51 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset, buf += sprintf(buf, "\tDISCONNECTED "); length += 14; } - item_length = sprintf(buf,"\nSMBs: %d Oplock Breaks: %d", + item_length = sprintf(buf, "\nSMBs: %d Oplock Breaks: %d", atomic_read(&tcon->num_smbs_sent), atomic_read(&tcon->num_oplock_brks)); buf += item_length; length += item_length; - item_length = sprintf(buf,"\nReads: %d Bytes %lld", + item_length = sprintf(buf, "\nReads: %d Bytes %lld", atomic_read(&tcon->num_reads), (long long)(tcon->bytes_read)); buf += item_length; length += item_length; - item_length = sprintf(buf,"\nWrites: %d Bytes: %lld", + item_length = sprintf(buf, "\nWrites: %d Bytes: %lld", atomic_read(&tcon->num_writes), (long long)(tcon->bytes_written)); + buf += item_length; + length += item_length; + item_length = sprintf(buf, + "\nLocks: %d HardLinks: %d Symlinks: %d", + atomic_read(&tcon->num_locks), + atomic_read(&tcon->num_hardlinks), + atomic_read(&tcon->num_symlinks)); + buf += item_length; + length += item_length; + + item_length = sprintf(buf, "\nOpens: %d Closes: %d Deletes: %d", + atomic_read(&tcon->num_opens), + atomic_read(&tcon->num_closes), + atomic_read(&tcon->num_deletes)); buf += item_length; length += item_length; - item_length = sprintf(buf, - "\nOpens: %d Deletes: %d\nMkdirs: %d Rmdirs: %d", - atomic_read(&tcon->num_opens), - atomic_read(&tcon->num_deletes), + item_length = sprintf(buf, "\nMkdirs: %d Rmdirs: %d", atomic_read(&tcon->num_mkdirs), atomic_read(&tcon->num_rmdirs)); buf += item_length; length += item_length; - item_length = sprintf(buf, - "\nRenames: %d T2 Renames %d", + item_length = sprintf(buf, "\nRenames: %d T2 Renames %d", atomic_read(&tcon->num_renames), atomic_read(&tcon->num_t2renames)); buf += item_length; length += item_length; + item_length = sprintf(buf, "\nFindFirst: %d FNext %d FClose %d", + atomic_read(&tcon->num_ffirst), + atomic_read(&tcon->num_fnext), + atomic_read(&tcon->num_fclose)); + buf += item_length; + length += item_length; } read_unlock(&GlobalSMBSeslock); @@ -360,7 +376,7 @@ cifs_proc_init(void) if (pde) pde->write_proc = oplockEnabled_write; - pde = create_proc_read_entry("ReenableOldCifsReaddirCode", 0, proc_fs_cifs, + pde = create_proc_read_entry("Experimental", 0, proc_fs_cifs, quotaEnabled_read, NULL); if (pde) pde->write_proc = quotaEnabled_write; @@ -419,7 +435,7 @@ cifs_proc_clean(void) remove_proc_entry("ExtendedSecurity",proc_fs_cifs); remove_proc_entry("PacketSigningEnabled",proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled",proc_fs_cifs); - remove_proc_entry("ReenableOldCifsReaddirCode",proc_fs_cifs); + remove_proc_entry("Experimental",proc_fs_cifs); remove_proc_entry("LookupCacheEnabled",proc_fs_cifs); remove_proc_entry("cifs", proc_root_fs); } diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index ec00d61d530..f799f6f0e72 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -24,6 +24,9 @@ #define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */ #define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */ #define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */ +#define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible. */ +#define CIFS_MOUNT_UNX_EMUL 0x80 /* Network compat with SFUnix emulation */ +#define CIFS_MOUNT_NO_BRL 0x100 /* No sending byte range locks to srv */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8cc23e7d0d5..43fb2aafa52 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -59,6 +59,8 @@ unsigned int ntlmv2_support = 0; unsigned int sign_CIFS_PDUs = 1; extern struct task_struct * oplockThread; /* remove sparse warning */ struct task_struct * oplockThread = NULL; +extern struct task_struct * dnotifyThread; /* remove sparse warning */ +struct task_struct * dnotifyThread = NULL; unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; module_param(CIFSMaxBufSize, int, 0); MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048"); @@ -73,6 +75,7 @@ module_param(cifs_max_pending, int, 0); MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256"); static DECLARE_COMPLETION(cifs_oplock_exited); +static DECLARE_COMPLETION(cifs_dnotify_exited); extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; @@ -781,6 +784,8 @@ static int cifs_oplock_thread(void * dummyarg) oplockThread = current; do { + if(try_to_freeze()) + continue; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1*HZ); @@ -834,8 +839,23 @@ static int cifs_oplock_thread(void * dummyarg) spin_unlock(&GlobalMid_Lock); } } while(!signal_pending(current)); - complete_and_exit (&cifs_oplock_exited, 0); oplockThread = NULL; + complete_and_exit (&cifs_oplock_exited, 0); +} + +static int cifs_dnotify_thread(void * dummyarg) +{ + daemonize("cifsdnotifyd"); + allow_signal(SIGTERM); + + dnotifyThread = current; + do { + if(try_to_freeze()) + continue; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(39*HZ); + } while(!signal_pending(current)); + complete_and_exit (&cifs_dnotify_exited, 0); } static int __init @@ -884,10 +904,16 @@ init_cifs(void) if (!rc) { rc = (int)kernel_thread(cifs_oplock_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_VM); - if(rc > 0) - return 0; - else + if(rc > 0) { + rc = (int)kernel_thread(cifs_dnotify_thread, NULL, + CLONE_FS | CLONE_FILES | CLONE_VM); + if(rc > 0) + return 0; + else + cERROR(1,("error %d create dnotify thread", rc)); + } else { cERROR(1,("error %d create oplock thread",rc)); + } } cifs_destroy_request_bufs(); } @@ -916,6 +942,10 @@ exit_cifs(void) send_sig(SIGTERM, oplockThread, 1); wait_for_completion(&cifs_oplock_exited); } + if(dnotifyThread) { + send_sig(SIGTERM, dnotifyThread, 1); + wait_for_completion(&cifs_dnotify_exited); + } } MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>"); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 1fd21f66f24..bb3404a99e5 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -81,6 +81,7 @@ extern int cifs_dir_notify(struct file *, unsigned long arg); /* Functions related to dir entries */ extern struct dentry_operations cifs_dentry_ops; +extern struct dentry_operations cifs_ci_dentry_ops; /* Functions related to symlinks */ extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); @@ -96,5 +97,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); extern int cifs_ioctl (struct inode * inode, struct file * filep, unsigned int command, unsigned long arg); -#define CIFS_VERSION "1.35" +#define CIFS_VERSION "1.36" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 81babab265e..a9c791edede 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -110,8 +110,9 @@ enum protocolEnum { */ struct TCP_Server_Info { - char server_Name[SERVER_NAME_LEN_WITH_NULL]; /* 15 chars + X'20'in 16th */ - char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; /* Unicode version of server_Name */ + /* 15 character server name + 0x20 16th byte indicating type = srv */ + char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; + char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; struct socket *ssocket; union { struct sockaddr_in sockAddr; @@ -147,8 +148,10 @@ struct TCP_Server_Info { /* (returned on Negotiate */ int capabilities; /* allow selective disabling of caps by smb sess */ __u16 timeZone; + __u16 CurrentMid; /* multiplex id - rotating counter */ char cryptKey[CIFS_CRYPTO_KEY_SIZE]; - char workstation_RFC1001_name[16]; /* 16th byte is always zero */ + /* 16th byte of RFC1001 workstation name is always null */ + char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ char mac_signing_key[CIFS_SESSION_KEY_SIZE + 16]; }; @@ -214,19 +217,41 @@ struct cifsTconInfo { atomic_t num_reads; atomic_t num_oplock_brks; atomic_t num_opens; + atomic_t num_closes; atomic_t num_deletes; atomic_t num_mkdirs; atomic_t num_rmdirs; atomic_t num_renames; atomic_t num_t2renames; + atomic_t num_ffirst; + atomic_t num_fnext; + atomic_t num_fclose; + atomic_t num_hardlinks; + atomic_t num_symlinks; + atomic_t num_locks; +#ifdef CONFIG_CIFS_STATS2 + unsigned long long time_writes; + unsigned long long time_reads; + unsigned long long time_opens; + unsigned long long time_deletes; + unsigned long long time_closes; + unsigned long long time_mkdirs; + unsigned long long time_rmdirs; + unsigned long long time_renames; + unsigned long long time_t2renames; + unsigned long long time_ffirst; + unsigned long long time_fnext; + unsigned long long time_fclose; +#endif /* CONFIG_CIFS_STATS2 */ __u64 bytes_read; __u64 bytes_written; spinlock_t stat_lock; -#endif +#endif /* CONFIG_CIFS_STATS */ FILE_SYSTEM_DEVICE_INFO fsDevInfo; FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if file system name truncated */ FILE_SYSTEM_UNIX_INFO fsUnixInfo; unsigned retry:1; + unsigned nocase:1; /* BB add field for back pointer to sb struct? */ }; @@ -306,6 +331,41 @@ CIFS_SB(struct super_block *sb) return sb->s_fs_info; } +static inline const char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) + return '/'; + else + return '\\'; +} + +#ifdef CONFIG_CIFS_STATS +#define cifs_stats_inc atomic_inc + +static inline void cifs_stats_bytes_written(struct cifsTconInfo *tcon, + unsigned int bytes) +{ + if (bytes) { + spin_lock(&tcon->stat_lock); + tcon->bytes_written += bytes; + spin_unlock(&tcon->stat_lock); + } +} + +static inline void cifs_stats_bytes_read(struct cifsTconInfo *tcon, + unsigned int bytes) +{ + spin_lock(&tcon->stat_lock); + tcon->bytes_read += bytes; + spin_unlock(&tcon->stat_lock); +} +#else + +#define cifs_stats_inc(field) do {} while(0) +#define cifs_stats_bytes_written(tcon, bytes) do {} while(0) +#define cifs_stats_bytes_read(tcon, bytes) do {} while(0) + +#endif /* one of these for every pending CIFS request to the server */ struct mid_q_entry { @@ -331,6 +391,20 @@ struct oplock_q_entry { __u16 netfid; }; +/* for pending dnotify requests */ +struct dir_notify_req { + struct list_head lhead; + __le16 Pid; + __le16 PidHigh; + __u16 Mid; + __u16 Tid; + __u16 Uid; + __u16 netfid; + __u32 filter; /* CompletionFilter (for multishot) */ + int multishot; + struct file * pfile; +}; + #define MID_FREE 0 #define MID_REQUEST_ALLOCATED 1 #define MID_REQUEST_SUBMITTED 2 @@ -399,6 +473,9 @@ GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ GLOBAL_EXTERN struct list_head GlobalOplock_Q; +GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; /* Outstanding dir notify requests */ +GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q; /* Dir notify response queue */ + /* * Global transaction id (XID) information */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index aede6a81316..cf466595b0d 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -36,9 +36,11 @@ #define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ #define SMB_COM_DELETE 0x06 /* trivial response */ #define SMB_COM_RENAME 0x07 /* trivial response */ +#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */ #define SMB_COM_SETATTR 0x09 /* trivial response */ #define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ #define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ +#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ #define SMB_COM_READ_ANDX 0x2E #define SMB_COM_WRITE_ANDX 0x2F #define SMB_COM_TRANSACTION2 0x32 @@ -52,6 +54,7 @@ #define SMB_COM_NT_TRANSACT 0xA0 #define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 #define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 /* no response */ #define SMB_COM_NT_RENAME 0xA5 /* trivial response */ /* Transact2 subcommand codes */ @@ -59,6 +62,7 @@ #define TRANS2_FIND_FIRST 0x01 #define TRANS2_FIND_NEXT 0x02 #define TRANS2_QUERY_FS_INFORMATION 0x03 +#define TRANS2_SET_FS_INFORMATION 0x04 #define TRANS2_QUERY_PATH_INFORMATION 0x05 #define TRANS2_SET_PATH_INFORMATION 0x06 #define TRANS2_QUERY_FILE_INFORMATION 0x07 @@ -267,10 +271,18 @@ /* CreateOptions */ #define CREATE_NOT_FILE 0x00000001 /* if set must not be file */ #define CREATE_WRITE_THROUGH 0x00000002 -#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ +#define CREATE_SEQUENTIAL 0x00000004 +#define CREATE_SYNC_ALERT 0x00000010 +#define CREATE_ASYNC_ALERT 0x00000020 +#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ +#define CREATE_NO_EA_KNOWLEDGE 0x00000200 +#define CREATE_EIGHT_DOT_THREE 0x00000400 #define CREATE_RANDOM_ACCESS 0x00000800 #define CREATE_DELETE_ON_CLOSE 0x00001000 +#define CREATE_OPEN_BY_ID 0x00002000 #define OPEN_REPARSE_POINT 0x00200000 +#define CREATE_OPTIONS_MASK 0x007FFFFF +#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ /* ImpersonationLevel flags */ #define SECURITY_ANONYMOUS 0 @@ -614,6 +626,7 @@ typedef struct smb_com_findclose_req { } FINDCLOSE_REQ; /* OpenFlags */ +#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */ #define REQ_OPLOCK 0x00000002 #define REQ_BATCHOPLOCK 0x00000004 #define REQ_OPENDIRONLY 0x00000008 @@ -669,6 +682,62 @@ typedef struct smb_com_open_rsp { __u16 ByteCount; /* bct = 0 */ } OPEN_RSP; +/* format of legacy open request */ +typedef struct smb_com_openx_req { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OpenFlags; + __le16 Mode; + __le16 Sattr; /* search attributes */ + __le16 FileAttributes; /* dos attrs */ + __le32 CreateTime; /* os2 format */ + __le16 OpenFunction; + __le32 EndOfFile; + __le32 Timeout; + __le32 Reserved; + __u16 ByteCount; /* file name follows */ + char fileName[1]; +} OPENX_REQ; + +typedef struct smb_com_openx_rsp { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le16 FileAttributes; + __le32 LastWriteTime; /* os2 format */ + __le32 EndOfFile; + __le16 Access; + __le16 FileType; + __le16 IPCState; + __le16 Action; + __u32 FileId; + __u16 Reserved; + __u16 ByteCount; +} OPENX_RSP; + +/* Legacy write request for older servers */ +typedef struct smb_com_writex_req { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; /* Timeout */ + __le16 WriteMode; /* 1 = write through */ + __le16 Remaining; + __le16 Reserved2; + __le16 DataLengthLow; + __le16 DataOffset; + __le16 ByteCount; + __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ + char Data[0]; +} WRITEX_REQ; + typedef struct smb_com_write_req { struct smb_hdr hdr; /* wct = 14 */ __u8 AndXCommand; @@ -700,6 +769,21 @@ typedef struct smb_com_write_rsp { __u16 ByteCount; } WRITE_RSP; +/* legacy read request for older servers */ +typedef struct smb_com_readx_req { + struct smb_hdr hdr; /* wct = 10 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 Reserved; + __le16 Remaining; + __le16 ByteCount; +} READX_REQ; + typedef struct smb_com_read_req { struct smb_hdr hdr; /* wct = 12 */ __u8 AndXCommand; @@ -876,6 +960,22 @@ typedef struct smb_com_create_directory_rsp { __u16 ByteCount; /* bct = 0 */ } CREATE_DIRECTORY_RSP; +typedef struct smb_com_query_information_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* 1 + namelen + 1 */ + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char FileName[1]; +} QUERY_INFORMATION_REQ; + +typedef struct smb_com_query_information_rsp { + struct smb_hdr hdr; /* wct = 10 */ + __le16 attr; + __le32 last_write_time; + __le32 size; + __u16 reserved[5]; + __le16 ByteCount; /* bcc = 0 */ +} QUERY_INFORMATION_RSP; + typedef struct smb_com_setattr_req { struct smb_hdr hdr; /* wct = 8 */ __le16 attr; @@ -1411,6 +1511,43 @@ typedef struct smb_com_transaction_qfsi_rsp { __u8 Pad; /* may be three bytes *//* followed by data area */ } TRANSACTION2_QFSI_RSP; + +/* SETFSInfo Levels */ +#define SMB_SET_CIFS_UNIX_INFO 0x200 +typedef struct smb_com_transaction2_setfsi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; /* 4 */ + __le16 ParameterOffset; + __le16 DataCount; /* 12 */ + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ + __le16 ByteCount; + __u8 Pad; + __u16 FileNum; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + __le16 ClientUnixMajor; /* Data start. */ + __le16 ClientUnixMinor; + __le64 ClientUnixCap; /* Data end */ +} TRANSACTION2_SETFSI_REQ; + +typedef struct smb_com_transaction2_setfsi_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} TRANSACTION2_SETFSI_RSP; + + typedef struct smb_com_transaction2_get_dfs_refer_req { struct smb_hdr hdr; /* wct = 15 */ __le16 TotalParameterCount; @@ -1551,12 +1688,20 @@ typedef struct { __le16 MinorVersionNumber; __le64 Capability; } FILE_SYSTEM_UNIX_INFO; /* Unix extensions info, level 0x200 */ + +/* Version numbers for CIFS UNIX major and minor. */ +#define CIFS_UNIX_MAJOR_VERSION 1 +#define CIFS_UNIX_MINOR_VERSION 0 + /* Linux/Unix extensions capability flags */ #define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ #define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */ #define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */ #define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */ +#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Use POSIX pathnames on the wire. */ + #define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */ + typedef struct { /* For undefined recommended transfer size return -1 in that field */ __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ @@ -1907,18 +2052,17 @@ struct data_blob { perhaps add a CreateDevice - to create Pipes and other special .inodes Also note POSIX open flags 2) Close - to return the last write time to do cache across close more safely - 3) PosixQFSInfo - to return statfs info - 4) FindFirst return unique inode number - what about resume key, two forms short (matches readdir) and full (enough info to cache inodes) - 5) Mkdir - set mode + 3) FindFirst return unique inode number - what about resume key, two + forms short (matches readdir) and full (enough info to cache inodes) + 4) Mkdir - set mode And under consideration: - 6) FindClose2 (return nanosecond timestamp ??) - 7) Use nanosecond timestamps throughout all time fields if + 5) FindClose2 (return nanosecond timestamp ??) + 6) Use nanosecond timestamps throughout all time fields if corresponding attribute flag is set - 8) sendfile - handle based copy - 9) Direct i/o - 10) "POSIX ACL" support - 11) Misc fcntls? + 7) sendfile - handle based copy + 8) Direct i/o + 9) Misc fcntls? what about fixing 64 bit alignment @@ -1974,7 +2118,7 @@ struct data_blob { */ -/* xsymlink is a symlink format that can be used +/* xsymlink is a symlink format (used by MacOS) that can be used to save symlink info in a regular file when mounted to operating systems that do not support the cifs Unix extensions or EAs (for xattr diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ea239dea571..6943f7c6de0 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -47,6 +47,10 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, struct smb_hdr * /* input */ , struct smb_hdr * /* out */ , int * /* bytes returned */ , const int long_op); +extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, + struct smb_hdr * /* input */ , int hdr_len, + const char * /* SMB data to send */ , int data_len, + int * /* bytes returned */ , const int long_op); extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); extern int is_valid_oplock_break(struct smb_hdr *smb); @@ -57,9 +61,9 @@ extern int decode_negTokenInit(unsigned char *security_blob, int length, extern int cifs_inet_pton(int, char * source, void *dst); extern int map_smb_to_linux_error(struct smb_hdr *smb); extern void header_assemble(struct smb_hdr *, char /* command */ , - const struct cifsTconInfo *, int /* specifies length - of fixed section (word count) in two byte units */ - ); + const struct cifsTconInfo *, int /* length of + fixed section (word count) in two byte units */); +extern __u16 GetNextMid(struct TCP_Server_Info *server); extern struct oplock_q_entry * AllocOplockQEntry(struct inode *, u16, struct cifsTconInfo *); extern void DeleteOplockQEntry(struct oplock_q_entry *); @@ -89,7 +93,7 @@ extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, const char *searchName, const struct nls_table *nls_codepage, - __u16 *searchHandle, struct cifs_search_info * psrch_inf, int map); + __u16 *searchHandle, struct cifs_search_info * psrch_inf, int map, const char dirsep); extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, __u16 searchHandle, struct cifs_search_info * psrch_inf); @@ -101,6 +105,10 @@ extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO * findData, const struct nls_table *nls_codepage, int remap); +extern int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, + FILE_ALL_INFO * findData, + const struct nls_table *nls_codepage, int remap); extern int CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, @@ -125,6 +133,9 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, int remap); extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); +extern int CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, + __u64 cap); + extern int CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon); extern int CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon); @@ -207,6 +218,11 @@ extern int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon, const int access_flags, const int omode, __u16 * netfid, int *pOplock, FILE_ALL_INFO *, const struct nls_table *nls_codepage, int remap); +extern int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, + const char *fileName, const int disposition, + const int access_flags, const int omode, + __u16 * netfid, int *pOplock, FILE_ALL_INFO *, + const struct nls_table *nls_codepage, int remap); extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, const int smb_file_id); @@ -222,7 +238,7 @@ extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 offset, unsigned int *nbytes, - const char __user *buf,const int long_op); + const char *buf,const int long_op); extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, __u64 * inode_number, const struct nls_table *nls_codepage, @@ -264,7 +280,8 @@ extern int CIFSSMBCopy(int xid, int remap_special_chars); extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, const int notify_subdirs,const __u16 netfid, - __u32 filter, const struct nls_table *nls_codepage); + __u32 filter, struct file * file, int multishot, + const struct nls_table *nls_codepage); extern ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char * EAData, size_t bufsize, const struct nls_table *nls_codepage, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 0db0b313d71..b8830118f09 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -166,11 +166,9 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,wct); -#ifdef CONFIG_CIFS_STATS - if(tcon != NULL) { - atomic_inc(&tcon->num_smbs_sent); - } -#endif /* CONFIG_CIFS_STATS */ + if(tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); + return rc; } @@ -269,11 +267,9 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, wct /*wct */ ); -#ifdef CONFIG_CIFS_STATS - if(tcon != NULL) { - atomic_inc(&tcon->num_smbs_sent); - } -#endif /* CONFIG_CIFS_STATS */ + if(tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); + return rc; } @@ -330,7 +326,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; - + pSMB->hdr.Mid = GetNextMid(server); pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; if (extended_security) pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; @@ -422,8 +418,8 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) } } - if (pSMB) - cifs_buf_release(pSMB); + + cifs_buf_release(pSMB); return rc; } @@ -518,6 +514,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */ if(ses->server) { + pSMB->hdr.Mid = GetNextMid(ses->server); + if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; @@ -537,9 +535,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) rc = -ESHUTDOWN; } } - if (pSMB) - cifs_small_buf_release(pSMB); up(&ses->sesSem); + cifs_small_buf_release(pSMB); /* if session dead then we do not need to do ulogoff, since server closed smb session, no sense reporting @@ -583,14 +580,10 @@ DelFileRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_deletes); if (rc) { cFYI(1, ("Error in RMFile = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&tcon->num_deletes); - } -#endif cifs_buf_release(pSMB); if (rc == -EAGAIN) @@ -632,14 +625,10 @@ RmDirRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_rmdirs); if (rc) { cFYI(1, ("Error in RMDir = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&tcon->num_rmdirs); - } -#endif cifs_buf_release(pSMB); if (rc == -EAGAIN) @@ -680,20 +669,157 @@ MkDirRetry: pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_mkdirs); if (rc) { cFYI(1, ("Error in Mkdir = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&tcon->num_mkdirs); - } -#endif + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto MkDirRetry; return rc; } +static __u16 convert_disposition(int disposition) +{ + __u16 ofun = 0; + + switch (disposition) { + case FILE_SUPERSEDE: + ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; + break; + case FILE_OPEN: + ofun = SMBOPEN_OAPPEND; + break; + case FILE_CREATE: + ofun = SMBOPEN_OCREATE; + break; + case FILE_OPEN_IF: + ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND; + break; + case FILE_OVERWRITE: + ofun = SMBOPEN_OTRUNC; + break; + case FILE_OVERWRITE_IF: + ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; + break; + default: + cFYI(1,("unknown disposition %d",disposition)); + ofun = SMBOPEN_OAPPEND; /* regular open */ + } + return ofun; +} + +int +SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, + const char *fileName, const int openDisposition, + const int access_flags, const int create_options, __u16 * netfid, + int *pOplock, FILE_ALL_INFO * pfile_info, + const struct nls_table *nls_codepage, int remap) +{ + int rc = -EACCES; + OPENX_REQ *pSMB = NULL; + OPENX_RSP *pSMBr = NULL; + int bytes_returned; + int name_len; + __u16 count; + +OldOpenRetry: + rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->AndXCommand = 0xFF; /* none */ + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + count = 1; /* account for one byte pad to word boundary */ + name_len = + cifsConvertToUCS((__le16 *) (pSMB->fileName + 1), + fileName, PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB improve check for buffer overruns BB */ + count = 0; /* no pad */ + name_len = strnlen(fileName, PATH_MAX); + name_len++; /* trailing null */ + strncpy(pSMB->fileName, fileName, name_len); + } + if (*pOplock & REQ_OPLOCK) + pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK); + else if (*pOplock & REQ_BATCHOPLOCK) { + pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK); + } + pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO); + /* BB fixme add conversion for access_flags to bits 0 - 2 of mode */ + /* 0 = read + 1 = write + 2 = rw + 3 = execute + */ + pSMB->Mode = cpu_to_le16(2); + pSMB->Mode |= cpu_to_le16(0x40); /* deny none */ + /* set file as system file if special file such + as fifo and server expecting SFU style and + no Unix extensions */ + + if(create_options & CREATE_OPTION_SPECIAL) + pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); + else + pSMB->FileAttributes = cpu_to_le16(ATTR_NORMAL); + + /* if ((omode & S_IWUGO) == 0) + pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/ + /* Above line causes problems due to vfs splitting create into two + pieces - need to set mode after file created not while it is + being created */ + + /* BB FIXME BB */ +/* pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); */ + /* BB FIXME END BB */ + pSMB->OpenFunction = convert_disposition(openDisposition); + count += name_len; + pSMB->hdr.smb_buf_length += count; + + pSMB->ByteCount = cpu_to_le16(count); + /* long_op set to 1 to allow for oplock break timeouts */ + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 1); + cifs_stats_inc(&tcon->num_opens); + if (rc) { + cFYI(1, ("Error in Open = %d", rc)); + } else { + /* BB verify if wct == 15 */ + +/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field BB */ + + *netfid = pSMBr->Fid; /* cifs fid stays in le */ + /* Let caller know file was created so we can set the mode. */ + /* Do we care about the CreateAction in any other cases? */ + /* BB FIXME BB */ +/* if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) + *pOplock |= CIFS_CREATE_ACTION; */ + /* BB FIXME END */ + + if(pfile_info) { + pfile_info->CreationTime = 0; /* BB convert CreateTime*/ + pfile_info->LastAccessTime = 0; /* BB fixme */ + pfile_info->LastWriteTime = 0; /* BB fixme */ + pfile_info->ChangeTime = 0; /* BB fixme */ + pfile_info->Attributes = pSMBr->FileAttributes; + /* the file_info buf is endian converted by caller */ + pfile_info->AllocationSize = pSMBr->EndOfFile; + pfile_info->EndOfFile = pSMBr->EndOfFile; + pfile_info->NumberOfLinks = cpu_to_le32(1); + } + } + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto OldOpenRetry; + return rc; +} + int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon, const char *fileName, const int openDisposition, @@ -738,7 +864,13 @@ openRetry: } pSMB->DesiredAccess = cpu_to_le32(access_flags); pSMB->AllocationSize = 0; - pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); + /* set file as system file if special file such + as fifo and server expecting SFU style and + no Unix extensions */ + if(create_options & CREATE_OPTION_SPECIAL) + pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); + else + pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); /* XP does not handle ATTR_POSIX_SEMANTICS */ /* but it helps speed up case sensitive checks for other servers such as Samba */ @@ -752,7 +884,7 @@ openRetry: being created */ pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); pSMB->CreateDisposition = cpu_to_le32(openDisposition); - pSMB->CreateOptions = cpu_to_le32(create_options); + pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); /* BB Expirement with various impersonation levels and verify */ pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); pSMB->SecurityFlags = @@ -765,6 +897,7 @@ openRetry: /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 1); + cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, ("Error in Open = %d", rc)); } else { @@ -782,11 +915,8 @@ openRetry: pfile_info->EndOfFile = pSMBr->EndOfFile; pfile_info->NumberOfLinks = cpu_to_le32(1); } - -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_opens); -#endif } + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto openRetry; @@ -807,11 +937,16 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, READ_RSP *pSMBr = NULL; char *pReadData = NULL; int bytes_returned; + int wct; cFYI(1,("Reading %d bytes on fid %d",count,netfid)); + if(tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 12; + else + wct = 10; /* old style read */ *nbytes = 0; - rc = smb_init(SMB_COM_READ_ANDX, 12, tcon, (void **) &pSMB, + rc = smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; @@ -823,14 +958,26 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); - pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); + if(wct == 12) + pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); + else if((lseek >> 32) > 0) /* can not handle this big offset for old */ + return -EIO; + pSMB->Remaining = 0; pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); pSMB->MaxCountHigh = cpu_to_le32(count >> 16); - pSMB->ByteCount = 0; /* no need to do le conversion since it is 0 */ - + if(wct == 12) + pSMB->ByteCount = 0; /* no need to do le conversion since 0 */ + else { + /* old style read */ + struct smb_com_readx_req * pSMBW = + (struct smb_com_readx_req *)pSMB; + pSMBW->ByteCount = 0; + } + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_reads); if (rc) { cERROR(1, ("Send error in read = %d", rc)); } else { @@ -876,12 +1023,20 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, int rc = -EACCES; WRITE_REQ *pSMB = NULL; WRITE_RSP *pSMBr = NULL; - int bytes_returned; + int bytes_returned, wct; __u32 bytes_sent; __u16 byte_count; /* cFYI(1,("write at %lld %d bytes",offset,count));*/ - rc = smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB, + if(tcon->ses == NULL) + return -ECONNABORTED; + + if(tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 14; + else + wct = 12; + + rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; @@ -892,7 +1047,11 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + if(wct == 14) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + else if((offset >> 32) > 0) /* can not handle this big offset for old */ + return -EIO; + pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; @@ -929,10 +1088,18 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); pSMB->hdr.smb_buf_length += bytes_sent+1; - pSMB->ByteCount = cpu_to_le16(byte_count); + + if(wct == 14) + pSMB->ByteCount = cpu_to_le16(byte_count); + else { /* old style write has byte count 4 bytes earlier */ + struct smb_com_writex_req * pSMBW = + (struct smb_com_writex_req *)pSMB; + pSMBW->ByteCount = cpu_to_le16(byte_count); + } rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, long_op); + cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error in write = %d", rc)); *nbytes = 0; @@ -951,56 +1118,70 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, } #ifdef CONFIG_CIFS_EXPERIMENTAL -int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, +int +CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, - const __u64 offset, unsigned int *nbytes, const char __user *buf, + const __u64 offset, unsigned int *nbytes, const char *buf, const int long_op) { int rc = -EACCES; WRITE_REQ *pSMB = NULL; - WRITE_RSP *pSMBr = NULL; - /*int bytes_returned;*/ - unsigned bytes_sent; + int bytes_returned; + int smb_hdr_len; + __u32 bytes_sent; __u16 byte_count; + cFYI(1,("write2 at %lld %d bytes",offset,count)); /* BB removeme BB */ rc = small_smb_init(SMB_COM_WRITE_ANDX, 14, tcon, (void **) &pSMB); - if (rc) return rc; - - pSMBr = (WRITE_RSP *)pSMB; /* BB removeme BB */ - /* tcon and ses pointer are checked in smb_init */ if (tcon->ses->server == NULL) return -ECONNABORTED; - pSMB->AndXCommand = 0xFF; /* none */ + pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); pSMB->OffsetHigh = cpu_to_le32(offset >> 32); pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; - bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & ~0xFF; + + /* Can increase buffer size if buffer is big enough in some cases - ie + can send more if LARGE_WRITE_X capability returned by the server and if + our buffer is big enough or if we convert to iovecs on socket writes + and eliminate the copy to the CIFS buffer */ + if(tcon->ses->capabilities & CAP_LARGE_WRITE_X) { + bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); + } else { + bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) + & ~0xFF; + } + if (bytes_sent > count) bytes_sent = count; - pSMB->DataLengthHigh = 0; pSMB->DataOffset = cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); - byte_count = bytes_sent + 1 /* pad */ ; - pSMB->DataLengthLow = cpu_to_le16(bytes_sent); - pSMB->DataLengthHigh = 0; - pSMB->hdr.smb_buf_length += byte_count; + byte_count = bytes_sent + 1 /* pad */ ; /* BB fix this for sends > 64K */ + pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); + pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); + smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ + pSMB->hdr.smb_buf_length += bytes_sent+1; pSMB->ByteCount = cpu_to_le16(byte_count); -/* rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, buf, buflen, &bytes_returned, long_op); */ /* BB fixme BB */ + rc = SendReceive2(xid, tcon->ses, (struct smb_hdr *) pSMB, smb_hdr_len, + buf, bytes_sent, &bytes_returned, long_op); + cifs_stats_inc(&tcon->num_writes); if (rc) { - cFYI(1, ("Send error in write2 (large write) = %d", rc)); + cFYI(1, ("Send error in write = %d", rc)); *nbytes = 0; - } else - *nbytes = le16_to_cpu(pSMBr->Count); + } else { + WRITE_RSP * pSMBr = (WRITE_RSP *)pSMB; + *nbytes = le16_to_cpu(pSMBr->CountHigh); + *nbytes = (*nbytes) << 16; + *nbytes += le16_to_cpu(pSMBr->Count); + } cifs_small_buf_release(pSMB); @@ -1009,6 +1190,8 @@ int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, return rc; } + + #endif /* CIFS_EXPERIMENTAL */ int @@ -1065,7 +1248,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, timeout); - + cifs_stats_inc(&tcon->num_locks); if (rc) { cFYI(1, ("Send error in Lock = %d", rc)); } @@ -1099,6 +1282,7 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) pSMB->ByteCount = 0; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_closes); if (rc) { if(rc!=-EINTR) { /* EINTR is expected when user ctl-c to kill app */ @@ -1171,16 +1355,11 @@ renameRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_renames); if (rc) { cFYI(1, ("Send error in rename = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&tcon->num_renames); - } -#endif - cifs_buf_release(pSMB); if (rc == -EAGAIN) @@ -1255,14 +1434,11 @@ int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&pTcon->num_t2renames); if (rc) { cFYI(1,("Send error in Rename (by file handle) = %d", rc)); } -#ifdef CONFIG_CIFS_STATS - else { - atomic_inc(&pTcon->num_t2renames); - } -#endif + cifs_buf_release(pSMB); /* Note: On -EAGAIN error only caller can retry on handle based calls @@ -1416,6 +1592,7 @@ createSymLinkRetry: pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_symlinks); if (rc) { cFYI(1, ("Send error in SetPathInfo (create symlink) = %d", @@ -1505,6 +1682,7 @@ createHardLinkRetry: pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_hardlinks); if (rc) { cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc)); } @@ -1575,6 +1753,7 @@ winCreateHardLinkRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_hardlinks); if (rc) { cFYI(1, ("Send error in hard link (NT rename) = %d", rc)); } @@ -1775,8 +1954,7 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, } } qreparse_out: - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ @@ -2165,6 +2343,65 @@ GetExtAttrOut: #endif /* CONFIG_POSIX */ +/* Legacy Query Path Information call for lookup to old servers such + as Win9x/WinME */ +int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, + FILE_ALL_INFO * pFinfo, + const struct nls_table *nls_codepage, int remap) +{ + QUERY_INFORMATION_REQ * pSMB; + QUERY_INFORMATION_RSP * pSMBr; + int rc = 0; + int bytes_returned; + int name_len; + + cFYI(1, ("In SMBQPath path %s", searchName)); +QInfRetry: + rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = strnlen(searchName, PATH_MAX); + name_len++; /* trailing null */ + strncpy(pSMB->FileName, searchName, name_len); + } + pSMB->BufferFormat = 0x04; + name_len++; /* account for buffer type byte */ + pSMB->hdr.smb_buf_length += (__u16) name_len; + pSMB->ByteCount = cpu_to_le16(name_len); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Send error in QueryInfo = %d", rc)); + } else if (pFinfo) { /* decode response */ + memset(pFinfo, 0, sizeof(FILE_ALL_INFO)); + pFinfo->AllocationSize = (__le64) pSMBr->size; + pFinfo->EndOfFile = (__le64) pSMBr->size; + pFinfo->Attributes = (__le32) pSMBr->attr; + } else + rc = -EIO; /* bad buffer passed in */ + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QInfRetry; + + return rc; +} + + + + int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, @@ -2396,7 +2633,7 @@ findUniqueRetry: if (rc) { cFYI(1, ("Send error in FindFileDirInfo = %d", rc)); } else { /* decode response */ - + cifs_stats_inc(&tcon->num_ffirst); /* BB fill in */ } @@ -2414,7 +2651,7 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, const char *searchName, const struct nls_table *nls_codepage, __u16 * pnetfid, - struct cifs_search_info * psrch_inf, int remap) + struct cifs_search_info * psrch_inf, int remap, const char dirsep) { /* level 257 SMB_ */ TRANSACTION2_FFIRST_REQ *pSMB = NULL; @@ -2441,7 +2678,7 @@ findFirstRetry: it got remapped to 0xF03A as if it were part of the directory name instead of a wildcard */ name_len *= 2; - pSMB->FileName[name_len] = '\\'; + pSMB->FileName[name_len] = dirsep; pSMB->FileName[name_len+1] = 0; pSMB->FileName[name_len+2] = '*'; pSMB->FileName[name_len+3] = 0; @@ -2455,7 +2692,7 @@ findFirstRetry: if(name_len > buffersize-header) free buffer exit; BB */ strncpy(pSMB->FileName, searchName, name_len); - pSMB->FileName[name_len] = '\\'; + pSMB->FileName[name_len] = dirsep; pSMB->FileName[name_len+1] = '*'; pSMB->FileName[name_len+2] = 0; name_len += 3; @@ -2496,6 +2733,7 @@ findFirstRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->num_ffirst); if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */ /* BB Add code to handle unsupported level rc */ @@ -2617,7 +2855,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); - + cifs_stats_inc(&tcon->num_fnext); if (rc) { if (rc == -EBADF) { psrch_inf->endOfSearch = TRUE; @@ -2694,6 +2932,7 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle if (rc) { cERROR(1, ("Send error in FindClose = %d", rc)); } + cifs_stats_inc(&tcon->num_fclose); cifs_small_buf_release(pSMB); /* Since session is dead, search handle closed on server already */ @@ -2827,7 +3066,10 @@ getDFSRetry: (void **) &pSMBr); if (rc) return rc; - + + /* server pointer checked in called function, + but should never be null here anyway */ + pSMB->hdr.Mid = GetNextMid(ses->server); pSMB->hdr.Tid = ses->ipc_tid; pSMB->hdr.Uid = ses->Suid; if (ses->capabilities & CAP_STATUS32) { @@ -3257,6 +3499,77 @@ QFSUnixRetry: return rc; } +int +CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap) +{ +/* level 0x200 SMB_SET_CIFS_UNIX_INFO */ + TRANSACTION2_SETFSI_REQ *pSMB = NULL; + TRANSACTION2_SETFSI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cFYI(1, ("In SETFSUnixInfo")); +SETFSUnixRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 4; /* 2 bytes zero followed by info level. */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum) - 4; + offset = param_offset + params; + + pSMB->MaxParameterCount = cpu_to_le16(4); + pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */ + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION); + byte_count = 1 /* pad */ + params + 12; + + pSMB->DataCount = cpu_to_le16(12); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + + /* Params. */ + pSMB->FileNum = 0; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO); + + /* Data. */ + pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION); + pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION); + pSMB->ClientUnixCap = cpu_to_le64(cap); + + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cERROR(1, ("Send error in SETFSUnixInfo = %d", rc)); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc) { + rc = -EIO; /* bad smb */ + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SETFSUnixRetry; + + return rc; +} + + int CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon, @@ -3839,12 +4152,14 @@ setPermsRetry: } int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, - const int notify_subdirs, const __u16 netfid, - __u32 filter, const struct nls_table *nls_codepage) + const int notify_subdirs, const __u16 netfid, + __u32 filter, struct file * pfile, int multishot, + const struct nls_table *nls_codepage) { int rc = 0; struct smb_com_transaction_change_notify_req * pSMB = NULL; struct smb_com_transaction_change_notify_rsp * pSMBr = NULL; + struct dir_notify_req *dnotify_req; int bytes_returned; cFYI(1, ("In CIFSSMBNotify for file handle %d",(int)netfid)); @@ -3877,6 +4192,22 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, (struct smb_hdr *) pSMBr, &bytes_returned, -1); if (rc) { cFYI(1, ("Error in Notify = %d", rc)); + } else { + /* Add file to outstanding requests */ + dnotify_req = (struct dir_notify_req *) kmalloc( + sizeof(struct dir_notify_req), GFP_KERNEL); + dnotify_req->Pid = pSMB->hdr.Pid; + dnotify_req->PidHigh = pSMB->hdr.PidHigh; + dnotify_req->Mid = pSMB->hdr.Mid; + dnotify_req->Tid = pSMB->hdr.Tid; + dnotify_req->Uid = pSMB->hdr.Uid; + dnotify_req->netfid = netfid; + dnotify_req->pfile = pfile; + dnotify_req->filter = filter; + dnotify_req->multishot = multishot; + spin_lock(&GlobalMid_Lock); + list_add_tail(&dnotify_req->lhead, &GlobalDnotifyReqList); + spin_unlock(&GlobalMid_Lock); } cifs_buf_release(pSMB); return rc; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e568cc47a7f..196976049c0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -29,6 +29,7 @@ #include <linux/utsname.h> #include <linux/mempool.h> #include <linux/delay.h> +#include <linux/completion.h> #include <asm/uaccess.h> #include <asm/processor.h> #include "cifspdu.h" @@ -44,6 +45,8 @@ #define CIFS_PORT 445 #define RFC1001_PORT 139 +static DECLARE_COMPLETION(cifsd_complete); + extern void SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, @@ -60,6 +63,7 @@ struct smb_vol { char *in6_addr; /* ipv6 address as human readable form of in6_addr */ char *iocharset; /* local code page for mapping to and from Unicode */ char source_rfc1001_name[16]; /* netbios name of client */ + char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */ uid_t linux_uid; gid_t linux_gid; mode_t file_mode; @@ -74,6 +78,10 @@ struct smb_vol { unsigned server_ino:1; /* use inode numbers from server ie UniqueId */ unsigned direct_io:1; unsigned remap:1; /* set to remap seven reserved chars in filenames */ + unsigned posix_paths:1; /* unset to not ask for posix pathnames. */ + unsigned sfu_emul:1; + unsigned nocase; /* request case insensitive filenames */ + unsigned nobrl; /* disable sending byte range locks to srv */ unsigned int rsize; unsigned int wsize; unsigned int sockopt; @@ -82,7 +90,8 @@ struct smb_vol { static int ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char * netb_name); + char * netb_name, + char * server_netb_name); static int ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket); @@ -175,7 +184,8 @@ cifs_reconnect(struct TCP_Server_Info *server) } else { rc = ipv4_connect(&server->addr.sockAddr, &server->ssocket, - server->workstation_RFC1001_name); + server->workstation_RFC1001_name, + server->server_RFC1001_name); } if(rc) { msleep(3000); @@ -337,6 +347,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) atomic_inc(&tcpSesAllocCount); length = tcpSesAllocCount.counter; write_unlock(&GlobalSMBSeslock); + complete(&cifsd_complete); if(length > 1) { mempool_resize(cifs_req_poolp, length + cifs_min_rcv, @@ -344,6 +355,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } while (server->tcpStatus != CifsExiting) { + if(try_to_freeze()) + continue; if (bigbuf == NULL) { bigbuf = cifs_buf_get(); if(bigbuf == NULL) { @@ -674,7 +687,7 @@ multi_t2_fnd: msleep(125); } - if (list_empty(&server->pending_mid_q)) { + if (!list_empty(&server->pending_mid_q)) { /* mpx threads have not exited yet give them at least the smb send timeout time for long ops */ /* due to delays on oplock break requests, we need @@ -711,7 +724,7 @@ multi_t2_fnd: GFP_KERNEL); } - msleep(250); + complete_and_exit(&cifsd_complete, 0); return 0; } @@ -735,7 +748,9 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) toupper(system_utsname.nodename[i]); } vol->source_rfc1001_name[15] = 0; - + /* null target name indicates to use *SMBSERVR default called name + if we end up sending RFC1001 session initialize */ + vol->target_rfc1001_name[0] = 0; vol->linux_uid = current->uid; /* current->euid instead? */ vol->linux_gid = current->gid; vol->dir_mode = S_IRWXUGO; @@ -745,6 +760,9 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */ vol->rw = TRUE; + /* default is always to request posix paths. */ + vol->posix_paths = 1; + if (!options) return 1; @@ -985,7 +1003,31 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) /* The string has 16th byte zero still from set at top of the function */ if((i==15) && (value[i] != 0)) - printk(KERN_WARNING "CIFS: netbiosname longer than 15 and was truncated.\n"); + printk(KERN_WARNING "CIFS: netbiosname longer than 15 truncated.\n"); + } + } else if (strnicmp(data, "servern", 7) == 0) { + /* servernetbiosname specified override *SMBSERVER */ + if (!value || !*value || (*value == ' ')) { + cFYI(1,("empty server netbiosname specified")); + } else { + /* last byte, type, is 0x20 for servr type */ + memset(vol->target_rfc1001_name,0x20,16); + + for(i=0;i<15;i++) { + /* BB are there cases in which a comma can be + valid in this workstation netbios name (and need + special handling)? */ + + /* user or mount helper must uppercase netbiosname */ + if (value[i]==0) + break; + else + vol->target_rfc1001_name[i] = value[i]; + } + /* The string has 16th byte zero still from + set at top of the function */ + if((i==15) && (value[i] != 0)) + printk(KERN_WARNING "CIFS: server netbiosname longer than 15 truncated.\n"); } } else if (strnicmp(data, "credentials", 4) == 0) { /* ignore */ @@ -1023,6 +1065,27 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->remap = 1; } else if (strnicmp(data, "nomapchars", 10) == 0) { vol->remap = 0; + } else if (strnicmp(data, "sfu", 3) == 0) { + vol->sfu_emul = 1; + } else if (strnicmp(data, "nosfu", 5) == 0) { + vol->sfu_emul = 0; + } else if (strnicmp(data, "posixpaths", 10) == 0) { + vol->posix_paths = 1; + } else if (strnicmp(data, "noposixpaths", 12) == 0) { + vol->posix_paths = 0; + } else if ((strnicmp(data, "nocase", 6) == 0) || + (strnicmp(data, "ignorecase", 10) == 0)) { + vol->nocase = 1; + } else if (strnicmp(data, "brl", 3) == 0) { + vol->nobrl = 0; + } else if ((strnicmp(data, "nobrl", 5) == 0) || + (strnicmp(data, "nolock", 6) == 0)) { + vol->nobrl = 1; + /* turn off mandatory locking in mode + if remote locking is turned off since the + local vfs will do advisory */ + if(vol->file_mode == (S_IALLUGO & ~(S_ISUID | S_IXGRP))) + vol->file_mode = S_IALLUGO; } else if (strnicmp(data, "setuids", 7) == 0) { vol->setuids = 1; } else if (strnicmp(data, "nosetuids", 9) == 0) { @@ -1242,7 +1305,7 @@ static void rfc1002mangle(char * target,char * source, unsigned int length) static int ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char * netbios_name) + char * netbios_name, char * target_name) { int rc = 0; int connected = 0; @@ -1320,8 +1383,14 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, ses_init_buf = kcalloc(1, sizeof(struct rfc1002_session_packet), GFP_KERNEL); if(ses_init_buf) { ses_init_buf->trailer.session_req.called_len = 32; - rfc1002mangle(ses_init_buf->trailer.session_req.called_name, - DEFAULT_CIFS_CALLED_NAME,16); + if(target_name && (target_name[0] != 0)) { + rfc1002mangle(ses_init_buf->trailer.session_req.called_name, + target_name, 16); + } else { + rfc1002mangle(ses_init_buf->trailer.session_req.called_name, + DEFAULT_CIFS_CALLED_NAME,16); + } + ses_init_buf->trailer.session_req.calling_len = 32; /* calling name ends in null (byte 16) from old smb convention. */ @@ -1554,7 +1623,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, sin_server.sin_port = htons(volume_info.port); else sin_server.sin_port = 0; - rc = ipv4_connect(&sin_server,&csocket,volume_info.source_rfc1001_name); + rc = ipv4_connect(&sin_server,&csocket, + volume_info.source_rfc1001_name, + volume_info.target_rfc1001_name); if (rc < 0) { cERROR(1, ("Error connecting to IPv4 socket. Aborting operation")); @@ -1604,9 +1675,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, kfree(volume_info.password); FreeXid(xid); return rc; - } else - rc = 0; + } + wait_for_completion(&cifsd_complete); + rc = 0; memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16); + memcpy(srvTcp->server_RFC1001_name, volume_info.target_rfc1001_name,16); srvTcp->sequence_number = 0; } } @@ -1660,8 +1733,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, else cifs_sb->wsize = CIFSMaxBufSize; /* default */ if(cifs_sb->rsize < PAGE_CACHE_SIZE) { - cifs_sb->rsize = PAGE_CACHE_SIZE; - cERROR(1,("Attempt to set readsize for mount to less than one page (4096)")); + cifs_sb->rsize = PAGE_CACHE_SIZE; + /* Windows ME does this */ + cFYI(1,("Attempt to set readsize for mount to less than one page (4096)")); } cifs_sb->mnt_uid = volume_info.linux_uid; cifs_sb->mnt_gid = volume_info.linux_gid; @@ -1679,8 +1753,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; if(volume_info.no_xattr) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; + if(volume_info.sfu_emul) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; + if(volume_info.nobrl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; + if(volume_info.direct_io) { - cERROR(1,("mounting share using direct i/o")); + cFYI(1,("mounting share using direct i/o")); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; } @@ -1694,6 +1773,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, to the same server share the last value passed in for the retry flag is used */ tcon->retry = volume_info.retry; + tcon->nocase = volume_info.nocase; } else { tcon = tconInfoAlloc(); if (tcon == NULL) @@ -1722,6 +1802,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (!rc) { atomic_inc(&pSesInfo->inUse); tcon->retry = volume_info.retry; + tcon->nocase = volume_info.nocase; } } } @@ -1743,8 +1824,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, spin_lock(&GlobalMid_Lock); srvTcp->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); - if(srvTcp->tsk) + if(srvTcp->tsk) { send_sig(SIGKILL,srvTcp->tsk,1); + wait_for_completion(&cifsd_complete); + } } /* If find_unc succeeded then rc == 0 so we can not end */ if (tcon) /* up accidently freeing someone elses tcon struct */ @@ -1757,8 +1840,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, temp_rc = CIFSSMBLogoff(xid, pSesInfo); /* if the socketUseCount is now zero */ if((temp_rc == -ESHUTDOWN) && - (pSesInfo->server->tsk)) + (pSesInfo->server->tsk)) { send_sig(SIGKILL,pSesInfo->server->tsk,1); + wait_for_completion(&cifsd_complete); + } } else cFYI(1, ("No session or bad tcon")); sesInfoFree(pSesInfo); @@ -1781,6 +1866,17 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cFYI(1,("server negotiated posix acl support")); sb->s_flags |= MS_POSIXACL; } + + /* Try and negotiate POSIX pathnames if we can. */ + if (volume_info.posix_paths && (CIFS_UNIX_POSIX_PATHNAMES_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + if (!CIFSSMBSetFSUnixInfo(xid, tcon, CIFS_UNIX_POSIX_PATHNAMES_CAP)) { + cFYI(1,("negotiated posix pathnames support")); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; + } else { + cFYI(1,("posix pathnames support requested but not supported")); + } + } } } } @@ -1830,6 +1926,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 13 /* wct */ ); + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req_no_secext.AndXCommand = 0xFF; pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq); @@ -2105,6 +2202,8 @@ CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.AndXCommand = 0xFF; pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); @@ -2371,6 +2470,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT); @@ -2713,6 +2814,8 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.AndXCommand = 0xFF; @@ -3084,6 +3187,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, NULL /*no tid */ , 4 /*wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); smb_buffer->Uid = ses->Suid; pSMB = (TCONX_REQ *) smb_buffer; pSMBr = (TCONX_RSP *) smb_buffer_response; @@ -3205,8 +3310,10 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) return 0; } else if (rc == -ESHUTDOWN) { cFYI(1,("Waking up socket by sending it signal")); - if(cifsd_task) + if(cifsd_task) { send_sig(SIGKILL,cifsd_task,1); + wait_for_completion(&cifsd_complete); + } rc = 0; } /* else - we have an smb session left on this socket do not kill cifsd */ diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 3f3538d4a1f..cf90c9ad2c8 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -48,6 +48,7 @@ build_path_from_dentry(struct dentry *direntry) struct dentry *temp; int namelen = 0; char *full_path; + char dirsep = CIFS_DIR_SEP(CIFS_SB(direntry->d_sb)); if(direntry == NULL) return NULL; /* not much we can do if dentry is freed and @@ -74,7 +75,7 @@ cifs_bp_rename_retry: if (namelen < 0) { break; } else { - full_path[namelen] = '\\'; + full_path[namelen] = dirsep; strncpy(full_path + namelen + 1, temp->d_name.name, temp->d_name.len); cFYI(0, (" name: %s ", full_path + namelen)); @@ -184,6 +185,13 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, desiredAccess, CREATE_NOT_DIR, &fileHandle, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + if(rc == -EIO) { + /* old server, retry the open legacy style */ + rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, + desiredAccess, CREATE_NOT_DIR, + &fileHandle, &oplock, buf, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + } if (rc) { cFYI(1, ("cifs_create returned 0x%x ", rc)); } else { @@ -209,7 +217,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, CIFS_MOUNT_MAP_SPECIAL_CHR); } else { - /* BB implement via Windows security descriptors */ + /* BB implement mode setting via Windows security descriptors */ /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/ /* could set r/o dos attribute if mode & 0222 == 0 */ } @@ -226,10 +234,14 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, } if (rc != 0) { - cFYI(1,("Create worked but get_inode_info failed with rc = %d", + cFYI(1, + ("Create worked but get_inode_info failed rc = %d", rc)); } else { - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); } if((nd->flags & LOOKUP_OPEN) == FALSE) { @@ -303,8 +315,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev up(&direntry->d_sb->s_vfs_rename_sem); if(full_path == NULL) rc = -ENOMEM; - - if (full_path && (pTcon->ses->capabilities & CAP_UNIX)) { + else if (pTcon->ses->capabilities & CAP_UNIX) { if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,(__u64)current->euid,(__u64)current->egid, @@ -322,10 +333,49 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t dev if(!rc) { rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,xid); - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; if(rc == 0) d_instantiate(direntry, newinode); } + } else { + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { + int oplock = 0; + u16 fileHandle; + FILE_ALL_INFO * buf; + + cFYI(1,("sfu compat create special file")); + + buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL); + if(buf == NULL) { + kfree(full_path); + FreeXid(xid); + return -ENOMEM; + } + + rc = CIFSSMBOpen(xid, pTcon, full_path, + FILE_CREATE, /* fail if exists */ + GENERIC_WRITE /* BB would + WRITE_OWNER | WRITE_DAC be better? */, + /* Create a file and set the + file attribute to SYSTEM */ + CREATE_NOT_DIR | CREATE_OPTION_SPECIAL, + &fileHandle, &oplock, buf, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + + if(!rc) { + /* BB Do not bother to decode buf since no + local inode yet to put timestamps in */ + CIFSSMBClose(xid, pTcon, fileHandle); + d_drop(direntry); + } + kfree(buf); + /* add code here to set EAs */ + } } kfree(full_path); @@ -382,7 +432,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct name parent_dir_inode->i_sb,xid); if ((rc == 0) && (newInode != NULL)) { - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; d_add(direntry, newInode); /* since paths are not looked up by component - the parent directories are presumed to be good here */ @@ -441,3 +494,42 @@ struct dentry_operations cifs_dentry_ops = { /* d_delete: cifs_d_delete, *//* not needed except for debugging */ /* no need for d_hash, d_compare, d_release, d_iput ... yet. BB confirm this BB */ }; + +static int cifs_ci_hash(struct dentry *dentry, struct qstr *q) +{ + struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls; + unsigned long hash; + int i; + + hash = init_name_hash(); + for (i = 0; i < q->len; i++) + hash = partial_name_hash(nls_tolower(codepage, q->name[i]), + hash); + q->hash = end_name_hash(hash); + + return 0; +} + +static int cifs_ci_compare(struct dentry *dentry, struct qstr *a, + struct qstr *b) +{ + struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls; + + if ((a->len == b->len) && + (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) { + /* + * To preserve case, don't let an existing negative dentry's + * case take precedence. If a is not a negative dentry, this + * should have no side effects + */ + memcpy((unsigned char *)a->name, b->name, a->len); + return 0; + } + return 1; +} + +struct dentry_operations cifs_ci_dentry_ops = { + .d_revalidate = cifs_d_revalidate, + .d_hash = cifs_ci_hash, + .d_compare = cifs_ci_compare, +}; diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index 7d2a9202c39..d527e2c7607 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -100,8 +100,10 @@ int cifs_dir_notify(struct file * file, unsigned long arg) } else { filter = convert_to_cifs_notify_flags(arg); if(filter != 0) { - rc = CIFSSMBNotify(xid, pTcon, 0 /* no subdirs */, netfid, - filter, cifs_sb->local_nls); + rc = CIFSSMBNotify(xid, pTcon, + 0 /* no subdirs */, netfid, + filter, file, arg & DN_MULTISHOT, + cifs_sb->local_nls); } else { rc = -EINVAL; } @@ -109,7 +111,7 @@ int cifs_dir_notify(struct file * file, unsigned long arg) it would close automatically but may be a way to do it easily when inode freed or when notify info is cleared/changed */ - cERROR(1,("notify rc %d",rc)); + cFYI(1,("notify rc %d",rc)); } } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 3497125189d..5ecda554f91 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -256,6 +256,13 @@ int cifs_open(struct inode *inode, struct file *file) CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + if (rc == -EIO) { + /* Old server, try legacy style OpenX */ + rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, + desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags + & CIFS_MOUNT_MAP_SPECIAL_CHR); + } if (rc) { cFYI(1, ("cifs_open returned 0x%x ", rc)); goto out; @@ -744,14 +751,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, 15 seconds is plenty */ } -#ifdef CONFIG_CIFS_STATS - if (total_written > 0) { - atomic_inc(&pTcon->num_writes); - spin_lock(&pTcon->stat_lock); - pTcon->bytes_written += total_written; - spin_unlock(&pTcon->stat_lock); - } -#endif + cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if (file->f_dentry) { @@ -791,9 +791,8 @@ static ssize_t cifs_write(struct file *file, const char *write_data, pTcon = cifs_sb->tcon; - /* cFYI(1, - (" write %d bytes to offset %lld of %s", write_size, - *poffset, file->f_dentry->d_name.name)); */ + cFYI(1,(" write %d bytes to offset %lld of %s", write_size, + *poffset, file->f_dentry->d_name.name)); /* BB removeme BB */ if (file->private_data == NULL) return -EBADF; @@ -846,7 +845,20 @@ static ssize_t cifs_write(struct file *file, const char *write_data, if (rc != 0) break; } - +#ifdef CONFIG_CIFS_EXPERIMENTAL + /* BB FIXME We can not sign across two buffers yet */ + if((experimEnabled) && ((pTcon->ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) { + rc = CIFSSMBWrite2(xid, pTcon, + open_file->netfid, + min_t(const int, cifs_sb->wsize, + write_size - total_written), + *poffset, &bytes_written, + write_data + total_written, + long_op); + } else + /* BB FIXME fixup indentation of line below */ +#endif rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, @@ -867,14 +879,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, 15 seconds is plenty */ } -#ifdef CONFIG_CIFS_STATS - if (total_written > 0) { - atomic_inc(&pTcon->num_writes); - spin_lock(&pTcon->stat_lock); - pTcon->bytes_written += total_written; - spin_unlock(&pTcon->stat_lock); - } -#endif + cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if (file->f_dentry) { @@ -1207,12 +1212,10 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, if (rc != 0) break; } - rc = CIFSSMBRead(xid, pTcon, - open_file->netfid, - current_read_size, *poffset, - &bytes_read, &smb_read_data); - + open_file->netfid, + current_read_size, *poffset, + &bytes_read, &smb_read_data); pSMBr = (struct smb_com_read_rsp *)smb_read_data; if (copy_to_user(current_offset, smb_read_data + 4 /* RFC1001 hdr */ @@ -1235,12 +1238,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, return rc; } } else { -#ifdef CONFIG_CIFS_STATS - atomic_inc(&pTcon->num_reads); - spin_lock(&pTcon->stat_lock); - pTcon->bytes_read += total_read; - spin_unlock(&pTcon->stat_lock); -#endif + cifs_stats_bytes_read(pTcon, bytes_read); *poffset += bytes_read; } } @@ -1289,11 +1287,10 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, if (rc != 0) break; } - rc = CIFSSMBRead(xid, pTcon, - open_file->netfid, - current_read_size, *poffset, - &bytes_read, ¤t_offset); + open_file->netfid, + current_read_size, *poffset, + &bytes_read, ¤t_offset); } if (rc || (bytes_read == 0)) { if (total_read) { @@ -1303,12 +1300,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return rc; } } else { -#ifdef CONFIG_CIFS_STATS - atomic_inc(&pTcon->num_reads); - spin_lock(&pTcon->stat_lock); - pTcon->bytes_read += total_read; - spin_unlock(&pTcon->stat_lock); -#endif + cifs_stats_bytes_read(pTcon, total_read); *poffset += bytes_read; } } @@ -1452,10 +1444,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, } rc = CIFSSMBRead(xid, pTcon, - open_file->netfid, - read_size, offset, - &bytes_read, &smb_read_data); - /* BB need to check return code here */ + open_file->netfid, + read_size, offset, + &bytes_read, &smb_read_data); + + /* BB more RC checks ? */ if (rc== -EAGAIN) { if (smb_read_data) { cifs_buf_release(smb_read_data); @@ -1480,12 +1473,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, le16_to_cpu(pSMBr->DataOffset), &lru_pvec); i += bytes_read >> PAGE_CACHE_SHIFT; -#ifdef CONFIG_CIFS_STATS - atomic_inc(&pTcon->num_reads); - spin_lock(&pTcon->stat_lock); - pTcon->bytes_read += bytes_read; - spin_unlock(&pTcon->stat_lock); -#endif + cifs_stats_bytes_read(pTcon, bytes_read); if ((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) { i++; /* account for partial page */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 8d336a90025..0fbe02ebc03 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -166,7 +166,13 @@ int cifs_get_inode_info_unix(struct inode **pinode, inode->i_fop = &cifs_file_direct_ops; else inode->i_fop = &cifs_file_ops; + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop->lock = NULL; inode->i_data.a_ops = &cifs_addr_ops; + /* check if server can support readpages */ + if(pTcon->ses->server->maxBuf < + 4096 + MAX_CIFS_HDR_SIZE) + inode->i_data.a_ops->readpages = NULL; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode")); inode->i_op = &cifs_dir_inode_ops; @@ -213,8 +219,18 @@ int cifs_get_inode_info(struct inode **pinode, pfindData = (FILE_ALL_INFO *)buf; /* could do find first instead but this returns more info */ rc = CIFSSMBQPathInfo(xid, pTcon, search_path, pfindData, - cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + /* BB optimize code so we do not make the above call + when server claims no NT SMB support and the above call + failed at least once - set flag in tcon or mount */ + if((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { + rc = SMBQueryInformation(xid, pTcon, search_path, + pfindData, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + } + } /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ if (rc) { @@ -320,6 +336,16 @@ int cifs_get_inode_info(struct inode **pinode, on dirs */ inode->i_mode = cifs_sb->mnt_dir_mode; inode->i_mode |= S_IFDIR; + } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (cifsInfo->cifsAttrs & ATTR_SYSTEM) && + /* No need to le64 convert size of zero */ + (pfindData->EndOfFile == 0)) { + inode->i_mode = cifs_sb->mnt_file_mode; + inode->i_mode |= S_IFIFO; +/* BB Finish for SFU style symlinks and devies */ +/* } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (cifsInfo->cifsAttrs & ATTR_SYSTEM) && ) */ + } else { inode->i_mode |= S_IFREG; /* treat the dos attribute of read-only as read-only @@ -359,7 +385,12 @@ int cifs_get_inode_info(struct inode **pinode, inode->i_fop = &cifs_file_direct_ops; else inode->i_fop = &cifs_file_ops; + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop->lock = NULL; inode->i_data.a_ops = &cifs_addr_ops; + if(pTcon->ses->server->maxBuf < + 4096 + MAX_CIFS_HDR_SIZE) + inode->i_data.a_ops->readpages = NULL; } else if (S_ISDIR(inode->i_mode)) { cFYI(1, (" Directory inode ")); inode->i_op = &cifs_dir_inode_ops; @@ -577,7 +608,10 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) rc = cifs_get_inode_info(&newinode, full_path, NULL, inode->i_sb,xid); - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); if (direntry->d_inode) direntry->d_inode->i_nlink = 2; diff --git a/fs/cifs/link.c b/fs/cifs/link.c index ab925ef4f86..b43e071fe11 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -198,7 +198,10 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) ("Create symlink worked but get_inode_info failed with rc = %d ", rc)); } else { - direntry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + direntry->d_op = &cifs_ci_dentry_ops; + else + direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); } } diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 20ae4153f79..fafbdbfa63a 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -34,8 +34,6 @@ extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern struct task_struct * oplockThread; -static __u16 GlobalMid; /* multiplex id - rotating counter */ - /* The xid serves as a useful identifier for each incoming vfs request, in a similar way to the mid which is useful to track each sent smb, and CurrentXid can also provide a running counter (although it @@ -51,6 +49,8 @@ _GetXid(void) GlobalTotalActiveXid++; if (GlobalTotalActiveXid > GlobalMaxActiveXid) GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */ + if(GlobalTotalActiveXid > 65000) + cFYI(1,("warning: more than 65000 requests active")); xid = GlobalCurrentXid++; spin_unlock(&GlobalMid_Lock); return xid; @@ -218,6 +218,76 @@ cifs_small_buf_release(void *buf_to_free) return; } +/* + Find a free multiplex id (SMB mid). Otherwise there could be + mid collisions which might cause problems, demultiplexing the + wrong response to this request. Multiplex ids could collide if + one of a series requests takes much longer than the others, or + if a very large number of long lived requests (byte range + locks or FindNotify requests) are pending. No more than + 64K-1 requests can be outstanding at one time. If no + mids are available, return zero. A future optimization + could make the combination of mids and uid the key we use + to demultiplex on (rather than mid alone). + In addition to the above check, the cifs demultiplex + code already used the command code as a secondary + check of the frame and if signing is negotiated the + response would be discarded if the mid were the same + but the signature was wrong. Since the mid is not put in the + pending queue until later (when it is about to be dispatched) + we do have to limit the number of outstanding requests + to somewhat less than 64K-1 although it is hard to imagine + so many threads being in the vfs at one time. +*/ +__u16 GetNextMid(struct TCP_Server_Info *server) +{ + __u16 mid = 0; + __u16 last_mid; + int collision; + + if(server == NULL) + return mid; + + spin_lock(&GlobalMid_Lock); + last_mid = server->CurrentMid; /* we do not want to loop forever */ + server->CurrentMid++; + /* This nested loop looks more expensive than it is. + In practice the list of pending requests is short, + fewer than 50, and the mids are likely to be unique + on the first pass through the loop unless some request + takes longer than the 64 thousand requests before it + (and it would also have to have been a request that + did not time out) */ + while(server->CurrentMid != last_mid) { + struct list_head *tmp; + struct mid_q_entry *mid_entry; + + collision = 0; + if(server->CurrentMid == 0) + server->CurrentMid++; + + list_for_each(tmp, &server->pending_mid_q) { + mid_entry = list_entry(tmp, struct mid_q_entry, qhead); + + if ((mid_entry->mid == server->CurrentMid) && + (mid_entry->midState == MID_REQUEST_SUBMITTED)) { + /* This mid is in use, try a different one */ + collision = 1; + break; + } + } + if(collision == 0) { + mid = server->CurrentMid; + break; + } + server->CurrentMid++; + } + spin_unlock(&GlobalMid_Lock); + return mid; +} + +/* NB: MID can not be set if treeCon not passed in, in that + case it is responsbility of caller to set the mid */ void header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , const struct cifsTconInfo *treeCon, int word_count @@ -233,7 +303,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , (2 * word_count) + sizeof (struct smb_hdr) - 4 /* RFC 1001 length field does not count */ + 2 /* for bcc field itself */ ; - /* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */ + /* Note that this is the only network field that has to be converted + to big endian and it is done just before we send it */ buffer->Protocol[0] = 0xFF; buffer->Protocol[1] = 'S'; @@ -245,8 +316,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , buffer->Pid = cpu_to_le16((__u16)current->tgid); buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); spin_lock(&GlobalMid_Lock); - GlobalMid++; - buffer->Mid = GlobalMid; spin_unlock(&GlobalMid_Lock); if (treeCon) { buffer->Tid = treeCon->tid; @@ -256,8 +325,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , if (treeCon->ses->capabilities & CAP_STATUS32) { buffer->Flags2 |= SMBFLG2_ERR_STATUS; } - - buffer->Uid = treeCon->ses->Suid; /* always in LE format */ + /* Uid is not converted */ + buffer->Uid = treeCon->ses->Suid; + buffer->Mid = GetNextMid(treeCon->ses->server); if(multiuser_mount != 0) { /* For the multiuser case, there are few obvious technically */ /* possible mechanisms to match the local linux user (uid) */ @@ -305,6 +375,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , } if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) buffer->Flags2 |= SMBFLG2_DFS; + if (treeCon->nocase) + buffer->Flags |= SMBFLG_CASELESS; if((treeCon->ses) && (treeCon->ses->server)) if(treeCon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) @@ -448,9 +520,7 @@ is_valid_oplock_break(struct smb_hdr *buf) list_for_each(tmp, &GlobalTreeConnectionList) { tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); if (tcon->tid == buf->Tid) { -#ifdef CONFIG_CIFS_STATS - atomic_inc(&tcon->num_oplock_brks); -#endif + cifs_stats_inc(&tcon->num_oplock_brks); list_for_each(tmp1,&tcon->openFileList){ netfile = list_entry(tmp1,struct cifsFileInfo, tlist); diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index a92af41d441..873b812c0f4 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -133,7 +133,6 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = { int cifs_inet_pton(int address_family, char *cp,void *dst) { - struct in_addr address; int value; int digit; int i; @@ -190,8 +189,7 @@ cifs_inet_pton(int address_family, char *cp,void *dst) if (value > addr_class_max[end - bytes]) return 0; - address.s_addr = *((__be32 *) bytes) | htonl(value); - *((__be32 *)dst) = address.s_addr; + *((__be32 *)dst) = *((__be32 *) bytes) | htonl(value); return 1; /* success */ } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 22557716f9a..a1e8dc901de 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -91,7 +91,10 @@ static int construct_dentry(struct qstr *qstring, struct file *file, } *ptmp_inode = new_inode(file->f_dentry->d_sb); - tmp_dentry->d_op = &cifs_dentry_ops; + if (pTcon->nocase) + tmp_dentry->d_op = &cifs_ci_dentry_ops; + else + tmp_dentry->d_op = &cifs_dentry_ops; if(*ptmp_inode == NULL) return rc; rc = 1; @@ -148,6 +151,13 @@ static void fill_in_inode(struct inode *tmp_inode, tmp_inode->i_mode = cifs_sb->mnt_dir_mode; } tmp_inode->i_mode |= S_IFDIR; + } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (attr & ATTR_SYSTEM) && (end_of_file == 0)) { + *pobject_type = DT_FIFO; + tmp_inode->i_mode |= S_IFIFO; +/* BB Finish for SFU style symlinks and devies */ +/* } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + (attr & ATTR_SYSTEM) && ) { */ /* we no longer mark these because we could not follow them */ /* } else if (attr & ATTR_REPARSE) { *pobject_type = DT_LNK; @@ -187,11 +197,17 @@ static void fill_in_inode(struct inode *tmp_inode, tmp_inode->i_fop = &cifs_file_direct_ops; else tmp_inode->i_fop = &cifs_file_ops; + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + tmp_inode->i_fop->lock = NULL; tmp_inode->i_data.a_ops = &cifs_addr_ops; - + if((cifs_sb->tcon) && (cifs_sb->tcon->ses) && + (cifs_sb->tcon->ses->server->maxBuf < + 4096 + MAX_CIFS_HDR_SIZE)) + tmp_inode->i_data.a_ops->readpages = NULL; if(isNewInode) - return; /* No sense invalidating pages for new inode since we - have not started caching readahead file data yet */ + return; /* No sense invalidating pages for new inode + since have not started caching readahead file + data yet */ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) && (local_size == tmp_inode->i_size)) { @@ -290,7 +306,13 @@ static void unix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_fop = &cifs_file_direct_ops; else tmp_inode->i_fop = &cifs_file_ops; + if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + tmp_inode->i_fop->lock = NULL; tmp_inode->i_data.a_ops = &cifs_addr_ops; + if((cifs_sb->tcon) && (cifs_sb->tcon->ses) && + (cifs_sb->tcon->ses->server->maxBuf < + 4096 + MAX_CIFS_HDR_SIZE)) + tmp_inode->i_data.a_ops->readpages = NULL; if(isNewInode) return; /* No sense invalidating pages for new inode since we @@ -374,7 +396,7 @@ ffirst_retry: rc = CIFSFindFirst(xid, pTcon,full_path,cifs_sb->local_nls, &cifsFile->netfid, &cifsFile->srch_inf, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); if(rc == 0) cifsFile->invalidHandle = FALSE; if((rc == -EOPNOTSUPP) && @@ -536,7 +558,8 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, while((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && (rc == 0) && (cifsFile->srch_inf.endOfSearch == FALSE)){ cFYI(1,("calling findnext2")); - rc = CIFSFindNext(xid,pTcon,cifsFile->netfid, &cifsFile->srch_inf); + rc = CIFSFindNext(xid,pTcon,cifsFile->netfid, + &cifsFile->srch_inf); if(rc) return -ENOENT; } @@ -548,14 +571,13 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, char * end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + smbCalcSize((struct smb_hdr *) cifsFile->srch_inf.ntwrk_buf_start); -/* dump_cifs_file_struct(file,"found entry in fce "); */ 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; cFYI(1,("found entry - pos_in_buf %d",pos_in_buf)); current_entry = cifsFile->srch_inf.srch_entries_start; for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) { - /* go entry to next entry figuring out which we need to start with */ + /* go entry by entry figuring out which is first */ /* if( . or ..) skip */ rc = cifs_entry_is_dot(current_entry,cifsFile); @@ -586,7 +608,6 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, *num_to_ret = 0; } else *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf; -/* dump_cifs_file_struct(file, "end fce ");*/ return rc; } @@ -721,7 +742,8 @@ static int cifs_filldir(char *pfindEntry, struct file *file, (FILE_DIRECTORY_INFO *)pfindEntry,&obj_type, rc); } - rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,tmp_inode->i_ino,obj_type); + rc = filldir(direntry,qstring.name,qstring.len,file->f_pos, + tmp_inode->i_ino,obj_type); if(rc) { cFYI(1,("filldir rc = %d",rc)); } @@ -805,14 +827,12 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) FreeXid(xid); return -EIO; } -/* dump_cifs_file_struct(file, "Begin rdir "); */ cifs_sb = CIFS_SB(file->f_dentry->d_sb); pTcon = cifs_sb->tcon; if(pTcon == NULL) return -EINVAL; -/* cFYI(1,("readdir2 pos: %lld",file->f_pos)); */ switch ((int) file->f_pos) { case 0: @@ -866,7 +886,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) cifsFile->search_resume_name = NULL; */ /* BB account for . and .. in f_pos as special case */ - /* dump_cifs_file_struct(file, "rdir after default ");*/ rc = find_cifs_entry(xid,pTcon, file, ¤t_entry,&num_to_fill); @@ -906,14 +925,14 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) cifs_save_resume_key(current_entry,cifsFile); break; } else - current_entry = nxt_dir_entry(current_entry,end_of_smb); + current_entry = nxt_dir_entry(current_entry, + end_of_smb); } kfree(tmp_buf); break; } /* end switch */ rddir2_exit: - /* dump_cifs_file_struct(file, "end rdir "); */ FreeXid(xid); return rc; } diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 0046c219833..96f89eb6604 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -49,7 +49,8 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) return NULL; } - temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS); + temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp, + SLAB_KERNEL | SLAB_NOFS); if (temp == NULL) return temp; else { @@ -179,27 +180,24 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, return rc; } -#ifdef CIFS_EXPERIMENTAL -/* BB finish off this function, adding support for writing set of pages as iovec */ -/* and also adding support for operations that need to parse the response smb */ - -int -smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, - unsigned int smb_buf_length, struct kvec * write_vector - /* page list */, struct sockaddr *sin) +#ifdef CONFIG_CIFS_EXPERIMENTAL +static int +smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, + unsigned int smb_hdr_length, const char * data, unsigned int datalen, + struct sockaddr *sin) { int rc = 0; int i = 0; struct msghdr smb_msg; - number_of_pages += 1; /* account for SMB header */ - struct kvec * piov = kmalloc(number_of_pages * sizeof(struct kvec)); - unsigned len = smb_buf_length + 4; - + struct kvec iov[2]; + unsigned len = smb_hdr_length + 4; + if(ssocket == NULL) return -ENOTSOCK; /* BB eventually add reconnect code here */ - iov.iov_base = smb_buffer; - iov.iov_len = len; - + iov[0].iov_base = smb_buffer; + iov[0].iov_len = len; + iov[1].iov_base = data; + iov[1].iov_len = datalen; smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof (struct sockaddr); smb_msg.msg_control = NULL; @@ -212,12 +210,12 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, Flags2 is converted in SendReceive */ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); - cFYI(1, ("Sending smb of length %d ", smb_buf_length)); + cFYI(1, ("Sending smb: hdrlen %d datalen %d", + smb_hdr_length,datalen)); dump_smb(smb_buffer, len); - while (len > 0) { - rc = kernel_sendmsg(ssocket, &smb_msg, &iov, number_of_pages, - len); + while (len + datalen > 0) { + rc = kernel_sendmsg(ssocket, &smb_msg, iov, 2, len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; if(i > 60) { @@ -232,9 +230,23 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, } if (rc < 0) break; - iov.iov_base += rc; - iov.iov_len -= rc; - len -= rc; + if(iov[0].iov_len > 0) { + if(rc >= len) { + iov[0].iov_len = 0; + rc -= len; + len = 0; + } else { /* some of hdr was not sent */ + len -= rc; + iov[0].iov_len -= rc; + iov[0].iov_base += rc; + continue; + } + } + if((iov[0].iov_len == 0) && (rc > 0)){ + iov[1].iov_base += rc; + iov[1].iov_len -= rc; + datalen -= rc; + } } if (rc < 0) { @@ -246,14 +258,15 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, return rc; } - int -CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, - struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op) +SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, + struct smb_hdr *in_buf, int hdrlen, const char * data, + int datalen, int *pbytes_returned, const int long_op) { int rc = 0; - unsigned long timeout = 15 * HZ; - struct mid_q_entry *midQ = NULL; + unsigned int receive_len; + unsigned long timeout; + struct mid_q_entry *midQ; if (ses == NULL) { cERROR(1,("Null smb session")); @@ -263,14 +276,8 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, cERROR(1,("Null tcp session")); return -EIO; } - if(pbytes_returned == NULL) - return -EIO; - else - *pbytes_returned = 0; - - - if(ses->server->tcpStatus == CIFS_EXITING) + if(ses->server->tcpStatus == CifsExiting) return -ENOENT; /* Ensure that we do not send more than 50 overlapping requests @@ -282,7 +289,8 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, } else { spin_lock(&GlobalMid_Lock); while(1) { - if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ + if(atomic_read(&ses->server->inFlight) >= + cifs_max_pending){ spin_unlock(&GlobalMid_Lock); wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) @@ -314,17 +322,17 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, if (ses->server->tcpStatus == CifsExiting) { rc = -ENOENT; - goto cifs_out_label; + goto out_unlock2; } else if (ses->server->tcpStatus == CifsNeedReconnect) { cFYI(1,("tcp session dead - return to caller to retry")); rc = -EAGAIN; - goto cifs_out_label; + goto out_unlock2; } else if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) { rc = -EAGAIN; - goto cifs_out_label; + goto out_unlock2; } /* else ok - we are setting up session */ } midQ = AllocMidQEntry(in_buf, ses); @@ -352,13 +360,12 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, return -EIO; } - /* BB can we sign efficiently in this path? */ - rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); +/* BB FIXME */ +/* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */ midQ->midState = MID_REQUEST_SUBMITTED; -/* rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, - piovec, - (struct sockaddr *) &(ses->server->addr.sockAddr));*/ + rc = smb_send2(ses->server->ssocket, in_buf, hdrlen, data, datalen, + (struct sockaddr *) &(ses->server->addr.sockAddr)); if(rc < 0) { DeleteMidQEntry(midQ); up(&ses->server->tcpSem); @@ -370,19 +377,137 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, return rc; } else up(&ses->server->tcpSem); -cifs_out_label: - if(midQ) - DeleteMidQEntry(midQ); - + if (long_op == -1) + goto cifs_no_response_exit2; + else if (long_op == 2) /* writes past end of file can take loong time */ + timeout = 300 * HZ; + else if (long_op == 1) + timeout = 45 * HZ; /* should be greater than + servers oplock break timeout (about 43 seconds) */ + else if (long_op > 2) { + timeout = MAX_SCHEDULE_TIMEOUT; + } else + timeout = 15 * HZ; + /* wait for 15 seconds or until woken up due to response arriving or + due to last connection to this server being unmounted */ + if (signal_pending(current)) { + /* if signal pending do not hold up user for full smb timeout + but we still give response a change to complete */ + timeout = 2 * HZ; + } + + /* No user interrupts in wait - wreaks havoc with performance */ + if(timeout != MAX_SCHEDULE_TIMEOUT) { + timeout += jiffies; + wait_event(ses->server->response_q, + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || + time_after(jiffies, timeout) || + ((ses->server->tcpStatus != CifsGood) && + (ses->server->tcpStatus != CifsNew))); + } else { + wait_event(ses->server->response_q, + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || + ((ses->server->tcpStatus != CifsGood) && + (ses->server->tcpStatus != CifsNew))); + } + + spin_lock(&GlobalMid_Lock); + if (midQ->resp_buf) { + spin_unlock(&GlobalMid_Lock); + receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); + } else { + cERROR(1,("No response buffer")); + if(midQ->midState == MID_REQUEST_SUBMITTED) { + if(ses->server->tcpStatus == CifsExiting) + rc = -EHOSTDOWN; + else { + ses->server->tcpStatus = CifsNeedReconnect; + midQ->midState = MID_RETRY_NEEDED; + } + } + + if (rc != -EHOSTDOWN) { + if(midQ->midState == MID_RETRY_NEEDED) { + rc = -EAGAIN; + cFYI(1,("marking request for retry")); + } else { + rc = -EIO; + } + } + spin_unlock(&GlobalMid_Lock); + DeleteMidQEntry(midQ); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return rc; + } + + if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { + cERROR(1, ("Frame too large received. Length: %d Xid: %d", + receive_len, xid)); + rc = -EIO; + } else { /* rcvd frame is ok */ + + if (midQ->resp_buf && + (midQ->midState == MID_RESPONSE_RECEIVED)) { + in_buf->smb_buf_length = receive_len; + /* BB verify that length would not overrun small buf */ + memcpy((char *)in_buf + 4, + (char *)midQ->resp_buf + 4, + receive_len); + + dump_smb(in_buf, 80); + /* convert the length into a more usable form */ + if((receive_len > 24) && + (ses->server->secMode & (SECMODE_SIGN_REQUIRED | + SECMODE_SIGN_ENABLED))) { + rc = cifs_verify_signature(in_buf, + ses->server->mac_signing_key, + midQ->sequence_number+1); + if(rc) { + cERROR(1,("Unexpected SMB signature")); + /* BB FIXME add code to kill session */ + } + } + + *pbytes_returned = in_buf->smb_buf_length; + + /* BB special case reconnect tid and uid here? */ + rc = map_smb_to_linux_error(in_buf); + + /* convert ByteCount if necessary */ + if (receive_len >= + sizeof (struct smb_hdr) - + 4 /* do not count RFC1001 header */ + + (2 * in_buf->WordCount) + 2 /* bcc */ ) + BCC(in_buf) = le16_to_cpu(BCC(in_buf)); + } else { + rc = -EIO; + cFYI(1,("Bad MID state? ")); + } + } +cifs_no_response_exit2: + DeleteMidQEntry(midQ); + if(long_op < 3) { - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; -} +out_unlock2: + up(&ses->server->tcpSem); + /* If not lock req, update # of requests on wire to server */ + if(long_op < 3) { + atomic_dec(&ses->server->inFlight); + wake_up(&ses->server->request_q); + } + return rc; +} #endif /* CIFS_EXPERIMENTAL */ int @@ -610,7 +735,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, BCC(out_buf) = le16_to_cpu(BCC(out_buf)); } else { rc = -EIO; - cFYI(1,("Bad MID state? ")); + cERROR(1,("Bad MID state? ")); } } cifs_no_response_exit: |