#include "cifs_debug.h"
#include "cifs_fs_sb.h"
+
+static void cifs_set_ops(struct inode *inode)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFREG:
+ 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)
+ inode->i_fop = &cifs_file_direct_nobrl_ops;
+ else
+ inode->i_fop = &cifs_file_direct_ops;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ inode->i_fop = &cifs_file_nobrl_ops;
+ else { /* not direct, send byte range locks */
+ inode->i_fop = &cifs_file_ops;
+ }
+
+
+ /* check if server can support readpages */
+ if (cifs_sb->tcon->ses->server->maxBuf <
+ PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
+ inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ inode->i_data.a_ops = &cifs_addr_ops;
+ break;
+ case S_IFDIR:
+ inode->i_op = &cifs_dir_inode_ops;
+ inode->i_fop = &cifs_dir_ops;
+ break;
+ case S_IFLNK:
+ inode->i_op = &cifs_symlink_inode_ops;
+ break;
+ default:
+ init_special_inode(inode, inode->i_mode, inode->i_rdev);
+ break;
+ }
+}
+
int cifs_get_inode_info_unix(struct inode **pinode,
const unsigned char *search_path, struct super_block *sb, int xid)
{
MAX_TREE_SIZE + 1) +
strnlen(search_path, MAX_PATHCONF) + 1,
GFP_KERNEL);
- if (tmp_path == NULL) {
+ if (tmp_path == NULL)
return -ENOMEM;
- }
+
/* have to skip first of the double backslash of
UNC name */
strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
inode->i_mode = le64_to_cpu(findData.Permissions);
/* since we set the inode type below we need to mask off
to avoid strange results if bits set above */
- inode->i_mode &= ~S_IFMT;
+ inode->i_mode &= ~S_IFMT;
if (type == UNIX_FILE) {
inode->i_mode |= S_IFREG;
} else if (type == UNIX_SYMLINK) {
cFYI(1, ("Size %ld and blocks %llu",
(unsigned long) inode->i_size,
(unsigned long long)inode->i_blocks));
- if (S_ISREG(inode->i_mode)) {
- cFYI(1, ("File inode"));
- 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)
- inode->i_fop =
- &cifs_file_direct_nobrl_ops;
- else
- inode->i_fop = &cifs_file_direct_ops;
- } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
- inode->i_fop = &cifs_file_nobrl_ops;
- else /* not direct, send byte range locks */
- inode->i_fop = &cifs_file_ops;
-
- /* check if server can support readpages */
- if (pTcon->ses->server->maxBuf <
- PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
- inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
- else
- inode->i_data.a_ops = &cifs_addr_ops;
- } else if (S_ISDIR(inode->i_mode)) {
- cFYI(1, ("Directory inode"));
- inode->i_op = &cifs_dir_inode_ops;
- inode->i_fop = &cifs_dir_ops;
- } else if (S_ISLNK(inode->i_mode)) {
- cFYI(1, ("Symbolic Link inode"));
- inode->i_op = &cifs_symlink_inode_ops;
- /* tmp_inode->i_fop = */ /* do not need to set to anything */
- } else {
- cFYI(1, ("Init special inode"));
- init_special_inode(inode, inode->i_mode,
- inode->i_rdev);
- }
+
+ cifs_set_ops(inode);
}
return rc;
}
#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */
-static int get_sfu_uid_mode(struct inode *inode,
+static int get_sfu_mode(struct inode *inode,
const unsigned char *path,
struct cifs_sb_info *cifs_sb, int xid)
{
if (decode_sfu_inode(inode,
le64_to_cpu(pfindData->EndOfFile),
search_path,
- cifs_sb, xid)) {
+ cifs_sb, xid))
cFYI(1, ("Unrecognized sfu inode type"));
- }
+
cFYI(1, ("sfu mode 0%o", inode->i_mode));
} else {
inode->i_mode |= S_IFREG;
}
spin_lock(&inode->i_lock);
- if (is_size_safe_to_change(cifsInfo, le64_to_cpu(pfindData->EndOfFile))) {
+ if (is_size_safe_to_change(cifsInfo,
+ le64_to_cpu(pfindData->EndOfFile))) {
/* can not safely shrink the file size here if the
client is writing to it due to potential races */
i_size_write(inode, le64_to_cpu(pfindData->EndOfFile));
/* 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(inode, search_path);
+ }
+#endif
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
- /* fill in uid, gid, mode from server ACL */
- /* BB FIXME this should also take into account the
- * default uid specified on mount if present */
- get_sfu_uid_mode(inode, search_path, cifs_sb, xid);
+ /* fill in remaining high mode bits e.g. SUID, VTX */
+ get_sfu_mode(inode, search_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;
atomic_set(&cifsInfo->inUse, 1);
}
- if (S_ISREG(inode->i_mode)) {
- cFYI(1, ("File inode"));
- 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)
- inode->i_fop =
- &cifs_file_direct_nobrl_ops;
- else
- inode->i_fop = &cifs_file_direct_ops;
- } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
- inode->i_fop = &cifs_file_nobrl_ops;
- else /* not direct, send byte range locks */
- inode->i_fop = &cifs_file_ops;
-
- if (pTcon->ses->server->maxBuf <
- PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
- inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
- else
- inode->i_data.a_ops = &cifs_addr_ops;
- } else if (S_ISDIR(inode->i_mode)) {
- cFYI(1, ("Directory inode"));
- inode->i_op = &cifs_dir_inode_ops;
- inode->i_fop = &cifs_dir_ops;
- } else if (S_ISLNK(inode->i_mode)) {
- cFYI(1, ("Symbolic Link inode"));
- inode->i_op = &cifs_symlink_inode_ops;
- } else {
- init_special_inode(inode, inode->i_mode,
- inode->i_rdev);
- }
+ cifs_set_ops(inode);
}
kfree(buf);
return rc;
}
+static const struct inode_operations cifs_ipc_inode_ops = {
+ .lookup = cifs_lookup,
+};
+
/* gets root inode */
void cifs_read_inode(struct inode *inode)
{
- int xid;
+ int xid, rc;
struct cifs_sb_info *cifs_sb;
cifs_sb = CIFS_SB(inode->i_sb);
xid = GetXid();
if (cifs_sb->tcon->unix_ext)
- cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid);
+ rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid);
else
- cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid);
+ rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid);
+ if (rc && cifs_sb->tcon->ipc) {
+ cFYI(1, ("ipc connection - fake read inode"));
+ inode->i_mode |= S_IFDIR;
+ inode->i_nlink = 2;
+ inode->i_op = &cifs_ipc_inode_ops;
+ inode->i_fop = &simple_dir_operations;
+ inode->i_uid = cifs_sb->mnt_uid;
+ inode->i_gid = cifs_sb->mnt_gid;
+ }
+
/* can not call macro FreeXid here since in a void func */
_FreeXid(xid);
}
cFYI(1, ("unknown inode type %d", type));
}
-#ifdef CONFIG_CIFS_DEBUG2
- cFYI(1, ("object type: %d", type));
-#endif
+ cFYI(DBG2, ("object type: %d", type));
tmp_inode->i_uid = le64_to_cpu(pData->Uid);
tmp_inode->i_gid = le64_to_cpu(pData->Gid);
tmp_inode->i_nlink = le64_to_cpu(pData->Nlinks);
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(pTcon->fsUnixInfo.Capability))) {
u32 oplock = 0;
- FILE_UNIX_BASIC_INFO * pInfo =
+ FILE_UNIX_BASIC_INFO *pInfo =
kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
if (pInfo == NULL) {
rc = -ENOMEM;
goto mkdir_out;
}
+ mode &= ~current->fs->umask;
rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT,
mode, NULL /* netfid */, pInfo, &oplock,
full_path, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
- if (rc) {
+ if (rc == -EOPNOTSUPP) {
+ kfree(pInfo);
+ goto mkdir_retry_old;
+ } else if (rc) {
cFYI(1, ("posix mkdir returned 0x%x", rc));
d_drop(direntry);
} else {
int obj_type;
- if (pInfo->Type == -1) /* no return info - go query */
+ if (pInfo->Type == cpu_to_le32(-1)) {
+ /* no return info, go query for it */
+ kfree(pInfo);
goto mkdir_get_info;
+ }
/*BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if need
to set uid/gid */
inc_nlink(inode);
direntry->d_op = &cifs_dentry_ops;
newinode = new_inode(inode->i_sb);
- if (newinode == NULL)
+ if (newinode == NULL) {
+ kfree(pInfo);
goto mkdir_get_info;
+ }
/* Is an i_ino of zero legal? */
/* Are there sanity checks we can use to ensure that
the server is really filling in that field? */
kfree(pInfo);
goto mkdir_out;
}
-
+mkdir_retry_old:
/* BB add setting the equivalent of mode via CreateX w/ACLs */
rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
} /* if we can not get memory just leave rc as EEXIST */
}
- if (rc) {
+ if (rc)
cFYI(1, ("rename rc %d", rc));
- }
if ((rc == -EIO) || (rc == -EEXIST)) {
int oplock = FALSE;
int cifs_revalidate(struct dentry *direntry)
{
int xid;
- int rc = 0;
+ int rc = 0, wbrc = 0;
char *full_path;
struct cifs_sb_info *cifs_sb;
struct cifsInodeInfo *cifsInode;
if (direntry->d_inode->i_mapping) {
/* do we need to lock inode until after invalidate completes
below? */
- filemap_fdatawrite(direntry->d_inode->i_mapping);
+ wbrc = filemap_fdatawrite(direntry->d_inode->i_mapping);
+ if (wbrc)
+ CIFS_I(direntry->d_inode)->write_behind_rc = wbrc;
}
if (invalidate_inode) {
/* shrink_dcache not necessary now that cifs dentry ops
shrink_dcache_parent(direntry); */
if (S_ISREG(direntry->d_inode->i_mode)) {
if (direntry->d_inode->i_mapping)
- filemap_fdatawait(direntry->d_inode->i_mapping);
+ wbrc = filemap_fdatawait(direntry->d_inode->i_mapping);
+ if (wbrc)
+ CIFS_I(direntry->d_inode)->write_behind_rc = wbrc;
/* may eventually have to do this for open files too */
if (list_empty(&(cifsInode->openFileList))) {
/* changed on server - flush read ahead pages */
if (!page)
return -ENOMEM;
- zero_user_page(page, offset, PAGE_CACHE_SIZE - offset, KM_USER0);
+ zero_user_segment(page, offset, PAGE_CACHE_SIZE);
unlock_page(page);
page_cache_release(page);
return rc;
}
i_size_write(inode, offset);
spin_unlock(&inode->i_lock);
+ /*
+ * unmap_mapping_range is called twice, first simply for efficiency
+ * so that truncate_inode_pages does fewer single-page unmaps. However
+ * after this first call, and before truncate_inode_pages finishes,
+ * it is possible for private pages to be COWed, which remain after
+ * truncate_inode_pages finishes, hence the second unmap_mapping_range
+ * call must be made for correctness.
+ */
unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
truncate_inode_pages(mapping, offset);
+ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
goto out_truncate;
do_expand:
/* BB check if we need to refresh inode from server now ? BB */
- /* need to flush data before changing file size on server */
- filemap_write_and_wait(direntry->d_inode->i_mapping);
-
if (attrs->ia_valid & ATTR_SIZE) {
+ /*
+ Flush data before changing file size on server. If the
+ flush returns error, store it to report later and continue.
+ BB: This should be smarter. Why bother flushing pages that
+ will be truncated anyway? Also, should we error out here if
+ the flush returns error?
+ */
+ rc = filemap_write_and_wait(direntry->d_inode->i_mapping);
+ if (rc != 0) {
+ CIFS_I(direntry->d_inode)->write_behind_rc = rc;
+ rc = 0;
+ }
+
/* To avoid spurious oplock breaks from server, in the case of
inodes that we already have open, avoid doing path based
setting of file size if we can do it by handle.
atomic_dec(&open_file->wrtPending);
cFYI(1, ("SetFSize for attrs rc = %d", rc));
if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
- int bytes_written;
+ unsigned int bytes_written;
rc = CIFSSMBWrite(xid, pTcon,
nfid, 0, attrs->ia_size,
&bytes_written, NULL, NULL,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
- int bytes_written;
+ unsigned int bytes_written;
rc = CIFSSMBWrite(xid, pTcon,
netfid, 0,
attrs->ia_size,
CIFS_MOUNT_MAP_SPECIAL_CHR);
else if (attrs->ia_valid & ATTR_MODE) {
rc = 0;
- if ((mode & S_IWUGO) == 0) /* not writeable */ {
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+ rc = mode_to_acl(direntry->d_inode, full_path, mode);
+ else if ((mode & S_IWUGO) == 0) {
+#else
+ if ((mode & S_IWUGO) == 0) {
+#endif
+ /* not writeable */
if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
set_dosattr = TRUE;
time_buf.Attributes =
if (time_buf.Attributes == 0)
time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL);
}
- /* BB to be implemented -
- via Windows security descriptors or streams */
- /* CIFSSMBWinSetPerms(xid, pTcon, full_path, mode, uid, gid,
- cifs_sb->local_nls); */
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+ mode_to_acl(direntry->d_inode, full_path, mode);
+#endif
}
if (attrs->ia_valid & ATTR_ATIME) {