cifs: convert cifs_get_inode_info and non-posix readdir to use cifs_iget

cifs: convert cifs_get_inode_info and non-posix readdir to use cifs_iget

Rather than allocating an inode and filling it out, have
cifs_get_inode_info fill out a cifs_fattr and call cifs_iget. This means
a pretty hefty reorganization of cifs_get_inode_info.

For the readdir codepath, add a couple of new functions for filling out
cifs_fattr's from different FindFile response infolevels.

Finally, remove cifs_new_inode since there are no more callers.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
Jeff Layton 2009-07-09 01:46:37 -04:00 committed by Steve French
parent b77863bfa1
commit 0b8f18e358
5 changed files with 249 additions and 526 deletions

View file

@ -327,7 +327,7 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
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 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
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 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
/* 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 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
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 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
/* 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 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
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 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
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 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
sizeof(struct cifs_sid)); */
return 0;
}
@ -671,8 +670,9 @@ static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
}
/* 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 @@ void acl_to_uid_mode(struct cifs_sb_info *cifs_sb, struct inode *inode,
/* 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));

View file

@ -479,6 +479,8 @@ struct dfs_info3_param {
*/
#define CIFS_FATTR_DFS_REFERRAL 0x1
#define CIFS_FATTR_DELETE_PENDING 0x2
#define CIFS_FATTR_NEED_REVAL 0x4
struct cifs_fattr {
u32 cf_flags;

View file

@ -102,7 +102,6 @@ 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_new_inode(struct super_block *sb, __u64 *inum);
extern struct inode *cifs_iget(struct super_block *sb,
struct cifs_fattr *fattr);
@ -113,7 +112,8 @@ extern int cifs_get_inode_info(struct inode **pinode,
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,
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);

View file

@ -82,23 +82,34 @@ void
cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
{
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
unsigned long now = jiffies;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
unsigned long oldtime = cifs_i->time;
inode->i_atime = fattr->cf_atime;
inode->i_mtime = fattr->cf_mtime;
inode->i_ctime = fattr->cf_ctime;
inode->i_mode = fattr->cf_mode;
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,
cifs_i->time, now));
cifs_i->time = now;
oldtime, cifs_i->time));
cifs_i->delete_pending = fattr->cf_flags & CIFS_FATTR_DELETE_PENDING;
/*
* Can't safely change the file size here if the client is writing to
@ -219,49 +230,6 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
}
/**
* 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
*
* 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.
*/
struct inode *
cifs_new_inode(struct super_block *sb, __u64 *inum)
{
struct inode *inode;
inode = new_inode(sb);
if (inode == NULL)
return NULL;
/*
* 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;
}
int cifs_get_inode_info_unix(struct inode **pinode,
const unsigned char *full_path,
struct super_block *sb, int xid)
@ -302,8 +270,8 @@ int cifs_get_inode_info_unix(struct inode **pinode,
return rc;
}
static int decode_sfu_inode(struct inode *inode, __u64 size,
const unsigned char *path,
static int
cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
struct cifs_sb_info *cifs_sb, int xid)
{
int rc;
@ -316,10 +284,15 @@ static int decode_sfu_inode(struct inode *inode, __u64 size,
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? */
}
@ -331,42 +304,46 @@ static int decode_sfu_inode(struct inode *inode, __u64 size,
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);
@ -376,8 +353,12 @@ static int decode_sfu_inode(struct inode *inode, __u64 size,
#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */
static int get_sfu_mode(struct inode *inode,
const unsigned char *path,
/*
* 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
@ -387,67 +368,79 @@ static int get_sfu_mode(struct inode *inode,
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);
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));
@ -482,164 +475,83 @@ int cifs_get_inode_info(struct inode **pinode,
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);
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;
@ -753,21 +665,14 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)
return ERR_PTR(-ENOMEM);
xid = GetXid();
if (cifs_sb->tcon->unix_ext) {
if (cifs_sb->tcon->unix_ext)
rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
if (!inode)
return ERR_PTR(-ENOMEM);
} else {
inode = iget_locked(sb, ino);
if (!inode)
return ERR_PTR(-ENOMEM);
if (!(inode->i_state & I_NEW))
return inode;
rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb,
else
rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
xid, NULL);
unlock_new_inode(inode);
}
if (!inode)
return ERR_PTR(-ENOMEM);
if (rc && cifs_sb->tcon->ipc) {
cFYI(1, ("ipc connection - fake read inode"));

View file

@ -112,239 +112,74 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
return dentry;
}
/* 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)
static void
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
{
struct dentry *tmp_dentry = NULL;
struct super_block *sb = file->f_path.dentry->d_sb;
int rc = 0;
fattr->cf_uid = cifs_sb->mnt_uid;
fattr->cf_gid = cifs_sb->mnt_gid;
cFYI(1, ("For %s", qstring->name));
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;
}
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
fattr->cf_dtype = DT_DIR;
} else {
tmp_dentry = d_alloc(file->f_path.dentry, qstring);
if (tmp_dentry == NULL) {
cERROR(1, ("Failed allocating dentry"));
*ptmp_inode = NULL;
return rc;
fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
fattr->cf_dtype = DT_REG;
}
if (CIFS_SB(sb)->tcon->nocase)
tmp_dentry->d_op = &cifs_ci_dentry_ops;
else
tmp_dentry->d_op = &cifs_dentry_ops;
if (fattr->cf_cifsattrs & ATTR_READONLY)
fattr->cf_mode &= ~S_IWUGO;
*ptmp_inode = cifs_new_inode(sb, inum);
if (*ptmp_inode == NULL)
return rc;
rc = 2;
}
tmp_dentry->d_time = jiffies;
*pnew_dentry = tmp_dentry;
return rc;
}
static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
char *buf, unsigned int *pobject_type, int isNewInode)
{
loff_t local_size;
struct timespec local_mtime;
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);
}
/* Linux can not store file creation time unfortunately so ignore it */
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;
}
} 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;
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
}
}
}
/* can not fill in nlink here as in qpathinfo version and Unx search */
if (atomic_read(&cifsInfo->inUse) == 0)
atomic_set(&cifsInfo->inUse, 1);
void
cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
struct cifs_sb_info *cifs_sb)
{
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);
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);
cifs_fill_common_info(fattr, cifs_sb);
}
/* 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);
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;
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;
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);
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;
fattr->cf_cifsattrs = le16_to_cpu(info->Attributes);
fattr->cf_bytes = le32_to_cpu(info->AllocationSize);
fattr->cf_eof = le32_to_cpu(info->DataSize);
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);
}
cifs_fill_common_info(fattr, cifs_sb);
}
/* BB eventually need to add the following helper function to
@ -846,11 +681,10 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
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;
@ -870,71 +704,53 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
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) {
if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
cifs_unix_basic_to_fattr(&fattr,
&((FILE_UNIX_INFO *) pfindEntry)->basic,
cifs_sb);
tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring,
&fattr);
obj_type = fattr.cf_dtype;
else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *)
pfindEntry, cifs_sb);
else
cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *)
pfindEntry, cifs_sb);
/* 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);
} else {
if (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)) {
rc = -ENOMEM;
goto out;
}
/* 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_INFO_STANDARD)
fill_in_inode(tmp_inode, 0, pfindEntry, &obj_type, rc);
else
fill_in_inode(tmp_inode, 1, pfindEntry, &obj_type, rc);
/* new inode - needs to be tied to dentry */
if (rc) {
d_instantiate(tmp_dentry, tmp_inode);
if (rc == 2)
d_rehash(tmp_dentry);
}
ino = cifs_uniqueid_to_ino_t(tmp_inode->i_ino);
}
tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
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;
}
out:
dput(tmp_dentry);
return rc;
}