Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
cifs: when ATTR_READONLY is set, only clear write bits on non-directories
cifs: remove cifsInodeInfo->inUse counter
cifs: convert cifs_get_inode_info and non-posix readdir to use cifs_iget
[CIFS] update cifs version number
cifs: add and use CIFSSMBUnixSetFileInfo for setattr calls
cifs: make a separate function for filling out FILE_UNIX_BASIC_INFO
cifs: rename CIFSSMBUnixSetInfo to CIFSSMBUnixSetPathInfo
cifs: add pid of initiating process to spnego upcall info
cifs: fix regression with O_EXCL creates and optimize away lookup
cifs: add new cifs_iget function and convert unix codepath to use it
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index 3a9b7a5..92888aa 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -5,7 +5,11 @@
on by default if server supports it). Add forceuid and forcegid
mount options (so that when negotiating unix extensions specifying
which uid mounted does not immediately force the server's reported
-uids to be overridden). Add support for scope moutn parm.
+uids to be overridden). Add support for scope mount parm. Improve
+hard link detection to use same inode for both. Do not set
+read-only dos attribute on directories (for chmod) since Windows
+explorer special cases this attribute bit for directories for
+a different purpose.
Version 1.58
------------
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index 4a4581c..051caec 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -86,6 +86,9 @@
/* strlen of ";user=" */
#define USER_KEY_LEN 6
+/* strlen of ";pid=0x" */
+#define PID_KEY_LEN 7
+
/* get a key struct with a SPNEGO security blob, suitable for session setup */
struct key *
cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
@@ -103,7 +106,8 @@
IP_KEY_LEN + INET6_ADDRSTRLEN +
MAX_MECH_STR_LEN +
UID_KEY_LEN + (sizeof(uid_t) * 2) +
- USER_KEY_LEN + strlen(sesInfo->userName) + 1;
+ USER_KEY_LEN + strlen(sesInfo->userName) +
+ PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
spnego_key = ERR_PTR(-ENOMEM);
description = kzalloc(desc_len, GFP_KERNEL);
@@ -141,6 +145,9 @@
dp = description + strlen(description);
sprintf(dp, ";user=%s", sesInfo->userName);
+ dp = description + strlen(description);
+ sprintf(dp, ";pid=0x%x", current->pid);
+
cFYI(1, ("key description = %s", description));
spnego_key = request_key(&cifs_spnego_key_type, description, "");
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 1403b5d..6941c22 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -327,7 +327,7 @@
static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
- struct inode *inode)
+ struct cifs_fattr *fattr)
{
int i;
int num_aces = 0;
@@ -340,7 +340,7 @@
if (!pdacl) {
/* no DACL in the security descriptor, set
all the permissions for user/group/other */
- inode->i_mode |= S_IRWXUGO;
+ fattr->cf_mode |= S_IRWXUGO;
return;
}
@@ -357,7 +357,7 @@
/* reset rwx permissions for user/group/other.
Also, if num_aces is 0 i.e. DACL has no ACEs,
user/group/other have no permissions */
- inode->i_mode &= ~(S_IRWXUGO);
+ fattr->cf_mode &= ~(S_IRWXUGO);
acl_base = (char *)pdacl;
acl_size = sizeof(struct cifs_acl);
@@ -379,17 +379,17 @@
if (compare_sids(&(ppace[i]->sid), pownersid))
access_flags_to_mode(ppace[i]->access_req,
ppace[i]->type,
- &(inode->i_mode),
+ &fattr->cf_mode,
&user_mask);
if (compare_sids(&(ppace[i]->sid), pgrpsid))
access_flags_to_mode(ppace[i]->access_req,
ppace[i]->type,
- &(inode->i_mode),
+ &fattr->cf_mode,
&group_mask);
if (compare_sids(&(ppace[i]->sid), &sid_everyone))
access_flags_to_mode(ppace[i]->access_req,
ppace[i]->type,
- &(inode->i_mode),
+ &fattr->cf_mode,
&other_mask);
/* memcpy((void *)(&(cifscred->aces[i])),
@@ -464,7 +464,7 @@
/* Convert CIFS ACL to POSIX form */
static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
- struct inode *inode)
+ struct cifs_fattr *fattr)
{
int rc;
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
@@ -472,7 +472,7 @@
char *end_of_acl = ((char *)pntsd) + acl_len;
__u32 dacloffset;
- if ((inode == NULL) || (pntsd == NULL))
+ if (pntsd == NULL)
return -EIO;
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
@@ -497,7 +497,7 @@
if (dacloffset)
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
- group_sid_ptr, inode);
+ group_sid_ptr, fattr);
else
cFYI(1, ("no ACL")); /* BB grant all or default perms? */
@@ -508,7 +508,6 @@
memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
sizeof(struct cifs_sid)); */
-
return 0;
}
@@ -671,8 +670,9 @@
}
/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
-void acl_to_uid_mode(struct cifs_sb_info *cifs_sb, struct inode *inode,
- const char *path, const __u16 *pfid)
+void
+cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
+ struct inode *inode, const char *path, const __u16 *pfid)
{
struct cifs_ntsd *pntsd = NULL;
u32 acllen = 0;
@@ -687,7 +687,7 @@
/* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
if (pntsd)
- rc = parse_sec_desc(pntsd, acllen, inode);
+ rc = parse_sec_desc(pntsd, acllen, fattr);
if (rc)
cFYI(1, ("parse sec desc failed rc = %d", rc));
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 9f669f9..44f3050 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -308,7 +308,6 @@
if (!cifs_inode)
return NULL;
cifs_inode->cifsAttrs = 0x20; /* default */
- atomic_set(&cifs_inode->inUse, 0);
cifs_inode->time = 0;
cifs_inode->write_behind_rc = 0;
/* Until the file is open and we have gotten oplock
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 9570a0e..6c17094 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -24,6 +24,19 @@
#define ROOT_I 2
+/*
+ * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down
+ * so that it will fit.
+ */
+static inline ino_t
+cifs_uniqueid_to_ino_t(u64 fileid)
+{
+ ino_t ino = (ino_t) fileid;
+ if (sizeof(ino_t) < sizeof(u64))
+ ino ^= fileid >> (sizeof(u64)-sizeof(ino_t)) * 8;
+ return ino;
+}
+
extern struct file_system_type cifs_fs_type;
extern const struct address_space_operations cifs_addr_ops;
extern const struct address_space_operations cifs_addr_ops_smallbuf;
@@ -100,5 +113,5 @@
extern const struct export_operations cifs_export_ops;
#endif /* EXPERIMENTAL */
-#define CIFS_VERSION "1.59"
+#define CIFS_VERSION "1.60"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index e1225e6..63f6cdf 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -364,13 +364,13 @@
struct list_head openFileList;
int write_behind_rc;
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
- atomic_t inUse; /* num concurrent users (local openers cifs) of file*/
unsigned long time; /* jiffies of last update/check of inode */
bool clientCanCacheRead:1; /* read oplock */
bool clientCanCacheAll:1; /* read and writebehind oplock */
bool oplockPending:1;
bool delete_pending:1; /* DELETE_ON_CLOSE is set */
u64 server_eof; /* current file size on server */
+ u64 uniqueid; /* server inode number */
struct inode vfs_inode;
};
@@ -472,6 +472,32 @@
char *node_name;
};
+/*
+ * common struct for holding inode info when searching for or updating an
+ * inode with new info
+ */
+
+#define CIFS_FATTR_DFS_REFERRAL 0x1
+#define CIFS_FATTR_DELETE_PENDING 0x2
+#define CIFS_FATTR_NEED_REVAL 0x4
+
+struct cifs_fattr {
+ u32 cf_flags;
+ u32 cf_cifsattrs;
+ u64 cf_uniqueid;
+ u64 cf_eof;
+ u64 cf_bytes;
+ uid_t cf_uid;
+ gid_t cf_gid;
+ umode_t cf_mode;
+ dev_t cf_rdev;
+ unsigned int cf_nlink;
+ unsigned int cf_dtype;
+ struct timespec cf_atime;
+ struct timespec cf_mtime;
+ struct timespec cf_ctime;
+};
+
static inline void free_dfs_info_param(struct dfs_info3_param *param)
{
if (param) {
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index a785f69..2d07f89 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -2328,19 +2328,7 @@
typedef struct {
__le32 NextEntryOffset;
__u32 ResumeKey; /* as with FileIndex - no need to convert */
- __le64 EndOfFile;
- __le64 NumOfBytes;
- __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */
- __le64 LastAccessTime;
- __le64 LastModificationTime;
- __le64 Uid;
- __le64 Gid;
- __le32 Type;
- __le64 DevMajor;
- __le64 DevMinor;
- __le64 UniqueId;
- __le64 Permissions;
- __le64 Nlinks;
+ FILE_UNIX_BASIC_INFO basic;
char FileName[1];
} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index c419416..da8fbf5 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -98,9 +98,13 @@
extern int cifs_posix_open(char *full_path, struct inode **pinode,
struct super_block *sb, int mode, int oflags,
int *poplock, __u16 *pnetfid, int xid);
-extern void posix_fill_in_inode(struct inode *tmp_inode,
- FILE_UNIX_BASIC_INFO *pData, int isNewInode);
-extern struct inode *cifs_new_inode(struct super_block *sb, __u64 *inum);
+extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
+ FILE_UNIX_BASIC_INFO *info,
+ struct cifs_sb_info *cifs_sb);
+extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
+extern struct inode *cifs_iget(struct super_block *sb,
+ struct cifs_fattr *fattr);
+
extern int cifs_get_inode_info(struct inode **pinode,
const unsigned char *search_path,
FILE_ALL_INFO *pfile_info,
@@ -108,8 +112,9 @@
extern int cifs_get_inode_info_unix(struct inode **pinode,
const unsigned char *search_path,
struct super_block *sb, int xid);
-extern void acl_to_uid_mode(struct cifs_sb_info *cifs_sb, struct inode *inode,
- const char *path, const __u16 *pfid);
+extern void cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
+ struct cifs_fattr *fattr, struct inode *inode,
+ const char *path, const __u16 *pfid);
extern int mode_to_acl(struct inode *inode, const char *path, __u64);
extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
@@ -215,7 +220,11 @@
dev_t device;
};
-extern int CIFSSMBUnixSetInfo(const int xid, struct cifsTconInfo *pTcon,
+extern int CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,
+ const struct cifs_unix_set_info_args *args,
+ u16 fid, u32 pid_of_opener);
+
+extern int CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *pTcon,
char *fileName,
const struct cifs_unix_set_info_args *args,
const struct nls_table *nls_codepage,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 61007c6..922f5fe 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -5074,10 +5074,114 @@
}
#endif /* temporarily unneeded SetAttr legacy function */
+static void
+cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
+ const struct cifs_unix_set_info_args *args)
+{
+ u64 mode = args->mode;
+
+ /*
+ * Samba server ignores set of file size to zero due to bugs in some
+ * older clients, but we should be precise - we use SetFileSize to
+ * set file size and do not want to truncate file size to zero
+ * accidently as happened on one Samba server beta by putting
+ * zero instead of -1 here
+ */
+ data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
+ data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
+ data_offset->LastStatusChange = cpu_to_le64(args->ctime);
+ data_offset->LastAccessTime = cpu_to_le64(args->atime);
+ data_offset->LastModificationTime = cpu_to_le64(args->mtime);
+ data_offset->Uid = cpu_to_le64(args->uid);
+ data_offset->Gid = cpu_to_le64(args->gid);
+ /* better to leave device as zero when it is */
+ data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
+ data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
+ data_offset->Permissions = cpu_to_le64(mode);
+
+ if (S_ISREG(mode))
+ data_offset->Type = cpu_to_le32(UNIX_FILE);
+ else if (S_ISDIR(mode))
+ data_offset->Type = cpu_to_le32(UNIX_DIR);
+ else if (S_ISLNK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
+ else if (S_ISCHR(mode))
+ data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
+ else if (S_ISBLK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
+ else if (S_ISFIFO(mode))
+ data_offset->Type = cpu_to_le32(UNIX_FIFO);
+ else if (S_ISSOCK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_SOCKET);
+}
+
int
-CIFSSMBUnixSetInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
- const struct cifs_unix_set_info_args *args,
- const struct nls_table *nls_codepage, int remap)
+CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,
+ const struct cifs_unix_set_info_args *args,
+ u16 fid, u32 pid_of_opener)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ FILE_UNIX_BASIC_INFO *data_offset;
+ int rc = 0;
+ u16 params, param_offset, offset, byte_count, count;
+
+ cFYI(1, ("Set Unix Info (via SetFileInfo)"));
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (FILE_UNIX_BASIC_INFO *)
+ ((char *)(&pSMB->hdr.Protocol) + offset);
+ count = sizeof(FILE_UNIX_BASIC_INFO);
+
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ 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);
+ pSMB->Fid = fid;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ cifs_fill_unix_set_info(data_offset, args);
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+ if (rc)
+ cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc));
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+int
+CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
+ const struct cifs_unix_set_info_args *args,
+ const struct nls_table *nls_codepage, int remap)
{
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL;
@@ -5086,7 +5190,6 @@
int bytes_returned = 0;
FILE_UNIX_BASIC_INFO *data_offset;
__u16 params, param_offset, offset, count, byte_count;
- __u64 mode = args->mode;
cFYI(1, ("In SetUID/GID/Mode"));
setPermsRetry:
@@ -5137,38 +5240,8 @@
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
pSMB->Reserved4 = 0;
pSMB->hdr.smb_buf_length += byte_count;
- /* Samba server ignores set of file size to zero due to bugs in some
- older clients, but we should be precise - we use SetFileSize to
- set file size and do not want to truncate file size to zero
- accidently as happened on one Samba server beta by putting
- zero instead of -1 here */
- data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
- data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
- data_offset->LastStatusChange = cpu_to_le64(args->ctime);
- data_offset->LastAccessTime = cpu_to_le64(args->atime);
- data_offset->LastModificationTime = cpu_to_le64(args->mtime);
- data_offset->Uid = cpu_to_le64(args->uid);
- data_offset->Gid = cpu_to_le64(args->gid);
- /* better to leave device as zero when it is */
- data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
- data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
- data_offset->Permissions = cpu_to_le64(mode);
- if (S_ISREG(mode))
- data_offset->Type = cpu_to_le32(UNIX_FILE);
- else if (S_ISDIR(mode))
- data_offset->Type = cpu_to_le32(UNIX_DIR);
- else if (S_ISLNK(mode))
- data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
- else if (S_ISCHR(mode))
- data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
- else if (S_ISBLK(mode))
- data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
- else if (S_ISFIFO(mode))
- data_offset->Type = cpu_to_le32(UNIX_FIFO);
- else if (S_ISSOCK(mode))
- data_offset->Type = cpu_to_le32(UNIX_SOCKET);
-
+ cifs_fill_unix_set_info(data_offset, args);
pSMB->ByteCount = cpu_to_le16(byte_count);
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 7dc6b74..4326ffd 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -188,6 +188,7 @@
FILE_UNIX_BASIC_INFO *presp_data;
__u32 posix_flags = 0;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_fattr fattr;
cFYI(1, ("posix open %s", full_path));
@@ -236,22 +237,21 @@
if (presp_data->Type == cpu_to_le32(-1))
goto posix_open_ret; /* open ok, caller does qpathinfo */
- /* get new inode and set it up */
if (!pinode)
goto posix_open_ret; /* caller does not need info */
+ cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb);
+
+ /* get new inode and set it up */
if (*pinode == NULL) {
- __u64 unique_id = le64_to_cpu(presp_data->UniqueId);
- *pinode = cifs_new_inode(sb, &unique_id);
+ *pinode = cifs_iget(sb, &fattr);
+ if (!*pinode) {
+ rc = -ENOMEM;
+ goto posix_open_ret;
+ }
+ } else {
+ cifs_fattr_to_inode(*pinode, &fattr);
}
- /* else an inode was passed in. Update its info, don't create one */
-
- /* We do not need to close the file if new_inode fails since
- the caller will retry qpathinfo as long as inode is null */
- if (*pinode == NULL)
- goto posix_open_ret;
-
- posix_fill_in_inode(*pinode, presp_data, 1);
cifs_fill_fileinfo(*pinode, *pnetfid, cifs_sb->tcon, write_only);
@@ -425,9 +425,10 @@
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
}
- CIFSSMBUnixSetInfo(xid, tcon, full_path, &args,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
} else {
/* BB implement mode setting via Windows security
descriptors e.g. */
@@ -515,10 +516,10 @@
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
}
- rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path,
- &args, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
if (!rc) {
rc = cifs_get_inode_info_unix(&newinode, full_path,
@@ -643,6 +644,15 @@
}
}
+ /*
+ * O_EXCL: optimize away the lookup, but don't hash the dentry. Let
+ * the VFS handle the create.
+ */
+ if (nd->flags & LOOKUP_EXCL) {
+ d_instantiate(direntry, NULL);
+ return 0;
+ }
+
/* can not grab the rename sem here since it would
deadlock in the cases (beginning of sys_rename itself)
in which we already have the sb rename sem */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 97ce4bf..c34b7f8 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -448,9 +448,9 @@
.mtime = NO_CHANGE_64,
.device = 0,
};
- CIFSSMBUnixSetInfo(xid, tcon, full_path, &args,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
+ CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
}
}
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 155c9e7..18afe57 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -77,239 +77,202 @@
}
}
-static void cifs_unix_info_to_inode(struct inode *inode,
- FILE_UNIX_BASIC_INFO *info, int force_uid_gid)
+/* populate an inode with info from a cifs_fattr struct */
+void
+cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
{
+ struct cifsInodeInfo *cifs_i = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
- __u64 num_of_bytes = le64_to_cpu(info->NumOfBytes);
- __u64 end_of_file = le64_to_cpu(info->EndOfFile);
+ unsigned long oldtime = cifs_i->time;
- inode->i_atime = cifs_NTtimeToUnix(info->LastAccessTime);
- inode->i_mtime =
- cifs_NTtimeToUnix(info->LastModificationTime);
- inode->i_ctime = cifs_NTtimeToUnix(info->LastStatusChange);
- inode->i_mode = le64_to_cpu(info->Permissions);
+ inode->i_atime = fattr->cf_atime;
+ inode->i_mtime = fattr->cf_mtime;
+ inode->i_ctime = fattr->cf_ctime;
+ inode->i_rdev = fattr->cf_rdev;
+ inode->i_nlink = fattr->cf_nlink;
+ inode->i_uid = fattr->cf_uid;
+ inode->i_gid = fattr->cf_gid;
+
+ /* if dynperm is set, don't clobber existing mode */
+ if (inode->i_state & I_NEW ||
+ !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM))
+ inode->i_mode = fattr->cf_mode;
+
+ cifs_i->cifsAttrs = fattr->cf_cifsattrs;
+ cifs_i->uniqueid = fattr->cf_uniqueid;
+
+ if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
+ cifs_i->time = 0;
+ else
+ cifs_i->time = jiffies;
+
+ cFYI(1, ("inode 0x%p old_time=%ld new_time=%ld", inode,
+ oldtime, cifs_i->time));
+
+ cifs_i->delete_pending = fattr->cf_flags & CIFS_FATTR_DELETE_PENDING;
/*
- * Since we set the inode type below we need to mask off
- * to avoid strange results if bits set above.
+ * Can't safely change the file size here if the client is writing to
+ * it due to potential races.
*/
- inode->i_mode &= ~S_IFMT;
- switch (le32_to_cpu(info->Type)) {
- case UNIX_FILE:
- inode->i_mode |= S_IFREG;
- break;
- case UNIX_SYMLINK:
- inode->i_mode |= S_IFLNK;
- break;
- case UNIX_DIR:
- inode->i_mode |= S_IFDIR;
- break;
- case UNIX_CHARDEV:
- inode->i_mode |= S_IFCHR;
- inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor),
- le64_to_cpu(info->DevMinor) & MINORMASK);
- break;
- case UNIX_BLOCKDEV:
- inode->i_mode |= S_IFBLK;
- inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor),
- le64_to_cpu(info->DevMinor) & MINORMASK);
- break;
- case UNIX_FIFO:
- inode->i_mode |= S_IFIFO;
- break;
- case UNIX_SOCKET:
- inode->i_mode |= S_IFSOCK;
- break;
- default:
- /* safest to call it a file if we do not know */
- inode->i_mode |= S_IFREG;
- cFYI(1, ("unknown type %d", le32_to_cpu(info->Type)));
- break;
- }
-
- if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) &&
- !force_uid_gid)
- inode->i_uid = cifs_sb->mnt_uid;
- else
- inode->i_uid = le64_to_cpu(info->Uid);
-
- if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) &&
- !force_uid_gid)
- inode->i_gid = cifs_sb->mnt_gid;
- else
- inode->i_gid = le64_to_cpu(info->Gid);
-
- inode->i_nlink = le64_to_cpu(info->Nlinks);
-
- cifsInfo->server_eof = end_of_file;
spin_lock(&inode->i_lock);
- if (is_size_safe_to_change(cifsInfo, end_of_file)) {
- /*
- * We can not safely change the file size here if the client
- * is writing to it due to potential races.
- */
- i_size_write(inode, end_of_file);
+ if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) {
+ i_size_write(inode, fattr->cf_eof);
/*
* i_blocks is not related to (i_size / i_blksize),
* but instead 512 byte (2**9) size is required for
* calculating num blocks.
*/
- inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
+ inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
}
spin_unlock(&inode->i_lock);
+
+ cifs_set_ops(inode, fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL);
}
+/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */
+void
+cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
+ struct cifs_sb_info *cifs_sb)
+{
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_uniqueid = le64_to_cpu(info->UniqueId);
+ fattr->cf_bytes = le64_to_cpu(info->NumOfBytes);
+ fattr->cf_eof = le64_to_cpu(info->EndOfFile);
+
+ fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
+ fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime);
+ fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange);
+ fattr->cf_mode = le64_to_cpu(info->Permissions);
+
+ /*
+ * Since we set the inode type below we need to mask off
+ * to avoid strange results if bits set above.
+ */
+ fattr->cf_mode &= ~S_IFMT;
+ switch (le32_to_cpu(info->Type)) {
+ case UNIX_FILE:
+ fattr->cf_mode |= S_IFREG;
+ fattr->cf_dtype = DT_REG;
+ break;
+ case UNIX_SYMLINK:
+ fattr->cf_mode |= S_IFLNK;
+ fattr->cf_dtype = DT_LNK;
+ break;
+ case UNIX_DIR:
+ fattr->cf_mode |= S_IFDIR;
+ fattr->cf_dtype = DT_DIR;
+ break;
+ case UNIX_CHARDEV:
+ fattr->cf_mode |= S_IFCHR;
+ fattr->cf_dtype = DT_CHR;
+ fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor),
+ le64_to_cpu(info->DevMinor) & MINORMASK);
+ break;
+ case UNIX_BLOCKDEV:
+ fattr->cf_mode |= S_IFBLK;
+ fattr->cf_dtype = DT_BLK;
+ fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor),
+ le64_to_cpu(info->DevMinor) & MINORMASK);
+ break;
+ case UNIX_FIFO:
+ fattr->cf_mode |= S_IFIFO;
+ fattr->cf_dtype = DT_FIFO;
+ break;
+ case UNIX_SOCKET:
+ fattr->cf_mode |= S_IFSOCK;
+ fattr->cf_dtype = DT_SOCK;
+ break;
+ default:
+ /* safest to call it a file if we do not know */
+ fattr->cf_mode |= S_IFREG;
+ fattr->cf_dtype = DT_REG;
+ cFYI(1, ("unknown type %d", le32_to_cpu(info->Type)));
+ break;
+ }
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
+ fattr->cf_uid = cifs_sb->mnt_uid;
+ else
+ fattr->cf_uid = le64_to_cpu(info->Uid);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
+ fattr->cf_gid = cifs_sb->mnt_gid;
+ else
+ fattr->cf_gid = le64_to_cpu(info->Gid);
+
+ fattr->cf_nlink = le64_to_cpu(info->Nlinks);
+}
/*
- * Needed to setup inode data for the directory which is the
- * junction to the new submount (ie to setup the fake directory
- * which represents a DFS referral)
- */
-static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat,
- struct super_block *sb)
-{
- struct inode *pinode = NULL;
-
- memset(pfnd_dat, 0, sizeof(FILE_UNIX_BASIC_INFO));
-
-/* __le64 pfnd_dat->EndOfFile = cpu_to_le64(0);
- __le64 pfnd_dat->NumOfBytes = cpu_to_le64(0);
- __u64 UniqueId = 0; */
- pfnd_dat->LastStatusChange =
- cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
- pfnd_dat->LastAccessTime =
- cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
- pfnd_dat->LastModificationTime =
- cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
- pfnd_dat->Type = cpu_to_le32(UNIX_DIR);
- pfnd_dat->Permissions = cpu_to_le64(S_IXUGO | S_IRWXU);
- pfnd_dat->Nlinks = cpu_to_le64(2);
- if (sb->s_root)
- pinode = sb->s_root->d_inode;
- if (pinode == NULL)
- return;
-
- /* fill in default values for the remaining based on root
- inode since we can not query the server for this inode info */
- pfnd_dat->DevMajor = cpu_to_le64(MAJOR(pinode->i_rdev));
- pfnd_dat->DevMinor = cpu_to_le64(MINOR(pinode->i_rdev));
- pfnd_dat->Uid = cpu_to_le64(pinode->i_uid);
- pfnd_dat->Gid = cpu_to_le64(pinode->i_gid);
-}
-
-/**
- * cifs_new inode - create new inode, initialize, and hash it
- * @sb - pointer to superblock
- * @inum - if valid pointer and serverino is enabled, replace i_ino with val
+ * Fill a cifs_fattr struct with fake inode info.
*
- * Create a new inode, initialize it for CIFS and hash it. Returns the new
- * inode or NULL if one couldn't be allocated.
- *
- * If the share isn't mounted with "serverino" or inum is a NULL pointer then
- * we'll just use the inode number assigned by new_inode(). Note that this can
- * mean i_ino collisions since the i_ino assigned by new_inode is not
- * guaranteed to be unique.
+ * Needed to setup cifs_fattr data for the directory which is the
+ * junction to the new submount (ie to setup the fake directory
+ * which represents a DFS referral).
*/
-struct inode *
-cifs_new_inode(struct super_block *sb, __u64 *inum)
+void
+cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
{
- struct inode *inode;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
- inode = new_inode(sb);
- if (inode == NULL)
- return NULL;
+ cFYI(1, ("creating fake fattr for DFS referral"));
- /*
- * BB: Is i_ino == 0 legal? Here, we assume that it is. If it isn't we
- * stop passing inum as ptr. Are there sanity checks we can use to
- * ensure that the server is really filling in that field? Also,
- * if serverino is disabled, perhaps we should be using iunique()?
- */
- if (inum && (CIFS_SB(sb)->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
- inode->i_ino = (unsigned long) *inum;
-
- /*
- * must set this here instead of cifs_alloc_inode since VFS will
- * clobber i_flags
- */
- if (sb->s_flags & MS_NOATIME)
- inode->i_flags |= S_NOATIME | S_NOCMTIME;
-
- insert_inode_hash(inode);
-
- return inode;
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
+ fattr->cf_uid = cifs_sb->mnt_uid;
+ fattr->cf_gid = cifs_sb->mnt_gid;
+ fattr->cf_atime = CURRENT_TIME;
+ fattr->cf_ctime = CURRENT_TIME;
+ fattr->cf_mtime = CURRENT_TIME;
+ fattr->cf_nlink = 2;
+ fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
}
int cifs_get_inode_info_unix(struct inode **pinode,
- const unsigned char *full_path, struct super_block *sb, int xid)
+ const unsigned char *full_path,
+ struct super_block *sb, int xid)
{
- int rc = 0;
+ int rc;
FILE_UNIX_BASIC_INFO find_data;
- struct cifsTconInfo *pTcon;
- struct inode *inode;
+ struct cifs_fattr fattr;
+ struct cifsTconInfo *tcon;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
- bool is_dfs_referral = false;
- struct cifsInodeInfo *cifsInfo;
- __u64 num_of_bytes;
- __u64 end_of_file;
- pTcon = cifs_sb->tcon;
+ tcon = cifs_sb->tcon;
cFYI(1, ("Getting info on %s", full_path));
/* could have done a find first instead but this returns more info */
- rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &find_data,
+ rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
- if (rc == -EREMOTE && !is_dfs_referral) {
- is_dfs_referral = true;
- cFYI(DBG2, ("DFS ref"));
- /* for DFS, server does not give us real inode data */
- fill_fake_finddataunix(&find_data, sb);
+
+ if (!rc) {
+ cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
+ } else if (rc == -EREMOTE) {
+ cifs_create_dfs_fattr(&fattr, sb);
rc = 0;
- } else if (rc)
- goto cgiiu_exit;
-
- num_of_bytes = le64_to_cpu(find_data.NumOfBytes);
- end_of_file = le64_to_cpu(find_data.EndOfFile);
-
- /* get new inode */
- if (*pinode == NULL) {
- __u64 unique_id = le64_to_cpu(find_data.UniqueId);
- *pinode = cifs_new_inode(sb, &unique_id);
- if (*pinode == NULL) {
- rc = -ENOMEM;
- goto cgiiu_exit;
- }
+ } else {
+ return rc;
}
- inode = *pinode;
- cifsInfo = CIFS_I(inode);
+ if (*pinode == NULL) {
+ /* get new inode */
+ *pinode = cifs_iget(sb, &fattr);
+ if (!*pinode)
+ rc = -ENOMEM;
+ } else {
+ /* we already have inode, update it */
+ cifs_fattr_to_inode(*pinode, &fattr);
+ }
- cFYI(1, ("Old time %ld", cifsInfo->time));
- cifsInfo->time = jiffies;
- cFYI(1, ("New time %ld", cifsInfo->time));
- /* this is ok to set on every inode revalidate */
- atomic_set(&cifsInfo->inUse, 1);
-
- cifs_unix_info_to_inode(inode, &find_data, 0);
-
- if (num_of_bytes < end_of_file)
- cFYI(1, ("allocation size less than end of file"));
- cFYI(1, ("Size %ld and blocks %llu",
- (unsigned long) inode->i_size,
- (unsigned long long)inode->i_blocks));
-
- cifs_set_ops(inode, is_dfs_referral);
-cgiiu_exit:
return rc;
}
-static int decode_sfu_inode(struct inode *inode, __u64 size,
- const unsigned char *path,
- struct cifs_sb_info *cifs_sb, int xid)
+static int
+cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
+ struct cifs_sb_info *cifs_sb, int xid)
{
int rc;
int oplock = 0;
@@ -321,10 +284,15 @@
pbuf = buf;
- if (size == 0) {
- inode->i_mode |= S_IFIFO;
+ fattr->cf_mode &= ~S_IFMT;
+
+ if (fattr->cf_eof == 0) {
+ fattr->cf_mode |= S_IFIFO;
+ fattr->cf_dtype = DT_FIFO;
return 0;
- } else if (size < 8) {
+ } else if (fattr->cf_eof < 8) {
+ fattr->cf_mode |= S_IFREG;
+ fattr->cf_dtype = DT_REG;
return -EINVAL; /* EOPNOTSUPP? */
}
@@ -336,42 +304,46 @@
if (rc == 0) {
int buf_type = CIFS_NO_BUFFER;
/* Read header */
- rc = CIFSSMBRead(xid, pTcon,
- netfid,
+ rc = CIFSSMBRead(xid, pTcon, netfid,
24 /* length */, 0 /* offset */,
&bytes_read, &pbuf, &buf_type);
if ((rc == 0) && (bytes_read >= 8)) {
if (memcmp("IntxBLK", pbuf, 8) == 0) {
cFYI(1, ("Block device"));
- inode->i_mode |= S_IFBLK;
+ fattr->cf_mode |= S_IFBLK;
+ fattr->cf_dtype = DT_BLK;
if (bytes_read == 24) {
/* we have enough to decode dev num */
__u64 mjr; /* major */
__u64 mnr; /* minor */
mjr = le64_to_cpu(*(__le64 *)(pbuf+8));
mnr = le64_to_cpu(*(__le64 *)(pbuf+16));
- inode->i_rdev = MKDEV(mjr, mnr);
+ fattr->cf_rdev = MKDEV(mjr, mnr);
}
} else if (memcmp("IntxCHR", pbuf, 8) == 0) {
cFYI(1, ("Char device"));
- inode->i_mode |= S_IFCHR;
+ fattr->cf_mode |= S_IFCHR;
+ fattr->cf_dtype = DT_CHR;
if (bytes_read == 24) {
/* we have enough to decode dev num */
__u64 mjr; /* major */
__u64 mnr; /* minor */
mjr = le64_to_cpu(*(__le64 *)(pbuf+8));
mnr = le64_to_cpu(*(__le64 *)(pbuf+16));
- inode->i_rdev = MKDEV(mjr, mnr);
+ fattr->cf_rdev = MKDEV(mjr, mnr);
}
} else if (memcmp("IntxLNK", pbuf, 7) == 0) {
cFYI(1, ("Symlink"));
- inode->i_mode |= S_IFLNK;
+ fattr->cf_mode |= S_IFLNK;
+ fattr->cf_dtype = DT_LNK;
} else {
- inode->i_mode |= S_IFREG; /* file? */
+ fattr->cf_mode |= S_IFREG; /* file? */
+ fattr->cf_dtype = DT_REG;
rc = -EOPNOTSUPP;
}
} else {
- inode->i_mode |= S_IFREG; /* then it is a file */
+ fattr->cf_mode |= S_IFREG; /* then it is a file */
+ fattr->cf_dtype = DT_REG;
rc = -EOPNOTSUPP; /* or some unknown SFU type */
}
CIFSSMBClose(xid, pTcon, netfid);
@@ -381,9 +353,13 @@
#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */
-static int get_sfu_mode(struct inode *inode,
- const unsigned char *path,
- struct cifs_sb_info *cifs_sb, int xid)
+/*
+ * Fetch mode bits as provided by SFU.
+ *
+ * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ?
+ */
+static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
+ struct cifs_sb_info *cifs_sb, int xid)
{
#ifdef CONFIG_CIFS_XATTR
ssize_t rc;
@@ -391,68 +367,80 @@
__u32 mode;
rc = CIFSSMBQueryEA(xid, cifs_sb->tcon, path, "SETFILEBITS",
- ea_value, 4 /* size of buf */, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ ea_value, 4 /* size of buf */, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc < 0)
return (int)rc;
else if (rc > 3) {
mode = le32_to_cpu(*((__le32 *)ea_value));
- inode->i_mode &= ~SFBITS_MASK;
- cFYI(1, ("special bits 0%o org mode 0%o", mode, inode->i_mode));
- inode->i_mode = (mode & SFBITS_MASK) | inode->i_mode;
+ fattr->cf_mode &= ~SFBITS_MASK;
+ cFYI(1, ("special bits 0%o org mode 0%o", mode,
+ fattr->cf_mode));
+ fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode;
cFYI(1, ("special mode bits 0%o", mode));
- return 0;
- } else {
- return 0;
}
+
+ return 0;
#else
return -EOPNOTSUPP;
#endif
}
-/*
- * Needed to setup inode data for the directory which is the
- * junction to the new submount (ie to setup the fake directory
- * which represents a DFS referral)
- */
-static void fill_fake_finddata(FILE_ALL_INFO *pfnd_dat,
- struct super_block *sb)
+/* Fill a cifs_fattr struct with info from FILE_ALL_INFO */
+void
+cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
+ struct cifs_sb_info *cifs_sb, bool adjust_tz)
{
- memset(pfnd_dat, 0, sizeof(FILE_ALL_INFO));
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
+ if (info->DeletePending)
+ fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING;
-/* __le64 pfnd_dat->AllocationSize = cpu_to_le64(0);
- __le64 pfnd_dat->EndOfFile = cpu_to_le64(0);
- __u8 pfnd_dat->DeletePending = 0;
- __u8 pfnd_data->Directory = 0;
- __le32 pfnd_dat->EASize = 0;
- __u64 pfnd_dat->IndexNumber = 0;
- __u64 pfnd_dat->IndexNumber1 = 0; */
- pfnd_dat->CreationTime =
- cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
- pfnd_dat->LastAccessTime =
- cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
- pfnd_dat->LastWriteTime =
- cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
- pfnd_dat->ChangeTime =
- cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
- pfnd_dat->Attributes = cpu_to_le32(ATTR_DIRECTORY);
- pfnd_dat->NumberOfLinks = cpu_to_le32(2);
+ if (info->LastAccessTime)
+ fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
+ else
+ fattr->cf_atime = CURRENT_TIME;
+
+ fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
+ fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
+
+ if (adjust_tz) {
+ fattr->cf_ctime.tv_sec += cifs_sb->tcon->ses->server->timeAdj;
+ fattr->cf_mtime.tv_sec += cifs_sb->tcon->ses->server->timeAdj;
+ }
+
+ fattr->cf_eof = le64_to_cpu(info->EndOfFile);
+ fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
+
+ if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
+ fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
+ fattr->cf_dtype = DT_DIR;
+ } else {
+ fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
+ fattr->cf_dtype = DT_REG;
+
+ /* clear write bits if ATTR_READONLY is set */
+ if (fattr->cf_cifsattrs & ATTR_READONLY)
+ fattr->cf_mode &= ~(S_IWUGO);
+ }
+
+ fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
+
+ fattr->cf_uid = cifs_sb->mnt_uid;
+ fattr->cf_gid = cifs_sb->mnt_gid;
}
int cifs_get_inode_info(struct inode **pinode,
const unsigned char *full_path, FILE_ALL_INFO *pfindData,
struct super_block *sb, int xid, const __u16 *pfid)
{
- int rc = 0;
- __u32 attr;
- struct cifsInodeInfo *cifsInfo;
+ int rc = 0, tmprc;
struct cifsTconInfo *pTcon;
- struct inode *inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
char *buf = NULL;
bool adjustTZ = false;
- bool is_dfs_referral = false;
- umode_t default_mode;
+ struct cifs_fattr fattr;
pTcon = cifs_sb->tcon;
cFYI(1, ("Getting info on %s", full_path));
@@ -487,164 +475,83 @@
adjustTZ = true;
}
}
- /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
- if (rc == -EREMOTE) {
- is_dfs_referral = true;
- fill_fake_finddata(pfindData, sb);
+
+ if (!rc) {
+ cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *) pfindData,
+ cifs_sb, adjustTZ);
+ } else if (rc == -EREMOTE) {
+ cifs_create_dfs_fattr(&fattr, sb);
rc = 0;
- } else if (rc)
+ } else {
goto cgii_exit;
+ }
- attr = le32_to_cpu(pfindData->Attributes);
-
- /* get new inode */
+ /*
+ * If an inode wasn't passed in, then get the inode number
+ *
+ * Is an i_ino of zero legal? Can we use that to check if the server
+ * supports returning inode numbers? Are there other sanity checks we
+ * can use to ensure that the server is really filling in that field?
+ *
+ * We can not use the IndexNumber field by default from Windows or
+ * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA
+ * CIFS spec claims that this value is unique within the scope of a
+ * share, and the windows docs hint that it's actually unique
+ * per-machine.
+ *
+ * There may be higher info levels that work but are there Windows
+ * server or network appliances for which IndexNumber field is not
+ * guaranteed unique?
+ */
if (*pinode == NULL) {
- __u64 inode_num;
- __u64 *pinum = &inode_num;
-
- /* Is an i_ino of zero legal? Can we use that to check
- if the server supports returning inode numbers? Are
- there other sanity checks we can use to ensure that
- the server is really filling in that field? */
-
- /* We can not use the IndexNumber field by default from
- Windows or Samba (in ALL_INFO buf) but we can request
- it explicitly. It may not be unique presumably if
- the server has multiple devices mounted under one share */
-
- /* There may be higher info levels that work but are
- there Windows server or network appliances for which
- IndexNumber field is not guaranteed unique? */
-
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
int rc1 = 0;
rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
- full_path, pinum,
+ full_path, &fattr.cf_uniqueid,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc1) {
- cFYI(1, ("GetSrvInodeNum rc %d", rc1));
- pinum = NULL;
/* BB EOPNOSUPP disable SERVER_INUM? */
+ cFYI(1, ("GetSrvInodeNum rc %d", rc1));
+ fattr.cf_uniqueid = iunique(sb, ROOT_I);
}
} else {
- pinum = NULL;
+ fattr.cf_uniqueid = iunique(sb, ROOT_I);
}
-
- *pinode = cifs_new_inode(sb, pinum);
- if (*pinode == NULL) {
- rc = -ENOMEM;
- goto cgii_exit;
- }
- }
- inode = *pinode;
- cifsInfo = CIFS_I(inode);
- cifsInfo->cifsAttrs = attr;
- cifsInfo->delete_pending = pfindData->DeletePending ? true : false;
- cFYI(1, ("Old time %ld", cifsInfo->time));
- cifsInfo->time = jiffies;
- cFYI(1, ("New time %ld", cifsInfo->time));
-
- /* blksize needs to be multiple of two. So safer to default to
- blksize and blkbits set in superblock so 2**blkbits and blksize
- will match rather than setting to:
- (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
-
- /* Linux can not store file creation time so ignore it */
- if (pfindData->LastAccessTime)
- inode->i_atime = cifs_NTtimeToUnix(pfindData->LastAccessTime);
- else /* do not need to use current_fs_time - time not stored */
- inode->i_atime = CURRENT_TIME;
- inode->i_mtime = cifs_NTtimeToUnix(pfindData->LastWriteTime);
- inode->i_ctime = cifs_NTtimeToUnix(pfindData->ChangeTime);
- cFYI(DBG2, ("Attributes came in as 0x%x", attr));
- if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) {
- inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj;
- inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj;
- }
-
- /* get default inode mode */
- if (attr & ATTR_DIRECTORY)
- default_mode = cifs_sb->mnt_dir_mode;
- else
- default_mode = cifs_sb->mnt_file_mode;
-
- /* set permission bits */
- if (atomic_read(&cifsInfo->inUse) == 0 ||
- (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
- inode->i_mode = default_mode;
- else {
- /* just reenable write bits if !ATTR_READONLY */
- if ((inode->i_mode & S_IWUGO) == 0 &&
- (attr & ATTR_READONLY) == 0)
- inode->i_mode |= (S_IWUGO & default_mode);
-
- inode->i_mode &= ~S_IFMT;
- }
- /* clear write bits if ATTR_READONLY is set */
- if (attr & ATTR_READONLY)
- inode->i_mode &= ~S_IWUGO;
-
- /* set inode type */
- if ((attr & ATTR_SYSTEM) &&
- (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) {
- /* no need to fix endianness on 0 */
- if (pfindData->EndOfFile == 0)
- inode->i_mode |= S_IFIFO;
- else if (decode_sfu_inode(inode,
- le64_to_cpu(pfindData->EndOfFile),
- full_path, cifs_sb, xid))
- cFYI(1, ("unknown SFU file type\n"));
} else {
- if (attr & ATTR_DIRECTORY)
- inode->i_mode |= S_IFDIR;
- else
- inode->i_mode |= S_IFREG;
+ fattr.cf_uniqueid = CIFS_I(*pinode)->uniqueid;
}
- cifsInfo->server_eof = le64_to_cpu(pfindData->EndOfFile);
- spin_lock(&inode->i_lock);
- if (is_size_safe_to_change(cifsInfo, cifsInfo->server_eof)) {
- /* can not safely shrink the file size here if the
- client is writing to it due to potential races */
- i_size_write(inode, cifsInfo->server_eof);
-
- /* 512 bytes (2**9) is the fake blocksize that must be
- used for this calculation */
- inode->i_blocks = (512 - 1 + le64_to_cpu(
- pfindData->AllocationSize)) >> 9;
+ /* query for SFU type info if supported and needed */
+ if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid);
+ if (tmprc)
+ cFYI(1, ("cifs_sfu_type failed: %d", tmprc));
}
- spin_unlock(&inode->i_lock);
- inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
-
- /* BB fill in uid and gid here? with help from winbind?
- or retrieve from NTFS stream extended attribute */
#ifdef CONFIG_CIFS_EXPERIMENTAL
/* fill in 0777 bits from ACL */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
cFYI(1, ("Getting mode bits from ACL"));
- acl_to_uid_mode(cifs_sb, inode, full_path, pfid);
+ cifs_acl_to_fattr(cifs_sb, &fattr, *pinode, full_path, pfid);
}
#endif
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
- /* fill in remaining high mode bits e.g. SUID, VTX */
- get_sfu_mode(inode, full_path, cifs_sb, xid);
- } else if (atomic_read(&cifsInfo->inUse) == 0) {
- inode->i_uid = cifs_sb->mnt_uid;
- inode->i_gid = cifs_sb->mnt_gid;
- /* set so we do not keep refreshing these fields with
- bad data after user has changed them in memory */
- atomic_set(&cifsInfo->inUse, 1);
+
+ /* fill in remaining high mode bits e.g. SUID, VTX */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
+ cifs_sfu_mode(&fattr, full_path, cifs_sb, xid);
+
+ if (!*pinode) {
+ *pinode = cifs_iget(sb, &fattr);
+ if (!*pinode)
+ rc = -ENOMEM;
+ } else {
+ cifs_fattr_to_inode(*pinode, &fattr);
}
- cifs_set_ops(inode, is_dfs_referral);
-
-
-
-
cgii_exit:
kfree(buf);
return rc;
@@ -695,33 +602,78 @@
return full_path;
}
+static int
+cifs_find_inode(struct inode *inode, void *opaque)
+{
+ struct cifs_fattr *fattr = (struct cifs_fattr *) opaque;
+
+ if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid)
+ return 0;
+
+ return 1;
+}
+
+static int
+cifs_init_inode(struct inode *inode, void *opaque)
+{
+ struct cifs_fattr *fattr = (struct cifs_fattr *) opaque;
+
+ CIFS_I(inode)->uniqueid = fattr->cf_uniqueid;
+ return 0;
+}
+
+/* Given fattrs, get a corresponding inode */
+struct inode *
+cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
+{
+ unsigned long hash;
+ struct inode *inode;
+
+ cFYI(1, ("looking for uniqueid=%llu", fattr->cf_uniqueid));
+
+ /* hash down to 32-bits on 32-bit arch */
+ hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
+
+ inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr);
+
+ /* we have fattrs in hand, update the inode */
+ if (inode) {
+ cifs_fattr_to_inode(inode, fattr);
+ if (sb->s_flags & MS_NOATIME)
+ inode->i_flags |= S_NOATIME | S_NOCMTIME;
+ if (inode->i_state & I_NEW) {
+ inode->i_ino = hash;
+ unlock_new_inode(inode);
+ }
+ }
+
+ return inode;
+}
+
/* gets root inode */
struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)
{
int xid;
struct cifs_sb_info *cifs_sb;
- struct inode *inode;
+ struct inode *inode = NULL;
long rc;
char *full_path;
- inode = iget_locked(sb, ino);
- if (!inode)
- return ERR_PTR(-ENOMEM);
- if (!(inode->i_state & I_NEW))
- return inode;
-
- cifs_sb = CIFS_SB(inode->i_sb);
+ cifs_sb = CIFS_SB(sb);
full_path = cifs_build_path_to_root(cifs_sb);
if (full_path == NULL)
return ERR_PTR(-ENOMEM);
xid = GetXid();
if (cifs_sb->tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb,
- xid);
+ rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
else
- rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb,
+ rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
xid, NULL);
+
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
if (rc && cifs_sb->tcon->ipc) {
cFYI(1, ("ipc connection - fake read inode"));
inode->i_mode |= S_IFDIR;
@@ -737,7 +689,6 @@
return ERR_PTR(rc);
}
- unlock_new_inode(inode);
kfree(full_path);
/* can not call macro FreeXid here since in a void func
@@ -1063,44 +1014,6 @@
return rc;
}
-void posix_fill_in_inode(struct inode *tmp_inode,
- FILE_UNIX_BASIC_INFO *pData, int isNewInode)
-{
- struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
- loff_t local_size;
- struct timespec local_mtime;
-
- cifsInfo->time = jiffies;
- atomic_inc(&cifsInfo->inUse);
-
- /* save mtime and size */
- local_mtime = tmp_inode->i_mtime;
- local_size = tmp_inode->i_size;
-
- cifs_unix_info_to_inode(tmp_inode, pData, 1);
- cifs_set_ops(tmp_inode, false);
-
- if (!S_ISREG(tmp_inode->i_mode))
- return;
-
- /*
- * No sense invalidating pages for new inode
- * since we we have not started caching
- * readahead file data yet.
- */
- if (isNewInode)
- return;
-
- if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
- (local_size == tmp_inode->i_size)) {
- cFYI(1, ("inode exists but unchanged"));
- } else {
- /* file may have changed on server */
- cFYI(1, ("invalidate inode, readdir detected change"));
- invalidate_remote_inode(tmp_inode);
- }
-}
-
int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
{
int rc = 0, tmprc;
@@ -1109,6 +1022,7 @@
struct cifsTconInfo *pTcon;
char *full_path = NULL;
struct inode *newinode = NULL;
+ struct cifs_fattr fattr;
cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode));
@@ -1148,7 +1062,6 @@
cFYI(1, ("posix mkdir returned 0x%x", rc));
d_drop(direntry);
} else {
- __u64 unique_id;
if (pInfo->Type == cpu_to_le32(-1)) {
/* no return info, go query for it */
kfree(pInfo);
@@ -1162,20 +1075,15 @@
else
direntry->d_op = &cifs_dentry_ops;
- unique_id = le64_to_cpu(pInfo->UniqueId);
- newinode = cifs_new_inode(inode->i_sb, &unique_id);
- if (newinode == NULL) {
+ cifs_unix_basic_to_fattr(&fattr, pInfo, cifs_sb);
+ newinode = cifs_iget(inode->i_sb, &fattr);
+ if (!newinode) {
kfree(pInfo);
goto mkdir_get_info;
}
- newinode->i_nlink = 2;
d_instantiate(direntry, newinode);
- /* we already checked in POSIXCreate whether
- frame was long enough */
- posix_fill_in_inode(direntry->d_inode,
- pInfo, 1 /* NewInode */);
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("instantiated dentry %p %s to inode %p",
direntry, direntry->d_name.name, newinode));
@@ -1238,10 +1146,10 @@
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
}
- CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
} else {
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
(mode & S_IWUGO) == 0) {
@@ -1622,6 +1530,7 @@
if (!err) {
generic_fillattr(dentry->d_inode, stat);
stat->blksize = CIFS_MAX_MSGSIZE;
+ stat->ino = CIFS_I(dentry->d_inode)->uniqueid;
}
return err;
}
@@ -1786,6 +1695,7 @@
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon;
struct cifs_unix_set_info_args *args = NULL;
+ struct cifsFileInfo *open_file;
cFYI(1, ("setattr_unix on file %s attrs->ia_valid=0x%x",
direntry->d_name.name, attrs->ia_valid));
@@ -1872,10 +1782,18 @@
args->ctime = NO_CHANGE_64;
args->device = 0;
- rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path, args,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ open_file = find_writable_file(cifsInode);
+ if (open_file) {
+ u16 nfid = open_file->netfid;
+ u32 npid = open_file->pid;
+ rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
+ atomic_dec(&open_file->wrtPending);
+ } else {
+ rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ }
if (!rc)
rc = inode_setattr(inode, attrs);
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 86d0055..f823a4a 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -63,374 +63,123 @@
}
#endif /* DEBUG2 */
-/* Returns 1 if new inode created, 2 if both dentry and inode were */
-/* Might check in the future if inode number changed so we can rehash inode */
-static int
-construct_dentry(struct qstr *qstring, struct file *file,
- struct inode **ptmp_inode, struct dentry **pnew_dentry,
- __u64 *inum)
+/*
+ * Find the dentry that matches "name". If there isn't one, create one. If it's
+ * a negative dentry or the uniqueid changed, then drop it and recreate it.
+ */
+static struct dentry *
+cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
+ struct cifs_fattr *fattr)
{
- struct dentry *tmp_dentry = NULL;
- struct super_block *sb = file->f_path.dentry->d_sb;
- int rc = 0;
+ struct dentry *dentry, *alias;
+ struct inode *inode;
+ struct super_block *sb = parent->d_inode->i_sb;
- cFYI(1, ("For %s", qstring->name));
+ cFYI(1, ("For %s", name->name));
- qstring->hash = full_name_hash(qstring->name, qstring->len);
- tmp_dentry = d_lookup(file->f_path.dentry, qstring);
- if (tmp_dentry) {
- /* BB: overwrite old name? i.e. tmp_dentry->d_name and
- * tmp_dentry->d_name.len??
- */
- cFYI(0, ("existing dentry with inode 0x%p",
- tmp_dentry->d_inode));
- *ptmp_inode = tmp_dentry->d_inode;
- if (*ptmp_inode == NULL) {
- *ptmp_inode = cifs_new_inode(sb, inum);
- if (*ptmp_inode == NULL)
- return rc;
- rc = 1;
- }
- } else {
- tmp_dentry = d_alloc(file->f_path.dentry, qstring);
- if (tmp_dentry == NULL) {
- cERROR(1, ("Failed allocating dentry"));
- *ptmp_inode = NULL;
- return rc;
- }
-
- if (CIFS_SB(sb)->tcon->nocase)
- tmp_dentry->d_op = &cifs_ci_dentry_ops;
- else
- tmp_dentry->d_op = &cifs_dentry_ops;
-
- *ptmp_inode = cifs_new_inode(sb, inum);
- if (*ptmp_inode == NULL)
- return rc;
- rc = 2;
+ dentry = d_lookup(parent, name);
+ if (dentry) {
+ /* FIXME: check for inode number changes? */
+ if (dentry->d_inode != NULL)
+ return dentry;
+ d_drop(dentry);
+ dput(dentry);
}
- tmp_dentry->d_time = jiffies;
- *pnew_dentry = tmp_dentry;
- return rc;
+ dentry = d_alloc(parent, name);
+ if (dentry == NULL)
+ return NULL;
+
+ inode = cifs_iget(sb, fattr);
+ if (!inode) {
+ dput(dentry);
+ return NULL;
+ }
+
+ if (CIFS_SB(sb)->tcon->nocase)
+ dentry->d_op = &cifs_ci_dentry_ops;
+ else
+ dentry->d_op = &cifs_dentry_ops;
+
+ alias = d_materialise_unique(dentry, inode);
+ if (alias != NULL) {
+ dput(dentry);
+ if (IS_ERR(alias))
+ return NULL;
+ dentry = alias;
+ }
+
+ return dentry;
}
-static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
- char *buf, unsigned int *pobject_type, int isNewInode)
+static void
+cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
{
- loff_t local_size;
- struct timespec local_mtime;
+ fattr->cf_uid = cifs_sb->mnt_uid;
+ fattr->cf_gid = cifs_sb->mnt_gid;
- struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
- struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
- __u32 attr;
- __u64 allocation_size;
- __u64 end_of_file;
- umode_t default_mode;
-
- /* save mtime and size */
- local_mtime = tmp_inode->i_mtime;
- local_size = tmp_inode->i_size;
-
- if (new_buf_type) {
- FILE_DIRECTORY_INFO *pfindData = (FILE_DIRECTORY_INFO *)buf;
-
- attr = le32_to_cpu(pfindData->ExtFileAttributes);
- allocation_size = le64_to_cpu(pfindData->AllocationSize);
- end_of_file = le64_to_cpu(pfindData->EndOfFile);
- tmp_inode->i_atime =
- cifs_NTtimeToUnix(pfindData->LastAccessTime);
- tmp_inode->i_mtime =
- cifs_NTtimeToUnix(pfindData->LastWriteTime);
- tmp_inode->i_ctime =
- cifs_NTtimeToUnix(pfindData->ChangeTime);
- } else { /* legacy, OS2 and DOS style */
- int offset = cifs_sb->tcon->ses->server->timeAdj;
- FIND_FILE_STANDARD_INFO *pfindData =
- (FIND_FILE_STANDARD_INFO *)buf;
-
- tmp_inode->i_mtime = cnvrtDosUnixTm(pfindData->LastWriteDate,
- pfindData->LastWriteTime,
- offset);
- tmp_inode->i_atime = cnvrtDosUnixTm(pfindData->LastAccessDate,
- pfindData->LastAccessTime,
- offset);
- tmp_inode->i_ctime = cnvrtDosUnixTm(pfindData->LastWriteDate,
- pfindData->LastWriteTime,
- offset);
- attr = le16_to_cpu(pfindData->Attributes);
- allocation_size = le32_to_cpu(pfindData->AllocationSize);
- end_of_file = le32_to_cpu(pfindData->DataSize);
+ if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
+ fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
+ fattr->cf_dtype = DT_DIR;
+ } else {
+ fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
+ fattr->cf_dtype = DT_REG;
}
- /* Linux can not store file creation time unfortunately so ignore it */
+ if (fattr->cf_cifsattrs & ATTR_READONLY)
+ fattr->cf_mode &= ~S_IWUGO;
- cifsInfo->cifsAttrs = attr;
-#ifdef CONFIG_CIFS_EXPERIMENTAL
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
- /* get more accurate mode via ACL - so force inode refresh */
- cifsInfo->time = 0;
- } else
-#endif /* CONFIG_CIFS_EXPERIMENTAL */
- cifsInfo->time = jiffies;
-
- /* treat dos attribute of read-only as read-only mode bit e.g. 555? */
- /* 2767 perms - indicate mandatory locking */
- /* BB fill in uid and gid here? with help from winbind?
- or retrieve from NTFS stream extended attribute */
- if (atomic_read(&cifsInfo->inUse) == 0) {
- tmp_inode->i_uid = cifs_sb->mnt_uid;
- tmp_inode->i_gid = cifs_sb->mnt_gid;
- }
-
- if (attr & ATTR_DIRECTORY)
- default_mode = cifs_sb->mnt_dir_mode;
- else
- default_mode = cifs_sb->mnt_file_mode;
-
- /* set initial permissions */
- if ((atomic_read(&cifsInfo->inUse) == 0) ||
- (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
- tmp_inode->i_mode = default_mode;
- else {
- /* just reenable write bits if !ATTR_READONLY */
- if ((tmp_inode->i_mode & S_IWUGO) == 0 &&
- (attr & ATTR_READONLY) == 0)
- tmp_inode->i_mode |= (S_IWUGO & default_mode);
-
- tmp_inode->i_mode &= ~S_IFMT;
- }
-
- /* clear write bits if ATTR_READONLY is set */
- if (attr & ATTR_READONLY)
- tmp_inode->i_mode &= ~S_IWUGO;
-
- /* set inode type */
- if ((attr & ATTR_SYSTEM) &&
- (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) {
- if (end_of_file == 0) {
- tmp_inode->i_mode |= S_IFIFO;
- *pobject_type = DT_FIFO;
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&
+ fattr->cf_cifsattrs & ATTR_SYSTEM) {
+ if (fattr->cf_eof == 0) {
+ fattr->cf_mode &= ~S_IFMT;
+ fattr->cf_mode |= S_IFIFO;
+ fattr->cf_dtype = DT_FIFO;
} else {
/*
- * trying to get the type can be slow, so just call
- * this a regular file for now, and mark for reval
+ * trying to get the type and mode via SFU can be slow,
+ * so just call those regular files for now, and mark
+ * for reval
*/
- tmp_inode->i_mode |= S_IFREG;
- *pobject_type = DT_REG;
- cifsInfo->time = 0;
+ fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
}
- } else {
- if (attr & ATTR_DIRECTORY) {
- tmp_inode->i_mode |= S_IFDIR;
- *pobject_type = DT_DIR;
- } else {
- tmp_inode->i_mode |= S_IFREG;
- *pobject_type = DT_REG;
- }
- }
-
- /* can not fill in nlink here as in qpathinfo version and Unx search */
- if (atomic_read(&cifsInfo->inUse) == 0)
- atomic_set(&cifsInfo->inUse, 1);
-
- cifsInfo->server_eof = end_of_file;
- spin_lock(&tmp_inode->i_lock);
- 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);
-
- /* 512 bytes (2**9) is the fake blocksize that must be used */
- /* for this calculation, even though the reported blocksize is larger */
- tmp_inode->i_blocks = (512 - 1 + allocation_size) >> 9;
- }
- spin_unlock(&tmp_inode->i_lock);
-
- if (allocation_size < end_of_file)
- cFYI(1, ("May be sparse file, allocation less than file size"));
- cFYI(1, ("File Size %ld and blocks %llu",
- (unsigned long)tmp_inode->i_size,
- (unsigned long long)tmp_inode->i_blocks));
- if (S_ISREG(tmp_inode->i_mode)) {
- cFYI(1, ("File inode"));
- tmp_inode->i_op = &cifs_file_inode_ops;
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
- tmp_inode->i_fop = &cifs_file_direct_nobrl_ops;
- else
- tmp_inode->i_fop = &cifs_file_direct_ops;
- } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
- tmp_inode->i_fop = &cifs_file_nobrl_ops;
- else
- tmp_inode->i_fop = &cifs_file_ops;
-
- if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
- (cifs_sb->tcon->ses->server->maxBuf <
- PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
- tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
- else
- tmp_inode->i_data.a_ops = &cifs_addr_ops;
-
- if (isNewInode)
- 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)) {
- cFYI(1, ("inode exists but unchanged"));
- } else {
- /* file may have changed on server */
- cFYI(1, ("invalidate inode, readdir detected change"));
- invalidate_remote_inode(tmp_inode);
- }
- } else if (S_ISDIR(tmp_inode->i_mode)) {
- cFYI(1, ("Directory inode"));
- tmp_inode->i_op = &cifs_dir_inode_ops;
- tmp_inode->i_fop = &cifs_dir_ops;
- } else if (S_ISLNK(tmp_inode->i_mode)) {
- cFYI(1, ("Symbolic Link inode"));
- tmp_inode->i_op = &cifs_symlink_inode_ops;
- } else {
- cFYI(1, ("Init special inode"));
- init_special_inode(tmp_inode, tmp_inode->i_mode,
- tmp_inode->i_rdev);
}
}
-static void unix_fill_in_inode(struct inode *tmp_inode,
- FILE_UNIX_INFO *pfindData, unsigned int *pobject_type, int isNewInode)
+void
+cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
+ struct cifs_sb_info *cifs_sb)
{
- loff_t local_size;
- struct timespec local_mtime;
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes);
+ fattr->cf_eof = le64_to_cpu(info->EndOfFile);
+ fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
+ fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
+ fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
+ fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
- struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
- struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
+ cifs_fill_common_info(fattr, cifs_sb);
+}
- __u32 type = le32_to_cpu(pfindData->Type);
- __u64 num_of_bytes = le64_to_cpu(pfindData->NumOfBytes);
- __u64 end_of_file = le64_to_cpu(pfindData->EndOfFile);
- cifsInfo->time = jiffies;
- atomic_inc(&cifsInfo->inUse);
+void
+cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
+ struct cifs_sb_info *cifs_sb)
+{
+ int offset = cifs_sb->tcon->ses->server->timeAdj;
- /* save mtime and size */
- local_mtime = tmp_inode->i_mtime;
- local_size = tmp_inode->i_size;
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
+ info->LastAccessTime, offset);
+ fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate,
+ info->LastWriteTime, offset);
+ fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate,
+ info->LastWriteTime, offset);
- tmp_inode->i_atime =
- cifs_NTtimeToUnix(pfindData->LastAccessTime);
- tmp_inode->i_mtime =
- cifs_NTtimeToUnix(pfindData->LastModificationTime);
- tmp_inode->i_ctime =
- cifs_NTtimeToUnix(pfindData->LastStatusChange);
+ fattr->cf_cifsattrs = le16_to_cpu(info->Attributes);
+ fattr->cf_bytes = le32_to_cpu(info->AllocationSize);
+ fattr->cf_eof = le32_to_cpu(info->DataSize);
- tmp_inode->i_mode = le64_to_cpu(pfindData->Permissions);
- /* since we set the inode type below we need to mask off type
- to avoid strange results if bits above were corrupt */
- tmp_inode->i_mode &= ~S_IFMT;
- if (type == UNIX_FILE) {
- *pobject_type = DT_REG;
- tmp_inode->i_mode |= S_IFREG;
- } else if (type == UNIX_SYMLINK) {
- *pobject_type = DT_LNK;
- tmp_inode->i_mode |= S_IFLNK;
- } else if (type == UNIX_DIR) {
- *pobject_type = DT_DIR;
- tmp_inode->i_mode |= S_IFDIR;
- } else if (type == UNIX_CHARDEV) {
- *pobject_type = DT_CHR;
- tmp_inode->i_mode |= S_IFCHR;
- tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor),
- le64_to_cpu(pfindData->DevMinor) & MINORMASK);
- } else if (type == UNIX_BLOCKDEV) {
- *pobject_type = DT_BLK;
- tmp_inode->i_mode |= S_IFBLK;
- tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor),
- le64_to_cpu(pfindData->DevMinor) & MINORMASK);
- } else if (type == UNIX_FIFO) {
- *pobject_type = DT_FIFO;
- tmp_inode->i_mode |= S_IFIFO;
- } else if (type == UNIX_SOCKET) {
- *pobject_type = DT_SOCK;
- tmp_inode->i_mode |= S_IFSOCK;
- } else {
- /* safest to just call it a file */
- *pobject_type = DT_REG;
- tmp_inode->i_mode |= S_IFREG;
- cFYI(1, ("unknown inode type %d", type));
- }
-
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
- tmp_inode->i_uid = cifs_sb->mnt_uid;
- else
- tmp_inode->i_uid = le64_to_cpu(pfindData->Uid);
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
- tmp_inode->i_gid = cifs_sb->mnt_gid;
- else
- tmp_inode->i_gid = le64_to_cpu(pfindData->Gid);
- tmp_inode->i_nlink = le64_to_cpu(pfindData->Nlinks);
-
- cifsInfo->server_eof = end_of_file;
- spin_lock(&tmp_inode->i_lock);
- 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);
-
- /* 512 bytes (2**9) is the fake blocksize that must be used */
- /* for this calculation, not the real blocksize */
- tmp_inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
- }
- spin_unlock(&tmp_inode->i_lock);
-
- if (S_ISREG(tmp_inode->i_mode)) {
- cFYI(1, ("File inode"));
- tmp_inode->i_op = &cifs_file_inode_ops;
-
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
- tmp_inode->i_fop = &cifs_file_direct_nobrl_ops;
- else
- tmp_inode->i_fop = &cifs_file_direct_ops;
- } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
- tmp_inode->i_fop = &cifs_file_nobrl_ops;
- else
- tmp_inode->i_fop = &cifs_file_ops;
-
- if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
- (cifs_sb->tcon->ses->server->maxBuf <
- PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
- tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
- else
- tmp_inode->i_data.a_ops = &cifs_addr_ops;
-
- if (isNewInode)
- return; /* No sense invalidating pages for new inode
- since we have not started caching readahead
- file data for it yet */
-
- if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
- (local_size == tmp_inode->i_size)) {
- cFYI(1, ("inode exists but unchanged"));
- } else {
- /* file may have changed on server */
- cFYI(1, ("invalidate inode, readdir detected change"));
- invalidate_remote_inode(tmp_inode);
- }
- } else if (S_ISDIR(tmp_inode->i_mode)) {
- cFYI(1, ("Directory inode"));
- tmp_inode->i_op = &cifs_dir_inode_ops;
- tmp_inode->i_fop = &cifs_dir_ops;
- } else if (S_ISLNK(tmp_inode->i_mode)) {
- cFYI(1, ("Symbolic Link inode"));
- tmp_inode->i_op = &cifs_symlink_inode_ops;
-/* tmp_inode->i_fop = *//* do not need to set to anything */
- } else {
- cFYI(1, ("Special inode"));
- init_special_inode(tmp_inode, tmp_inode->i_mode,
- tmp_inode->i_rdev);
- }
+ cifs_fill_common_info(fattr, cifs_sb);
}
/* BB eventually need to add the following helper function to
@@ -872,7 +621,7 @@
len = strnlen(filename, PATH_MAX);
}
- *pinum = le64_to_cpu(pFindData->UniqueId);
+ *pinum = le64_to_cpu(pFindData->basic.UniqueId);
} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
FILE_DIRECTORY_INFO *pFindData =
(FILE_DIRECTORY_INFO *)current_entry;
@@ -932,11 +681,12 @@
int rc = 0;
struct qstr qstring;
struct cifsFileInfo *pCifsF;
- unsigned int obj_type;
- __u64 inum;
+ u64 inum;
+ ino_t ino;
+ struct super_block *sb;
struct cifs_sb_info *cifs_sb;
- struct inode *tmp_inode;
struct dentry *tmp_dentry;
+ struct cifs_fattr fattr;
/* get filename and len into qstring */
/* get dentry */
@@ -954,60 +704,53 @@
if (rc != 0)
return 0;
- cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ sb = file->f_path.dentry->d_sb;
+ cifs_sb = CIFS_SB(sb);
qstring.name = scratch_buf;
rc = cifs_get_name_from_search_buf(&qstring, pfindEntry,
pCifsF->srch_inf.info_level,
pCifsF->srch_inf.unicode, cifs_sb,
- max_len,
- &inum /* returned */);
+ max_len, &inum /* returned */);
if (rc)
return rc;
- /* only these two infolevels return valid inode numbers */
- if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX ||
- pCifsF->srch_inf.info_level == SMB_FIND_FILE_ID_FULL_DIR_INFO)
- rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry,
- &inum);
- else
- rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry,
- NULL);
-
- if ((tmp_inode == NULL) || (tmp_dentry == NULL))
- return -ENOMEM;
-
- /* we pass in rc below, indicating whether it is a new inode,
- so we can figure out whether to invalidate the inode cached
- data if the file has changed */
if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
- unix_fill_in_inode(tmp_inode,
- (FILE_UNIX_INFO *)pfindEntry,
- &obj_type, rc);
+ cifs_unix_basic_to_fattr(&fattr,
+ &((FILE_UNIX_INFO *) pfindEntry)->basic,
+ cifs_sb);
else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
- fill_in_inode(tmp_inode, 0 /* old level 1 buffer type */,
- pfindEntry, &obj_type, rc);
+ cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *)
+ pfindEntry, cifs_sb);
else
- fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc);
+ cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *)
+ pfindEntry, cifs_sb);
- if (rc) /* new inode - needs to be tied to dentry */ {
- d_instantiate(tmp_dentry, tmp_inode);
- if (rc == 2)
- d_rehash(tmp_dentry);
- }
+ /* FIXME: make _to_fattr functions fill this out */
+ if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_ID_FULL_DIR_INFO)
+ fattr.cf_uniqueid = inum;
+ else
+ fattr.cf_uniqueid = iunique(sb, ROOT_I);
+ ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
+ tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
- tmp_inode->i_ino, obj_type);
+ ino, fattr.cf_dtype);
+
+ /*
+ * we can not return filldir errors to the caller since they are
+ * "normal" when the stat blocksize is too small - we return remapped
+ * error instead
+ *
+ * FIXME: This looks bogus. filldir returns -EOVERFLOW in the above
+ * case already. Why should we be clobbering other errors from it?
+ */
if (rc) {
cFYI(1, ("filldir rc = %d", rc));
- /* we can not return filldir errors to the caller
- since they are "normal" when the stat blocksize
- is too small - we return remapped error instead */
rc = -EOVERFLOW;
}
-
dput(tmp_dentry);
return rc;
}