diff options
Diffstat (limited to 'fs/cifs/inode.c')
-rw-r--r-- | fs/cifs/inode.c | 431 |
1 files changed, 292 insertions, 139 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 6f0683c..39869c3 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -29,6 +29,7 @@ #include "cifsproto.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" +#include "fscache.h" static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) @@ -51,7 +52,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) /* check if server can support readpages */ - if (cifs_sb->tcon->ses->server->maxBuf < + if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else @@ -287,8 +288,8 @@ int cifs_get_file_info_unix(struct file *filp) struct cifs_fattr fattr; struct inode *inode = filp->f_path.dentry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; - struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; + struct cifsFileInfo *cfile = filp->private_data; + struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink); xid = GetXid(); rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data); @@ -312,15 +313,21 @@ int cifs_get_inode_info_unix(struct inode **pinode, FILE_UNIX_BASIC_INFO find_data; struct cifs_fattr fattr; struct cifsTconInfo *tcon; + struct tcon_link *tlink; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - tcon = cifs_sb->tcon; cFYI(1, "Getting info on %s", full_path); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + /* could have done a find first instead but this returns more info */ rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); if (!rc) { cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); @@ -331,6 +338,13 @@ int cifs_get_inode_info_unix(struct inode **pinode, return rc; } + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + int tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid); + if (tmprc) + cFYI(1, "CIFSCheckMFSymlink: %d", tmprc); + } + if (*pinode == NULL) { /* get new inode */ cifs_fill_uniqueid(sb, &fattr); @@ -352,7 +366,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, int rc; int oplock = 0; __u16 netfid; - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct tcon_link *tlink; + struct cifsTconInfo *tcon; char buf[24]; unsigned int bytes_read; char *pbuf; @@ -371,7 +386,12 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, return -EINVAL; /* EOPNOTSUPP? */ } - rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, GENERIC_READ, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & @@ -379,7 +399,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, if (rc == 0) { int buf_type = CIFS_NO_BUFFER; /* Read header */ - rc = CIFSSMBRead(xid, pTcon, netfid, + rc = CIFSSMBRead(xid, tcon, netfid, 24 /* length */, 0 /* offset */, &bytes_read, &pbuf, &buf_type); if ((rc == 0) && (bytes_read >= 8)) { @@ -421,8 +441,9 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, fattr->cf_dtype = DT_REG; rc = -EOPNOTSUPP; /* or some unknown SFU type */ } - CIFSSMBClose(xid, pTcon, netfid); + CIFSSMBClose(xid, tcon, netfid); } + cifs_put_tlink(tlink); return rc; } @@ -440,11 +461,19 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, ssize_t rc; char ea_value[4]; __u32 mode; + struct tcon_link *tlink; + struct cifsTconInfo *tcon; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); - rc = CIFSSMBQAllEAs(xid, cifs_sb->tcon, path, "SETFILEBITS", + rc = CIFSSMBQAllEAs(xid, tcon, path, "SETFILEBITS", ea_value, 4 /* size of buf */, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); if (rc < 0) return (int)rc; else if (rc > 3) { @@ -467,6 +496,8 @@ static void cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, struct cifs_sb_info *cifs_sb, bool adjust_tz) { + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); + memset(fattr, 0, sizeof(*fattr)); fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); if (info->DeletePending) @@ -481,8 +512,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, 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_ctime.tv_sec += tcon->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; } fattr->cf_eof = le64_to_cpu(info->EndOfFile); @@ -514,8 +545,8 @@ int cifs_get_file_info(struct file *filp) struct cifs_fattr fattr; struct inode *inode = filp->f_path.dentry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; - struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; + struct cifsFileInfo *cfile = filp->private_data; + struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink); xid = GetXid(); rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data); @@ -553,26 +584,33 @@ int cifs_get_inode_info(struct inode **pinode, { int rc = 0, tmprc; struct cifsTconInfo *pTcon; + struct tcon_link *tlink; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); char *buf = NULL; bool adjustTZ = false; struct cifs_fattr fattr; - pTcon = cifs_sb->tcon; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + cFYI(1, "Getting info on %s", full_path); if ((pfindData == NULL) && (*pinode != NULL)) { if (CIFS_I(*pinode)->clientCanCacheRead) { cFYI(1, "No need to revalidate cached inode sizes"); - return rc; + goto cgii_exit; } } /* if file info not passed in then get it from server */ if (pfindData == NULL) { buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; + if (buf == NULL) { + rc = -ENOMEM; + goto cgii_exit; + } pfindData = (FILE_ALL_INFO *)buf; /* could do find first instead but this returns more info */ @@ -660,6 +698,13 @@ int cifs_get_inode_info(struct inode **pinode, if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid); + if (tmprc) + cFYI(1, "CIFSCheckMFSymlink: %d", tmprc); + } + if (!*pinode) { *pinode = cifs_iget(sb, &fattr); if (!*pinode) @@ -670,6 +715,7 @@ int cifs_get_inode_info(struct inode **pinode, cgii_exit: kfree(buf); + cifs_put_tlink(tlink); return rc; } @@ -682,6 +728,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) int pplen = cifs_sb->prepathlen; int dfsplen; char *full_path = NULL; + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); /* if no prefix path, simply set path to the root of share to "" */ if (pplen == 0) { @@ -691,8 +738,8 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) return full_path; } - if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) - dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1); + if (tcon->Flags & SMB_SHARE_IS_IN_DFS) + dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1); else dfsplen = 0; @@ -701,7 +748,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) return full_path; if (dfsplen) { - strncpy(full_path, cifs_sb->tcon->treeName, dfsplen); + strncpy(full_path, tcon->treeName, dfsplen); /* switch slash direction in prepath depending on whether * windows or posix style path names */ @@ -723,18 +770,17 @@ cifs_find_inode(struct inode *inode, void *opaque) { struct cifs_fattr *fattr = (struct cifs_fattr *) opaque; + /* don't match inode with different uniqueid */ if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) return 0; - /* - * uh oh -- it's a directory. We can't use it since hardlinked dirs are - * verboten. Disable serverino and return it as if it were found, the - * caller can discard it, generate a uniqueid and retry the find - */ - if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) { + /* don't match inode of different type */ + if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT)) + return 0; + + /* if it's not a directory or has no dentries, then flag it */ + if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; - cifs_autodisable_serverino(CIFS_SB(inode->i_sb)); - } return 1; } @@ -748,6 +794,27 @@ cifs_init_inode(struct inode *inode, void *opaque) return 0; } +/* + * walk dentry list for an inode and report whether it has aliases that + * are hashed. We use this to determine if a directory inode can actually + * be used. + */ +static bool +inode_has_hashed_dentries(struct inode *inode) +{ + struct dentry *dentry; + + spin_lock(&dcache_lock); + list_for_each_entry(dentry, &inode->i_dentry, d_alias) { + if (!d_unhashed(dentry) || IS_ROOT(dentry)) { + spin_unlock(&dcache_lock); + return true; + } + } + spin_unlock(&dcache_lock); + return false; +} + /* Given fattrs, get a corresponding inode */ struct inode * cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) @@ -763,12 +830,16 @@ retry_iget5_locked: inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); if (inode) { - /* was there a problematic inode number collision? */ + /* was there a potentially problematic inode collision? */ if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { - iput(inode); - fattr->cf_uniqueid = iunique(sb, ROOT_I); fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; - goto retry_iget5_locked; + + if (inode_has_hashed_dentries(inode)) { + cifs_autodisable_serverino(CIFS_SB(sb)); + iput(inode); + fattr->cf_uniqueid = iunique(sb, ROOT_I); + goto retry_iget5_locked; + } } cifs_fattr_to_inode(inode, fattr); @@ -776,6 +847,12 @@ retry_iget5_locked: inode->i_flags |= S_NOATIME | S_NOCMTIME; if (inode->i_state & I_NEW) { inode->i_ino = hash; + if (S_ISREG(inode->i_mode)) + inode->i_data.backing_dev_info = sb->s_bdi; +#ifdef CONFIG_CIFS_FSCACHE + /* initialize per-inode cache cookie pointer */ + CIFS_I(inode)->fscache = NULL; +#endif unlock_new_inode(inode); } } @@ -787,27 +864,32 @@ retry_iget5_locked: struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) { int xid; - struct cifs_sb_info *cifs_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct inode *inode = NULL; long rc; char *full_path; + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_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) + if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, xid, NULL); if (!inode) - return ERR_PTR(-ENOMEM); + return ERR_PTR(rc); + +#ifdef CONFIG_CIFS_FSCACHE + /* populate tcon->resource_id */ + tcon->resource_id = CIFS_I(inode)->uniqueid; +#endif - if (rc && cifs_sb->tcon->ipc) { + if (rc && tcon->ipc) { cFYI(1, "ipc connection - fake read inode"); inode->i_mode |= S_IFDIR; inode->i_nlink = 2; @@ -843,7 +925,8 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, struct cifsFileInfo *open_file; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct tcon_link *tlink = NULL; + struct cifsTconInfo *pTcon; FILE_BASIC_INFO info_buf; if (attrs == NULL) @@ -882,13 +965,22 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, /* * If the file is already open for write, just use that fileid */ - open_file = find_writable_file(cifsInode); + open_file = find_writable_file(cifsInode, true); if (open_file) { netfid = open_file->netfid; netpid = open_file->pid; + pTcon = tlink_tcon(open_file->tlink); goto set_via_filehandle; } + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + tlink = NULL; + goto out; + } + pTcon = tlink_tcon(tlink); + /* * NT4 apparently returns success on this call, but it doesn't * really work. @@ -932,6 +1024,8 @@ set_via_filehandle: else cifsFileInfo_put(open_file); out: + if (tlink != NULL) + cifs_put_tlink(tlink); return rc; } @@ -949,10 +1043,16 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid) struct inode *inode = dentry->d_inode; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; + struct tcon_link *tlink; + struct cifsTconInfo *tcon; __u32 dosattr, origattr; FILE_BASIC_INFO *info_buf = NULL; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb->local_nls, @@ -1021,6 +1121,7 @@ out_close: CIFSSMBClose(xid, tcon, netfid); out: kfree(info_buf); + cifs_put_tlink(tlink); return rc; /* @@ -1060,12 +1161,18 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) struct cifsInodeInfo *cifs_inode; struct super_block *sb = dir->i_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; + struct tcon_link *tlink; + struct cifsTconInfo *tcon; struct iattr *attrs = NULL; __u32 dosattr = 0, origattr = 0; cFYI(1, "cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + xid = GetXid(); /* Unlink can be called from rename so we can not take the @@ -1073,8 +1180,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto unlink_out; } if ((tcon->ses->capabilities & CAP_UNIX) && @@ -1140,10 +1246,11 @@ out_reval: dir->i_ctime = dir->i_mtime = current_fs_time(sb); cifs_inode = CIFS_I(dir); CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ - +unlink_out: kfree(full_path); kfree(attrs); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -1152,6 +1259,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) int rc = 0, tmprc; int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; @@ -1159,16 +1267,18 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) cFYI(1, "In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode); - xid = GetXid(); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = GetXid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto mkdir_out; } if ((pTcon->ses->capabilities & CAP_UNIX) && @@ -1326,6 +1436,7 @@ mkdir_get_info: mkdir_out: kfree(full_path); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -1334,6 +1445,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) int rc = 0; int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; char *full_path = NULL; struct cifsInodeInfo *cifsInode; @@ -1342,18 +1454,23 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) xid = GetXid(); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; - full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto rmdir_exit; + } + + cifs_sb = CIFS_SB(inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto rmdir_exit; } + pTcon = tlink_tcon(tlink); rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); if (!rc) { drop_nlink(inode); @@ -1374,6 +1491,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb); +rmdir_exit: kfree(full_path); FreeXid(xid); return rc; @@ -1384,10 +1502,16 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, struct dentry *to_dentry, const char *toPath) { struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct tcon_link *tlink; + struct cifsTconInfo *pTcon; __u16 srcfid; int oplock, rc; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + /* try path-based rename first */ rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & @@ -1399,11 +1523,11 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, * rename by filehandle to various Windows servers. */ if (rc == 0 || rc != -ETXTBSY) - return rc; + goto do_rename_exit; /* open-file renames don't work across directories */ if (to_dentry->d_parent != from_dentry->d_parent) - return rc; + goto do_rename_exit; /* open the file to be renamed -- we need DELETE perms */ rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE, @@ -1419,7 +1543,8 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, CIFSSMBClose(xid, pTcon, srcfid); } - +do_rename_exit: + cifs_put_tlink(tlink); return rc; } @@ -1428,29 +1553,22 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, { char *fromName = NULL; char *toName = NULL; - struct cifs_sb_info *cifs_sb_source; - struct cifs_sb_info *cifs_sb_target; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *tcon; FILE_UNIX_BASIC_INFO *info_buf_source = NULL; FILE_UNIX_BASIC_INFO *info_buf_target; int xid, rc, tmprc; - cifs_sb_target = CIFS_SB(target_dir->i_sb); - cifs_sb_source = CIFS_SB(source_dir->i_sb); - tcon = cifs_sb_source->tcon; + cifs_sb = CIFS_SB(source_dir->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); xid = GetXid(); /* - * BB: this might be allowed if same server, but different share. - * Consider adding support for this - */ - if (tcon != cifs_sb_target->tcon) { - rc = -EXDEV; - goto cifs_rename_exit; - } - - /* * we already have the rename sem so we do not need to * grab it again here to protect the path integrity */ @@ -1485,17 +1603,16 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, info_buf_target = info_buf_source + 1; tmprc = CIFSSMBUnixQPathInfo(xid, tcon, fromName, info_buf_source, - cifs_sb_source->local_nls, - cifs_sb_source->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (tmprc != 0) goto unlink_target; - tmprc = CIFSSMBUnixQPathInfo(xid, tcon, - toName, info_buf_target, - cifs_sb_target->local_nls, - /* remap based on source sb */ - cifs_sb_source->mnt_cifs_flags & + tmprc = CIFSSMBUnixQPathInfo(xid, tcon, toName, + info_buf_target, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (tmprc == 0 && (info_buf_source->UniqueId == @@ -1523,6 +1640,7 @@ cifs_rename_exit: kfree(fromName); kfree(toName); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -1564,21 +1682,22 @@ cifs_invalidate_mapping(struct inode *inode) /* write back any cached data */ if (inode->i_mapping && inode->i_mapping->nrpages != 0) { rc = filemap_write_and_wait(inode->i_mapping); - if (rc) - cifs_i->write_behind_rc = rc; + mapping_set_error(inode->i_mapping, rc); } invalidate_remote_inode(inode); + cifs_fscache_reset_inode_cookie(inode); } int cifs_revalidate_file(struct file *filp) { int rc = 0; struct inode *inode = filp->f_path.dentry->d_inode; + struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; if (!cifs_inode_needs_reval(inode)) goto check_inval; - if (CIFS_SB(inode->i_sb)->tcon->unix_ext) + if (tlink_tcon(cfile->tlink)->unix_ext) rc = cifs_get_file_info_unix(filp); else rc = cifs_get_file_info(filp); @@ -1619,7 +1738,7 @@ int cifs_revalidate_dentry(struct dentry *dentry) "jiffies %ld", full_path, inode, inode->i_count.counter, dentry, dentry->d_time, jiffies); - if (CIFS_SB(sb)->tcon->unix_ext) + if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, @@ -1635,13 +1754,29 @@ check_inval: } int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) + struct kstat *stat) { + struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); int err = cifs_revalidate_dentry(dentry); + if (!err) { generic_fillattr(dentry->d_inode, stat); stat->blksize = CIFS_MAX_MSGSIZE; stat->ino = CIFS_I(dentry->d_inode)->uniqueid; + + /* + * If on a multiuser mount without unix extensions, and the + * admin hasn't overridden them, set the ownership to the + * fsuid/fsgid of the current process. + */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && + !tcon->unix_ext) { + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) + stat->uid = current_fsuid(); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) + stat->gid = current_fsgid(); + } } return err; } @@ -1663,26 +1798,16 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from) return rc; } -static int cifs_vmtruncate(struct inode *inode, loff_t offset) +static void cifs_setsize(struct inode *inode, loff_t offset) { loff_t oldsize; - int err; spin_lock(&inode->i_lock); - err = inode_newsize_ok(inode, offset); - if (err) { - spin_unlock(&inode->i_lock); - goto out; - } - oldsize = inode->i_size; i_size_write(inode, offset); spin_unlock(&inode->i_lock); + truncate_pagecache(inode, oldsize, offset); - if (inode->i_op->truncate) - inode->i_op->truncate(inode); -out: - return err; } static int @@ -1693,7 +1818,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, struct cifsFileInfo *open_file; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct tcon_link *tlink = NULL; + struct cifsTconInfo *pTcon = NULL; /* * To avoid spurious oplock breaks from server, in the case of @@ -1704,10 +1830,11 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, * writebehind data than the SMB timeout for the SetPathInfo * request would allow */ - open_file = find_writable_file(cifsInode); + open_file = find_writable_file(cifsInode, true); if (open_file) { __u16 nfid = open_file->netfid; __u32 npid = open_file->pid; + pTcon = tlink_tcon(open_file->tlink); rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, npid, false); cifsFileInfo_put(open_file); @@ -1722,6 +1849,13 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, rc = -EINVAL; if (rc != 0) { + if (pTcon == NULL) { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + } + /* Set file size by pathname rather than by handle either because no valid, writeable file handle for it was found or because there was an error setting @@ -1751,11 +1885,13 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, CIFSSMBClose(xid, pTcon, netfid); } } + if (tlink) + cifs_put_tlink(tlink); } if (rc == 0) { cifsInode->server_eof = attrs->ia_size; - rc = cifs_vmtruncate(inode, attrs->ia_size); + cifs_setsize(inode, attrs->ia_size); cifs_truncate_page(inode->i_mapping, inode->i_size); } @@ -1771,7 +1907,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) struct inode *inode = direntry->d_inode; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct tcon_link *tlink; + struct cifsTconInfo *pTcon; struct cifs_unix_set_info_args *args = NULL; struct cifsFileInfo *open_file; @@ -1780,14 +1917,12 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) xid = GetXid(); - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { - /* check if we have permission to change attrs */ - rc = inode_change_ok(inode, attrs); - if (rc < 0) - goto out; - else - rc = 0; - } + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + attrs->ia_valid |= ATTR_FORCE; + + rc = inode_change_ok(inode, attrs); + if (rc < 0) + goto out; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -1807,10 +1942,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) * the flush returns error? */ rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) { - cifsInode->write_behind_rc = rc; - rc = 0; - } + mapping_set_error(inode->i_mapping, rc); + rc = 0; if (attrs->ia_valid & ATTR_SIZE) { rc = cifs_set_file_size(inode, attrs, xid, full_path); @@ -1860,31 +1993,45 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) args->ctime = NO_CHANGE_64; args->device = 0; - open_file = find_writable_file(cifsInode); + open_file = find_writable_file(cifsInode, true); if (open_file) { u16 nfid = open_file->netfid; u32 npid = open_file->pid; + pTcon = tlink_tcon(open_file->tlink); rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); cifsFileInfo_put(open_file); } else { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto out; + } + pTcon = tlink_tcon(tlink); rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); } - if (!rc) { - rc = inode_setattr(inode, attrs); + if (rc) + goto out; - /* force revalidate when any of these times are set since some - of the fs types (eg ext3, fat) do not have fine enough - time granularity to match protocol, and we do not have a - a way (yet) to query the server fs's time granularity (and - whether it rounds times down). - */ - if (!rc && (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME))) - cifsInode->time = 0; - } + if ((attrs->ia_valid & ATTR_SIZE) && + attrs->ia_size != i_size_read(inode)) + truncate_setsize(inode, attrs->ia_size); + + setattr_copy(inode, attrs); + mark_inode_dirty(inode); + + /* force revalidate when any of these times are set since some + of the fs types (eg ext3, fat) do not have fine enough + time granularity to match protocol, and we do not have a + a way (yet) to query the server fs's time granularity (and + whether it rounds times down). + */ + if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME)) + cifsInode->time = 0; out: kfree(args); kfree(full_path); @@ -1909,14 +2056,13 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) cFYI(1, "setattr on file %s attrs->iavalid 0x%x", direntry->d_name.name, attrs->ia_valid); - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { - /* check if we have permission to change attrs */ - rc = inode_change_ok(inode, attrs); - if (rc < 0) { - FreeXid(xid); - return rc; - } else - rc = 0; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + attrs->ia_valid |= ATTR_FORCE; + + rc = inode_change_ok(inode, attrs); + if (rc < 0) { + FreeXid(xid); + return rc; } full_path = build_path_from_dentry(direntry); @@ -1938,10 +2084,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) * the flush returns error? */ rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) { - cifsInode->write_behind_rc = rc; - rc = 0; - } + mapping_set_error(inode->i_mapping, rc); + rc = 0; if (attrs->ia_valid & ATTR_SIZE) { rc = cifs_set_file_size(inode, attrs, xid, full_path); @@ -2024,8 +2168,17 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) /* do not need local check to inode_check_ok since the server does that */ - if (!rc) - rc = inode_setattr(inode, attrs); + if (rc) + goto cifs_setattr_exit; + + if ((attrs->ia_valid & ATTR_SIZE) && + attrs->ia_size != i_size_read(inode)) + truncate_setsize(inode, attrs->ia_size); + + setattr_copy(inode, attrs); + mark_inode_dirty(inode); + return 0; + cifs_setattr_exit: kfree(full_path); FreeXid(xid); @@ -2037,7 +2190,7 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs) { struct inode *inode = direntry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon = cifs_sb_master_tcon(cifs_sb); if (pTcon->unix_ext) return cifs_setattr_unix(direntry, attrs); |