diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 29 | ||||
-rw-r--r-- | fs/cifs/CHANGES | 9 | ||||
-rw-r--r-- | fs/cifs/TODO | 8 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifspdu.h | 84 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 16 | ||||
-rw-r--r-- | fs/cifs/connect.c | 130 | ||||
-rw-r--r-- | fs/cifs/file.c | 5 | ||||
-rw-r--r-- | fs/cifs/inode.c | 6 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 6 | ||||
-rw-r--r-- | fs/dcache.c | 150 |
12 files changed, 273 insertions, 177 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index 488521ed9e9..a722b5a3f75 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1864,20 +1864,14 @@ config CIFS file servers such as Windows 2000 (including Windows 2003, NT 4 and Windows XP) as well by Samba (which provides excellent CIFS server support for Linux and many other operating systems). Limited - support for Windows ME and similar servers is provided as well. - You must use the smbfs client filesystem to access older SMB servers - such as OS/2 and DOS. + support for OS/2 and Windows ME and similar servers is provided as well. The intent of the cifs module is to provide an advanced - network file system client for mounting to CIFS compliant servers, + network file system client for mounting to CIFS compliant servers, including support for dfs (hierarchical name space), secure per-user session establishment, safe distributed caching (oplock), optional - packet signing, Unicode and other internationalization improvements, - and optional Winbind (nsswitch) integration. You do not need to enable - cifs if running only a (Samba) server. It is possible to enable both - smbfs and cifs (e.g. if you are using CIFS for accessing Windows 2003 - and Samba 3 servers, and smbfs for accessing old servers). If you need - to mount to Samba or Windows from this machine, say Y. + packet signing, Unicode and other internationalization improvements. + If you need to mount to Samba or Windows from this machine, say Y. config CIFS_STATS bool "CIFS statistics" @@ -1970,14 +1964,13 @@ config CIFS_EXPERIMENTAL depends on CIFS && EXPERIMENTAL help Enables cifs features under testing. These features are - experimental and currently include support for writepages - (multipage writebehind performance improvements) and directory - change notification ie fcntl(F_DNOTIFY) as well as some security - improvements. Some also depend on setting at runtime the - pseudo-file /proc/fs/cifs/Experimental (which is disabled by - default). See the file fs/cifs/README for more details. - - If unsure, say N. + experimental and currently include DFS support and directory + change notification ie fcntl(F_DNOTIFY), as well as the upcall + mechanism which will be used for Kerberos session negotiation + and uid remapping. Some of these features also may depend on + setting a value of 1 to the pseudo-file /proc/fs/cifs/Experimental + (which is disabled by default). See the file fs/cifs/README + for more details. If unsure, say N. config CIFS_UPCALL bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)" diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 85e3850bf2c..5fe13593b57 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,8 +1,15 @@ Version 1.47 ------------ Fix oops in list_del during mount caused by unaligned string. +Fix file corruption which could occur on some large file +copies caused by writepages page i/o completion bug. Seek to SEEK_END forces check for update of file size for non-cached -files. +files. Allow file size to be updated on remote extend of locally open, +non-cached file. Fix reconnect to newer Samba servers (or other servers +which support the CIFS Unix/POSIX extensions) so that we again tell the +server the Unix/POSIX cifs capabilities which we support (SetFSInfo). +Add experimental support for new POSIX Open/Mkdir (which returns +stat information on the open, and allows setting the mode). Version 1.46 ------------ diff --git a/fs/cifs/TODO b/fs/cifs/TODO index fc34c74ec4b..68372946dc9 100644 --- a/fs/cifs/TODO +++ b/fs/cifs/TODO @@ -128,3 +128,11 @@ negotiated size) and send larger write sizes to modern servers. 4) More exhaustively test against less common servers. More testing against Windows 9x, Windows ME servers. + +DOS attrs - returned as pseudo-xattr in Samba format (check VFAT and NTFS for this too) + +mount check for unmatched uids - and uid override + +Add mount option for Linux extension disable per mount, and partial disable per mount (uid off, symlink/fifo/mknod on but what about posix acls?) + +Free threads at umount --force that are stuck on the sesSem diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 01ae24af9cf..c97c08eb481 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -100,5 +100,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.47" +#define CIFS_VERSION "1.48" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 068ef51edbf..7d9505491b1 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -1,7 +1,7 @@ /* * fs/cifs/cifspdu.h * - * Copyright (c) International Business Machines Corp., 2002,2005 + * Copyright (c) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -544,7 +544,8 @@ typedef union smb_com_session_setup_andx { /* unsigned char * NativeOS; */ /* unsigned char * NativeLanMan; */ /* unsigned char * PrimaryDomain; */ - } __attribute__((packed)) resp; /* NTLM response with or without extended sec*/ + } __attribute__((packed)) resp; /* NTLM response + (with or without extended sec) */ struct { /* request format */ struct smb_hdr hdr; /* wct = 10 */ @@ -795,6 +796,8 @@ typedef struct smb_com_openx_rsp { __u16 ByteCount; } __attribute__((packed)) OPENX_RSP; +/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */ + /* Legacy write request for older servers */ typedef struct smb_com_writex_req { struct smb_hdr hdr; /* wct = 12 */ @@ -1352,11 +1355,13 @@ struct smb_t2_rsp { #define SMB_QUERY_FILE_UNIX_BASIC 0x200 #define SMB_QUERY_FILE_UNIX_LINK 0x201 #define SMB_QUERY_POSIX_ACL 0x204 -#define SMB_QUERY_XATTR 0x205 +#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ #define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ #define SMB_QUERY_POSIX_PERMISSION 0x207 #define SMB_QUERY_POSIX_LOCK 0x208 -/* #define SMB_POSIX_OPEN 0x209 */ +/* #define SMB_POSIX_OPEN 0x209 */ +/* #define SMB_POSIX_UNLINK 0x20a */ +#define SMB_QUERY_FILE__UNIX_INFO2 0x20b #define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee #define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 #define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ @@ -1377,8 +1382,10 @@ struct smb_t2_rsp { #define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ #define SMB_SET_POSIX_LOCK 0x208 #define SMB_POSIX_OPEN 0x209 +#define SMB_POSIX_UNLINK 0x20a +#define SMB_SET_FILE_UNIX_INFO2 #define SMB_SET_FILE_BASIC_INFO2 0x3ec -#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo level too */ +#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ #define SMB_FILE_ALL_INFO2 0x3fa #define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb #define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc @@ -1428,7 +1435,7 @@ typedef struct smb_com_transaction2_qpi_rsp { struct smb_hdr hdr; /* wct = 10 + SetupCount */ struct trans2_resp t2; __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ + __u16 Reserved2; /* parameter word is present for infolevels > 100 */ } __attribute__((packed)) TRANSACTION2_QPI_RSP; typedef struct smb_com_transaction2_spi_req { @@ -1461,7 +1468,7 @@ typedef struct smb_com_transaction2_spi_rsp { struct smb_hdr hdr; /* wct = 10 + SetupCount */ struct trans2_resp t2; __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ + __u16 Reserved2; /* parameter word is present for infolevels > 100 */ } __attribute__((packed)) TRANSACTION2_SPI_RSP; struct set_file_rename { @@ -1627,6 +1634,7 @@ typedef struct smb_com_transaction2_fnext_rsp_parms { #define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 #define SMB_QUERY_CIFS_UNIX_INFO 0x200 #define SMB_QUERY_POSIX_FS_INFO 0x201 +#define SMB_QUERY_POSIX_WHO_AM_I 0x202 #define SMB_QUERY_LABEL_INFO 0x3ea #define SMB_QUERY_FS_QUOTA_INFO 0x3ee #define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef @@ -1659,9 +1667,21 @@ typedef struct smb_com_transaction_qfsi_rsp { struct smb_hdr hdr; /* wct = 10 + SetupCount */ struct trans2_resp t2; __u16 ByteCount; - __u8 Pad; /* may be three bytes *//* followed by data area */ + __u8 Pad; /* may be three bytes? *//* followed by data area */ } __attribute__((packed)) TRANSACTION2_QFSI_RSP; +typedef struct whoami_rsp_data { /* Query level 0x202 */ + __u32 flags; /* 0 = Authenticated user 1 = GUEST */ + __u32 mask; /* which flags bits server understands ie 0x0001 */ + __u64 unix_user_id; + __u64 unix_user_gid; + __u32 number_of_supplementary_gids; /* may be zero */ + __u32 number_of_sids; /* may be zero */ + __u32 length_of_sid_array; /* in bytes - may be zero */ + __u32 pad; /* reserved - MBZ */ + /* __u64 gid_array[0]; */ /* may be empty */ + /* __u8 * psid_list */ /* may be empty */ +} __attribute__((packed)) WHOAMI_RSP_DATA; /* SETFSInfo Levels */ #define SMB_SET_CIFS_UNIX_INFO 0x200 @@ -1858,8 +1878,11 @@ typedef struct { #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 /* Allow POSIX path chars */ +#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based + calls including posix open + and posix unlink */ #ifdef CONFIG_CIFS_POSIX -#define CIFS_UNIX_CAP_MASK 0x0000001b +#define CIFS_UNIX_CAP_MASK 0x0000003b #else #define CIFS_UNIX_CAP_MASK 0x00000013 #endif /* CONFIG_CIFS_POSIX */ @@ -1946,7 +1969,7 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */ __le32 AlignmentRequirement; __le32 FileNameLength; char FileName[1]; -} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */ +} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */ /* defines for enumerating possible values of the Unix type field below */ #define UNIX_FILE 0 @@ -1970,11 +1993,11 @@ typedef struct { __u64 UniqueId; __le64 Permissions; __le64 Nlinks; -} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ +} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ typedef struct { char LinkDest[1]; -} __attribute__((packed)) FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */ +} __attribute__((packed)) FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */ /* The following three structures are needed only for setting time to NT4 and some older servers via @@ -2011,7 +2034,7 @@ typedef struct { __le64 ChangeTime; __le32 Attributes; __u32 Pad; -} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */ +} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */ struct file_allocation_info { __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ @@ -2020,7 +2043,7 @@ struct file_allocation_info { struct file_end_of_file_info { __le64 FileSize; /* offset to end of file */ -} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */ +} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */ struct file_alt_name_info { __u8 alt_name[1]; @@ -2075,6 +2098,19 @@ struct cifs_posix_acl { /* access conrol list (ACL) */ /* end of POSIX ACL definitions */ +typedef struct { + __u32 OpenFlags; /* same as NT CreateX */ + __u32 PosixOpenFlags; + __u32 Mode; + __u16 Level; /* reply level requested (see QPathInfo levels) */ + __u16 Pad; /* reserved - MBZ */ +} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */ + +typedef struct { + /* reply varies based on requested level */ +} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ + + struct file_internal_info { __u64 UniqueId; /* inode number */ } __attribute__((packed)); /* level 0x3ee */ @@ -2238,7 +2274,8 @@ struct data_blob { 1) PosixCreateX - to set and return the mode, inode#, device info and 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 + 2) Close - to return the last write time to do cache across close + more safely 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 @@ -2273,7 +2310,8 @@ struct data_blob { TRANSACTION2 (18 cases) SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2 (BB verify that never need to set allocation size) - SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via Unix ext?) + SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via + Unix ext?) COPY (note support for copy across directories) - FUTURE, OPTIONAL setting/getting OS/2 EAs - FUTURE (BB can this handle @@ -2293,13 +2331,13 @@ struct data_blob { T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) - BB check for missing inode fields Actually need QUERY_FILE_UNIX_INFO since has inode num BB what about a) blksize/blkbits/blocks - b) i_version - c) i_rdev - d) notify mask? - e) generation - f) size_seqcount + b) i_version + c) i_rdev + d) notify mask? + e) generation + f) size_seqcount T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX - TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended + TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL @@ -2338,7 +2376,7 @@ typedef struct file_xattr_info { __u32 xattr_value_len; char xattr_name[0]; /* followed by xattr_value[xattr_value_len], no pad */ -} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute, info level 0x205 */ +} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute, info level 0x205 */ /* flags for chattr command */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index f1f8225102f..6148b82170c 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -23,6 +23,7 @@ #include <linux/nls.h> struct statfs; +struct smb_vol; /* ***************************************************************** @@ -57,7 +58,7 @@ extern int SendReceiveBlockingLock(const unsigned int /* xid */ , int * /* bytes returned */); extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); -extern int is_size_safe_to_change(struct cifsInodeInfo *); +extern int is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); @@ -147,6 +148,8 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, unsigned int *pnum_referrals, unsigned char ** preferrals, int remap); +extern void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, + struct super_block * sb, struct smb_vol * vol); extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 472e33e0f3c..b8e91470c27 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -158,9 +158,15 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, nls_codepage); if(!rc && (tcon->tidStatus == CifsNeedReconnect)) { mark_open_files_invalid(tcon); - rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon - , nls_codepage); + rc = CIFSTCon(0, tcon->ses, tcon->treeName, + tcon, nls_codepage); up(&tcon->ses->sesSem); + /* tell server which Unix caps we support */ + if (tcon->ses->capabilities & CAP_UNIX) + reset_cifs_unix_caps(0 /* no xid */, + tcon, + NULL /* we do not know sb */, + NULL /* no vol info */); /* BB FIXME add code to check if wsize needs update due to negotiated smb buffer size shrinking */ @@ -298,6 +304,12 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); up(&tcon->ses->sesSem); + /* tell server which Unix caps we support */ + if (tcon->ses->capabilities & CAP_UNIX) + reset_cifs_unix_caps(0 /* no xid */, + tcon, + NULL /* do not know sb */, + NULL /* no vol info */); /* BB FIXME add code to check if wsize needs update due to negotiated smb buffer size shrinking */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2caca06b4ba..20ba7dcc995 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1613,6 +1613,76 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket) return rc; } +void reset_cifs_unix_caps(int xid, struct cifsTconInfo * tcon, + struct super_block * sb, struct smb_vol * vol_info) +{ + /* if we are reconnecting then should we check to see if + * any requested capabilities changed locally e.g. via + * remount but we can not do much about it here + * if they have (even if we could detect it by the following) + * Perhaps we could add a backpointer to array of sb from tcon + * or if we change to make all sb to same share the same + * sb as NFS - then we only have one backpointer to sb. + * What if we wanted to mount the server share twice once with + * and once without posixacls or posix paths? */ + __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + + + if(!CIFSSMBQFSUnixInfo(xid, tcon)) { + __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + + /* check for reconnect case in which we do not + want to change the mount behavior if we can avoid it */ + if(vol_info == NULL) { + /* turn off POSIX ACL and PATHNAMES if not set + originally at mount time */ + if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + + + + + } + + cap &= CIFS_UNIX_CAP_MASK; + if(vol_info && vol_info->no_psx_acl) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + else if(CIFS_UNIX_POSIX_ACL_CAP & cap) { + cFYI(1,("negotiated posix acl support")); + if(sb) + sb->s_flags |= MS_POSIXACL; + } + + if(vol_info && vol_info->posix_paths == 0) + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + else if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + cFYI(1,("negotiate posix pathnames")); + if(sb) + CIFS_SB(sb)->mnt_cifs_flags |= + CIFS_MOUNT_POSIX_PATHS; + } + + cFYI(1,("Negotiate caps 0x%x",(int)cap)); +#ifdef CONFIG_CIFS_DEBUG2 + if(cap & CIFS_UNIX_FCNTL_CAP) + cFYI(1,("FCNTL cap")); + if(cap & CIFS_UNIX_EXTATTR_CAP) + cFYI(1,("EXTATTR cap")); + if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cFYI(1,("POSIX path cap")); + if(cap & CIFS_UNIX_XATTR_CAP) + cFYI(1,("XATTR cap")); + if(cap & CIFS_UNIX_POSIX_ACL_CAP) + cFYI(1,("POSIX ACL cap")); +#endif /* CIFS_DEBUG2 */ + if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { + cFYI(1,("setting capabilities failed")); + } + } +} + int cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, char *mount_data, const char *devname) @@ -1928,20 +1998,25 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (tcon == NULL) rc = -ENOMEM; else { - /* check for null share name ie connect to dfs root */ + /* check for null share name ie connecting to + * dfs root */ - /* BB check if this works for exactly length three strings */ + /* BB check if this works for exactly length + * three strings */ if ((strchr(volume_info.UNC + 3, '\\') == NULL) && (strchr(volume_info.UNC + 3, '/') == NULL)) { rc = connect_to_dfs_path(xid, pSesInfo, - "", cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + "", cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); kfree(volume_info.UNC); FreeXid(xid); return -ENODEV; } else { + /* BB Do we need to wrap sesSem around + * this TCon call and Unix SetFS as + * we do on SessSetup and reconnect? */ rc = CIFSTCon(xid, pSesInfo, volume_info.UNC, tcon, cifs_sb->local_nls); @@ -1962,6 +2037,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, sb->s_maxbytes = (u64) 1 << 31; /* 2 GB */ } + /* BB FIXME fix time_gran to be larger for LANMAN sessions */ sb->s_time_gran = 100; /* on error free sesinfo and tcon struct if needed */ @@ -2006,45 +2082,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* do not care if following two calls succeed - informational */ CIFSSMBQFSDeviceInfo(xid, tcon); CIFSSMBQFSAttributeInfo(xid, tcon); - - if (tcon->ses->capabilities & CAP_UNIX) { - if(!CIFSSMBQFSUnixInfo(xid, tcon)) { - __u64 cap = - le64_to_cpu(tcon->fsUnixInfo.Capability); - cap &= CIFS_UNIX_CAP_MASK; - if(volume_info.no_psx_acl) - cap &= ~CIFS_UNIX_POSIX_ACL_CAP; - else if(CIFS_UNIX_POSIX_ACL_CAP & cap) { - cFYI(1,("negotiated posix acl support")); - sb->s_flags |= MS_POSIXACL; - } - - if(volume_info.posix_paths == 0) - cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - else if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { - cFYI(1,("negotiate posix pathnames")); - cifs_sb->mnt_cifs_flags |= - CIFS_MOUNT_POSIX_PATHS; - } - - cFYI(1,("Negotiate caps 0x%x",(int)cap)); -#ifdef CONFIG_CIFS_DEBUG2 - if(cap & CIFS_UNIX_FCNTL_CAP) - cFYI(1,("FCNTL cap")); - if(cap & CIFS_UNIX_EXTATTR_CAP) - cFYI(1,("EXTATTR cap")); - if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) - cFYI(1,("POSIX path cap")); - if(cap & CIFS_UNIX_XATTR_CAP) - cFYI(1,("XATTR cap")); - if(cap & CIFS_UNIX_POSIX_ACL_CAP) - cFYI(1,("POSIX ACL cap")); -#endif /* CIFS_DEBUG2 */ - if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { - cFYI(1,("setting capabilities failed")); - } - } - } + + /* tell server which Unix caps we support */ + if (tcon->ses->capabilities & CAP_UNIX) + reset_cifs_unix_caps(xid, tcon, sb, &volume_info); + if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) cifs_sb->wsize = min(cifs_sb->wsize, (tcon->ses->server->maxBuf - diff --git a/fs/cifs/file.c b/fs/cifs/file.c index e9dcf5ee29a..07ff9351e9e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1954,7 +1954,7 @@ static int cifs_readpage(struct file *file, struct page *page) refreshing the inode only on increases in the file size but this is tricky to do without racing with writebehind page caching in the current Linux kernel design */ -int is_size_safe_to_change(struct cifsInodeInfo *cifsInode) +int is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) { struct cifsFileInfo *open_file = NULL; @@ -1976,6 +1976,9 @@ int is_size_safe_to_change(struct cifsInodeInfo *cifsInode) return 1; } + if(i_size_read(&cifsInode->vfs_inode) < end_of_file) + return 1; + return 0; } else return 1; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index c4fa91b8b62..3f5bc83dc3d 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -140,7 +140,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, inode->i_gid = le64_to_cpu(findData.Gid); inode->i_nlink = le64_to_cpu(findData.Nlinks); - if (is_size_safe_to_change(cifsInfo)) { + if (is_size_safe_to_change(cifsInfo, end_of_file)) { /* can not safely change the file size here if the client is writing to it due to potential races */ @@ -491,8 +491,8 @@ int cifs_get_inode_info(struct inode **pinode, /* BB add code here - validate if device or weird share or device type? */ } - if (is_size_safe_to_change(cifsInfo)) { - /* can not safely change the file size here if the + if (is_size_safe_to_change(cifsInfo, le64_to_cpu(pfindData->EndOfFile))) { + /* can not safely shrink the file size here if the client is writing to it due to potential races */ i_size_write(inode,le64_to_cpu(pfindData->EndOfFile)); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 782940be550..c6220bd2716 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -222,7 +222,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, atomic_set(&cifsInfo->inUse, 1); } - if (is_size_safe_to_change(cifsInfo)) { + if (is_size_safe_to_change(cifsInfo, end_of_file)) { /* can not safely change the file size here if the client is writing to it due to potential races */ i_size_write(tmp_inode, end_of_file); @@ -351,10 +351,10 @@ static void unix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_gid = le64_to_cpu(pfindData->Gid); tmp_inode->i_nlink = le64_to_cpu(pfindData->Nlinks); - if (is_size_safe_to_change(cifsInfo)) { + if (is_size_safe_to_change(cifsInfo, end_of_file)) { /* can not safely change the file size here if the client is writing to it due to potential races */ - i_size_write(tmp_inode,end_of_file); + i_size_write(tmp_inode, end_of_file); /* 512 bytes (2**9) is the fake blocksize that must be used */ /* for this calculation, not the real blocksize */ diff --git a/fs/dcache.c b/fs/dcache.c index b5f61393291..d68631f18df 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1739,41 +1739,45 @@ shouldnt_be_hashed: * @rootmnt: vfsmnt to which the root dentry belongs * @buffer: buffer to return value in * @buflen: buffer length - * @fail_deleted: what to return for deleted files * - * Convert a dentry into an ASCII path name. If the entry has been deleted, - * then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, - * the the string " (deleted)" is appended. Note that this is ambiguous. + * Convert a dentry into an ASCII path name. If the entry has been deleted + * the string " (deleted)" is appended. Note that this is ambiguous. * - * Returns the buffer or an error code. + * Returns the buffer or an error code if the path was too long. + * + * "buflen" should be positive. Caller holds the dcache_lock. */ -static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, - struct dentry *root, struct vfsmount *rootmnt, - char *buffer, int buflen, int fail_deleted) +static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt, + struct dentry *root, struct vfsmount *rootmnt, + char *buffer, int buflen) { - int namelen, is_slash; - - if (buflen < 2) - return ERR_PTR(-ENAMETOOLONG); - buffer += --buflen; - *buffer = '\0'; + char * end = buffer+buflen; + char * retval; + int namelen; - spin_lock(&dcache_lock); + *--end = '\0'; + buflen--; if (!IS_ROOT(dentry) && d_unhashed(dentry)) { - if (fail_deleted) { - buffer = ERR_PTR(-ENOENT); - goto out; - } - if (buflen < 10) - goto Elong; buflen -= 10; - buffer -= 10; - memcpy(buffer, " (deleted)", 10); + end -= 10; + if (buflen < 0) + goto Elong; + memcpy(end, " (deleted)", 10); } - while (dentry != root || vfsmnt != rootmnt) { + + if (buflen < 1) + goto Elong; + /* Get '/' right */ + retval = end-1; + *retval = '/'; + + for (;;) { struct dentry * parent; + if (dentry == root && vfsmnt == rootmnt) + break; if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + /* Global root? */ spin_lock(&vfsmount_lock); if (vfsmnt->mnt_parent == vfsmnt) { spin_unlock(&vfsmount_lock); @@ -1787,60 +1791,33 @@ static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, parent = dentry->d_parent; prefetch(parent); namelen = dentry->d_name.len; - if (buflen <= namelen) - goto Elong; buflen -= namelen + 1; - buffer -= namelen; - memcpy(buffer, dentry->d_name.name, namelen); - *--buffer = '/'; + if (buflen < 0) + goto Elong; + end -= namelen; + memcpy(end, dentry->d_name.name, namelen); + *--end = '/'; + retval = end; dentry = parent; } - /* Get '/' right */ - if (*buffer != '/') - *--buffer = '/'; -out: - spin_unlock(&dcache_lock); - return buffer; + return retval; global_root: - /* - * We went past the (vfsmount, dentry) we were looking for and have - * either hit a root dentry, a lazily unmounted dentry, an - * unconnected dentry, or the file is on a pseudo filesystem. - */ namelen = dentry->d_name.len; - is_slash = (namelen == 1 && *dentry->d_name.name == '/'); - if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) { - /* - * Make sure we won't return a pathname starting with '/'. - * - * Historically, we also glue together the root dentry and - * remaining name for pseudo filesystems like pipefs, which - * have the MS_NOUSER flag set. This results in pathnames - * like "pipe:[439336]". - */ - if (*buffer == '/') { - buffer++; - buflen++; - } - if (is_slash) - goto out; - } - if (buflen < namelen) + buflen -= namelen; + if (buflen < 0) goto Elong; - buffer -= namelen; - memcpy(buffer, dentry->d_name.name, namelen); - goto out; - + retval -= namelen-1; /* hit the slash */ + memcpy(retval, dentry->d_name.name, namelen); + return retval; Elong: - buffer = ERR_PTR(-ENAMETOOLONG); - goto out; + return ERR_PTR(-ENAMETOOLONG); } /* write full pathname into buffer and return start of pathname */ -char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, - int buflen) +char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt, + char *buf, int buflen) { char *res; struct vfsmount *rootmnt; @@ -1850,7 +1827,9 @@ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, rootmnt = mntget(current->fs->rootmnt); root = dget(current->fs->root); read_unlock(¤t->fs->lock); - res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0); + spin_lock(&dcache_lock); + res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen); + spin_unlock(&dcache_lock); dput(root); mntput(rootmnt); return res; @@ -1876,10 +1855,10 @@ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, */ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) { - int error, len; + int error; struct vfsmount *pwdmnt, *rootmnt; struct dentry *pwd, *root; - char *page = (char *) __get_free_page(GFP_USER), *cwd; + char *page = (char *) __get_free_page(GFP_USER); if (!page) return -ENOMEM; @@ -1891,18 +1870,29 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) root = dget(current->fs->root); read_unlock(¤t->fs->lock); - cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1); - error = PTR_ERR(cwd); - if (IS_ERR(cwd)) - goto out; + error = -ENOENT; + /* Has the current directory has been unlinked? */ + spin_lock(&dcache_lock); + if (pwd->d_parent == pwd || !d_unhashed(pwd)) { + unsigned long len; + char * cwd; - error = -ERANGE; - len = PAGE_SIZE + page - cwd; - if (len <= size) { - error = len; - if (copy_to_user(buf, cwd, len)) - error = -EFAULT; - } + cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE); + spin_unlock(&dcache_lock); + + error = PTR_ERR(cwd); + if (IS_ERR(cwd)) + goto out; + + error = -ERANGE; + len = PAGE_SIZE + page - cwd; + if (len <= size) { + error = len; + if (copy_to_user(buf, cwd, len)) + error = -EFAULT; + } + } else + spin_unlock(&dcache_lock); out: dput(pwd); |