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

@@ -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,9 +270,9 @@ 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,
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;
@@ -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,9 +353,13 @@ 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,
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;
@@ -386,68 +367,80 @@ static int get_sfu_mode(struct inode *inode,
__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));
@@ -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);
/* 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;
@@ -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"));