if (inode->i_state & I_NEW) {
cifs_dbg(FYI, "%s: inode %llu is new\n",
__func__, cifs_i->uniqueid); return;
}
/* don't bother with revalidation if we have an oplock */ if (CIFS_CACHE_READ(cifs_i)) {
cifs_dbg(FYI, "%s: inode %llu is oplocked\n",
__func__, cifs_i->uniqueid); return;
}
/* revalidate if mtime or size have changed */
fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode);
mtime = inode_get_mtime(inode); if (timespec64_equal(&mtime, &fattr->cf_mtime) &&
cifs_i->netfs.remote_i_size == fattr->cf_eof) {
cifs_dbg(FYI, "%s: inode %llu is unchanged\n",
__func__, cifs_i->uniqueid); return;
}
/* * copy nlink to the inode, unless it wasn't provided. Provide * sane values if we don't have an existing one and none was provided
*/ staticvoid
cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
{ /* * if we're in a situation where we can't trust what we * got from the server (readdir, some non-unix cases) * fake reasonable values
*/ if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { /* only provide fake values on a new inode */ if (inode->i_state & I_NEW) { if (fattr->cf_cifsattrs & ATTR_DIRECTORY)
set_nlink(inode, 2); else
set_nlink(inode, 1);
} return;
}
/* we trust the server, so update it */
set_nlink(inode, fattr->cf_nlink);
}
/* populate an inode with info from a cifs_fattr struct */ int
cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, bool from_readdir)
{ struct cifsInodeInfo *cifs_i = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
if (!(inode->i_state & I_NEW) &&
unlikely(inode_wrong_type(inode, fattr->cf_mode))) {
CIFS_I(inode)->time = 0; /* force reval */ return -ESTALE;
} if (inode->i_state & I_NEW)
CIFS_I(inode)->netfs.zero_point = fattr->cf_eof;
cifs_revalidate_cache(inode, fattr);
spin_lock(&inode->i_lock);
fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode);
fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode);
fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode); /* we do not want atime to be less than mtime, it broke some apps */ if (timespec64_compare(&fattr->cf_atime, &fattr->cf_mtime) < 0)
inode_set_atime_to_ts(inode, fattr->cf_mtime); else
inode_set_atime_to_ts(inode, fattr->cf_atime);
inode_set_mtime_to_ts(inode, fattr->cf_mtime);
inode_set_ctime_to_ts(inode, fattr->cf_ctime);
inode->i_rdev = fattr->cf_rdev;
cifs_nlink_fattr_to_inode(inode, fattr);
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;
if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); else
clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags);
cifs_i->netfs.remote_i_size = fattr->cf_eof; /* * Can't safely change the file size here if the client is writing to * it due to potential races.
*/ if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) {
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 + fattr->cf_bytes) >> 9;
}
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) return;
fattr->cf_uniqueid = iunique(sb, ROOT_I);
}
/* 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); /* old POSIX extensions don't get create time */
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;
cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type)); break;
}
fattr->cf_uid = cifs_sb->ctx->linux_uid; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) {
u64 id = le64_to_cpu(info->Uid); if (id < ((uid_t)-1)) {
kuid_t uid = make_kuid(&init_user_ns, id); if (uid_valid(uid))
fattr->cf_uid = uid;
}
}
fattr->cf_gid = cifs_sb->ctx->linux_gid; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) {
u64 id = le64_to_cpu(info->Gid); if (id < ((gid_t)-1)) {
kgid_t gid = make_kgid(&init_user_ns, id); if (gid_valid(gid))
fattr->cf_gid = gid;
}
}
fattr->cf_nlink = le64_to_cpu(info->Nlinks);
}
/* * Fill a cifs_fattr struct with fake inode info. * * 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 or reparse mount point).
*/ staticvoid cifs_create_junction_fattr(struct cifs_fattr *fattr, struct super_block *sb)
{ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
/* 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_remap(cifs_sb));
cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc);
cifs_put_tlink(tlink);
wire_type = (wire & POSIX_FILETYPE_MASK) >> POSIX_FILETYPE_SHIFT; /* older servers do not set POSIX file type in the mode field in the response */ if ((wire_type == 0) && is_dir)
mode = wire_perms_to_posix(wire) | S_IFDIR; else
mode = (wire_perms_to_posix(wire) | wire_filetype_to_posix(wire_type)); return (umode_t)mode;
}
/* Fill a cifs_fattr struct with info from POSIX info struct */ staticvoid smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, struct super_block *sb)
{ struct smb311_posix_qinfo *info = &data->posix_fi; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
memset(fattr, 0, sizeof(*fattr));
/* no fattr->flags to set */
fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
fattr->cf_uniqueid = le64_to_cpu(info->Inode);
if (info->LastAccessTime)
fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); else
ktime_get_coarse_real_ts64(&fattr->cf_atime);
if (!server->ops->query_file_info) {
free_dentry_path(page); return -ENOSYS;
}
xid = get_xid();
rc = server->ops->query_file_info(xid, tcon, cfile, &data); switch (rc) { case 0: /* TODO: add support to query reparse tag */
data.adjust_tz = false; if (data.symlink_target) {
data.reparse_point = true;
data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
}
path = build_path_from_dentry(dentry, page); if (IS_ERR(path)) {
rc = PTR_ERR(path); goto cgfi_exit;
}
cifs_open_info_to_fattr(&fattr, &data, inode->i_sb); if (fattr.cf_flags & CIFS_FATTR_DELETE_PENDING)
cifs_mark_open_handles_for_deleted_file(inode, path); break; case -EREMOTE:
cifs_create_junction_fattr(&fattr, inode->i_sb); break; case -EOPNOTSUPP: case -EINVAL: /* * FIXME: legacy server -- fall back to path-based call? * for now, just skip revalidating and mark inode for * immediate reval.
*/
rc = 0;
CIFS_I(inode)->time = 0; goto cgfi_exit; default: goto cgfi_exit;
}
/* * don't bother with SFU junk here -- just mark inode as needing * revalidation.
*/
fattr.cf_uniqueid = CIFS_I(inode)->uniqueid;
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; /* if filetype is different, return error */
rc = cifs_fattr_to_inode(inode, &fattr, false);
cgfi_exit:
cifs_free_open_info(&data);
free_dentry_path(page);
free_xid(xid); return rc;
}
/* Simple function to return a 64 bit hash of string. Rarely called */ static __u64 simple_hashstr(constchar *str)
{ const __u64 hash_mult = 1125899906842597ULL; /* a big enough prime */
__u64 hash = 0;
while (*str)
hash = (hash + (__u64) *str++) * hash_mult;
return hash;
}
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY /** * cifs_backup_query_path_info - SMB1 fallback code to get ino * * Fallback code to get file metadata when we don't have access to * full_path (EACCES) and have backup creds. * * @xid: transaction id used to identify original request in logs * @tcon: information about the server share we have mounted * @sb: the superblock stores info such as disk space available * @full_path: name of the file we are getting the metadata for * @resp_buf: will be set to cifs resp buf and needs to be freed with * cifs_buf_release() when done with @data * @data: will be set to search info result buffer
*/ staticint
cifs_backup_query_path_info(int xid, struct cifs_tcon *tcon, struct super_block *sb, constchar *full_path, void **resp_buf,
FILE_ALL_INFO **data)
{ struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_search_info info = {0};
u16 flags; int rc;
*resp_buf = NULL;
info.endOfSearch = false; if (tcon->unix_ext)
info.info_level = SMB_FIND_FILE_UNIX; elseif ((tcon->ses->capabilities &
tcon->ses->server->vals->cap_nt_find) == 0)
info.info_level = SMB_FIND_FILE_INFO_STANDARD; elseif (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; else/* no srvino useful for fallback to some netapp */
info.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { if (*inode)
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; else
fattr->cf_uniqueid = iunique(sb, ROOT_I); return;
}
/* * If we have an inode pass a NULL tcon to ensure we don't * make a round trip to the server. This only works for SMB2+.
*/
rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path,
&fattr->cf_uniqueid, data); if (rc) { /* * If that fails reuse existing ino or generate one * and disable server ones
*/ if (*inode)
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; else {
fattr->cf_uniqueid = iunique(sb, ROOT_I);
cifs_autodisable_serverino(cifs_sb);
} return;
}
/* If no errors, check for zero root inode (invalid) */ if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) {
cifs_dbg(FYI, "Invalid (0) inodenum\n"); if (*inode) { /* reuse */
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
} else { /* make an ino by hashing the UNC */
fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO;
fattr->cf_uniqueid = simple_hashstr(tcon->tree_name);
}
}
}
rc = -EOPNOTSUPP;
data->reparse.tag = tag; if (!data->reparse.tag) { if (server->ops->query_symlink) {
rc = server->ops->query_symlink(xid, tcon,
cifs_sb, full_path,
&data->symlink_target);
} if (rc == -EOPNOTSUPP)
data->reparse.tag = IO_REPARSE_TAG_INTERNAL;
}
switch (data->reparse.tag) { case 0: /* SMB1 symlink */ break; case IO_REPARSE_TAG_INTERNAL:
rc = 0; if (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY) {
cifs_create_junction_fattr(fattr, sb); goto out;
} break; default: /* Check for cached reparse point data */ if (data->symlink_target || data->reparse.buf) {
rc = 0;
} elseif (iov && server->ops->get_reparse_point_buffer) { struct reparse_data_buffer *reparse_buf;
u32 reparse_len;
reparse_buf = server->ops->get_reparse_point_buffer(iov, &reparse_len);
rc = parse_reparse_point(reparse_buf, reparse_len,
cifs_sb, full_path, data); /* * If the reparse point was not handled but it is the * name surrogate which points to directory, then treat * is as a new mount point. Name surrogate reparse point * represents another named entity in the system.
*/ if (rc == -EOPNOTSUPP &&
IS_REPARSE_TAG_NAME_SURROGATE(data->reparse.tag) &&
(le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY)) {
rc = 0;
cifs_create_junction_fattr(fattr, sb); goto out;
} /* * If the reparse point is unsupported by the Linux SMB * client then let it process by the SMB server. So mask * the -EOPNOTSUPP error code. This will allow Linux SMB * client to send SMB OPEN request to server. If server * does not support this reparse point too then server * will return error during open the path.
*/ if (rc == -EOPNOTSUPP)
rc = 0;
}
tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
/* * 1. Fetch file metadata if not provided (data)
*/
if (!data) {
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
full_path, &tmp_data);
data = &tmp_data;
}
/* * 2. Convert it to internal cifs metadata (fattr)
*/
switch (rc) { case 0: /* * If the file is a reparse point, it is more complicated * since we have to check if its reparse tag matches a known * special file type e.g. symlink or fifo or char etc.
*/ if (cifs_open_data_reparse(data)) {
rc = reparse_info_to_fattr(data, sb, xid, tcon,
full_path, fattr);
} else {
cifs_open_info_to_fattr(fattr, data, sb);
} if (!rc && *inode &&
(fattr->cf_flags & CIFS_FATTR_DELETE_PENDING))
cifs_mark_open_handles_for_deleted_file(*inode, full_path); break; case -EREMOTE: /* DFS link, no metadata available on this server */
cifs_create_junction_fattr(fattr, sb);
rc = 0; break; case -EACCES: #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY /* * perm errors, try again with backup flags if possible * * For SMB2 and later the backup intent flag * is already sent if needed on open and there * is no path based FindFirst operation to use * to retry with
*/ if (backup_cred(cifs_sb) && is_smb1_server(server)) { /* for easier reading */
FILE_ALL_INFO *fi;
FILE_DIRECTORY_INFO *fdi;
SEARCH_ID_FULL_DIR_INFO *si;
/* * 4. Tweak fattr based on mount options
*/ #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
handle_mnt_opt: #endif/* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ /* 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)
cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc);
}
/* fill in 0777 bits from ACL */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) {
rc = cifs_acl_to_fattr(cifs_sb, fattr, *inode, true, full_path, fid); if (rc == -EREMOTE)
rc = 0; if (rc) {
cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n",
__func__, rc); goto out;
}
} elseif (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
rc = cifs_acl_to_fattr(cifs_sb, fattr, *inode, false, full_path, fid); if (rc == -EREMOTE)
rc = 0; if (rc) {
cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n",
__func__, rc); goto out;
}
} elseif (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) /* fill in remaining high mode bits e.g. SUID, VTX */
cifs_sfu_mode(fattr, full_path, cifs_sb, xid); elseif (!(tcon->posix_extensions)) /* clear write bits if ATTR_READONLY is set */ if (fattr->cf_cifsattrs & ATTR_READONLY)
fattr->cf_mode &= ~(S_IWUGO);
tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
/* * 1. Fetch file metadata if not provided (data)
*/ if (!data) {
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
full_path, &tmp_data);
data = &tmp_data;
}
/* * 2. Convert it to internal cifs metadata (fattr)
*/
switch (rc) { case 0: if (cifs_open_data_reparse(data)) {
rc = reparse_info_to_fattr(data, sb, xid, tcon,
full_path, fattr);
} else {
smb311_posix_info_to_fattr(fattr, data, sb);
} break; case -EREMOTE: /* DFS link, no metadata available on this server */
cifs_create_junction_fattr(fattr, sb);
rc = 0; break; case -EACCES: /* * For SMB2 and later the backup intent flag * is already sent if needed on open and there * is no path based FindFirst operation to use * to retry with so nothing we can do, bail out
*/ goto out; default:
cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); goto out;
}
/* * 3. Tweak fattr based on mount options
*/ /* check for Minshall+French symlinks */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path);
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
}
/* [!] The compared values must be the same in struct cifs_fscache_inode_key. */
/* don't match inode with different uniqueid */ if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) return 0;
/* use createtime like an i_generation field */ if (CIFS_I(inode)->createtime != fattr->cf_createtime) return 0;
/* don't match inode of different type */ if (inode_wrong_type(inode, fattr->cf_mode)) return 0;
/* if it's not a directory or has no dentries, then flag it */ if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry))
fattr->cf_flags |= CIFS_FATTR_INO_COLLISION;
/* * 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.
*/ staticbool
inode_has_hashed_dentries(struct inode *inode)
{ struct dentry *dentry;
/* * Samba throws this field away, but windows may actually use it. * Do not set ctime unless other time stamps are changed explicitly * (i.e. by utimes()) since we would then have a mix of client and * server times.
*/ if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
cifs_dbg(FYI, "CIFS - CTIME changed\n");
info_buf.ChangeTime =
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
} else
info_buf.ChangeTime = 0;
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY /* * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit * and rename it to a random name that hopefully won't conflict with * anything else.
*/ int
cifs_rename_pending_delete(constchar *full_path, struct dentry *dentry, constunsignedint xid)
{ int oplock = 0; int rc; struct cifs_fid fid; struct cifs_open_parms oparms; struct inode *inode = d_inode(dentry); struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct tcon_link *tlink; struct cifs_tcon *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);
/* * We cannot rename the file if the server doesn't support * CAP_INFOLEVEL_PASSTHRU
*/ if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) {
rc = -EBUSY; goto out;
}
/* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */ if (dosattr != origattr) {
info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); if (info_buf == NULL) {
rc = -ENOMEM; goto out_close;
}
info_buf->Attributes = cpu_to_le32(dosattr);
rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,
current->tgid); /* although we would like to mark the file hidden
if that fails we will still try to rename it */ if (!rc)
cifsInode->cifsAttrs = dosattr; else
dosattr = origattr; /* since not able to change them */
}
/* try to set DELETE_ON_CLOSE */ if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) {
rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid,
current->tgid); /* * some samba versions return -ENOENT when we try to set the * file disposition here. Likely a samba bug, but work around * it for now. This means that some cifsXXX files may hang * around after they shouldn't. * * BB: remove this hack after more servers have the fix
*/ if (rc == -ENOENT)
rc = 0; elseif (rc != 0) {
rc = -EBUSY; goto undo_rename;
}
set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags);
}
/* * reset everything back to the original state. Don't bother * dealing with errors here since we can't do anything about * them anyway.
*/
undo_rename:
CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name,
cifs_sb->local_nls, cifs_remap(cifs_sb));
undo_setattr: if (dosattr != origattr) {
info_buf->Attributes = cpu_to_le32(origattr); if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,
current->tgid))
cifsInode->cifsAttrs = origattr;
}
/* copied from fs/nfs/dir.c with small changes */ staticvoid
cifs_drop_nlink(struct inode *inode)
{
spin_lock(&inode->i_lock); if (inode->i_nlink > 0)
drop_nlink(inode);
spin_unlock(&inode->i_lock);
}
/* * If d_inode(dentry) is null (usually meaning the cached dentry * is a negative dentry) then we would attempt a standard SMB delete, but * if that fails we can not attempt the fall back mechanisms on EACCES * but will return the EACCES to the caller. Note that the VFS does not call * unlink on negative dentries currently.
*/ staticint __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyrename)
{ int rc = 0; unsignedint xid; constchar *full_path; void *page; struct inode *inode = d_inode(dentry); struct cifsInodeInfo *cifs_inode; struct super_block *sb = dir->i_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct tcon_link *tlink; struct cifs_tcon *tcon;
__u32 dosattr = 0, origattr = 0; struct TCP_Server_Info *server; struct iattr *attrs = NULL; bool rehash = false;
if (unlikely(cifs_forced_shutdown(cifs_sb))) return -EIO;
/* Unhash dentry in advance to prevent any concurrent opens */
spin_lock(&dentry->d_lock); if (!d_unhashed(dentry)) {
__d_drop(dentry);
rehash = true;
}
spin_unlock(&dentry->d_lock);
tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
xid = get_xid();
page = alloc_dentry_path();
if (tcon->nodelete) {
rc = -EACCES; goto unlink_out;
}
/* Unlink can be called from rename so we can not take the
* sb->s_vfs_rename_mutex here */
full_path = build_path_from_dentry(dentry, page); if (IS_ERR(full_path)) {
rc = PTR_ERR(full_path); goto unlink_out;
}
retry_std_delete: if (!server->ops->unlink) {
rc = -ENOSYS; goto psx_del_no_retry;
}
/* For SMB2+, if the file is open, we always perform a silly rename. * * We check for d_count() right after calling * cifs_close_deferred_file_under_dentry() to make sure that the * dentry's refcount gets dropped in case the file had any deferred * close.
*/ if (!sillyrename && server->vals->protocol_id > SMB10_PROT_ID) {
spin_lock(&dentry->d_lock); if (d_count(dentry) > 1)
sillyrename = true;
spin_unlock(&dentry->d_lock);
}
/* undo the setattr if we errored out and it's needed */ if (rc != 0 && dosattr != 0)
cifs_set_file_info(inode, attrs, xid, full_path, origattr);
out_reval: if (inode) {
cifs_inode = CIFS_I(inode);
cifs_inode->time = 0; /* will force revalidate to get info
when needed */
inode_set_ctime_current(inode);
}
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
cifs_inode = CIFS_I(dir);
CIFS_I(dir)->time = 0; /* force revalidate of dir as well */
unlink_out:
free_dentry_path(page);
kfree(attrs);
free_xid(xid);
cifs_put_tlink(tlink); if (rehash)
d_rehash(dentry); return rc;
}
if (!S_ISDIR(inode->i_mode)) { /* * mkdir succeeded, but another client has managed to remove the * sucker and replace it with non-directory. Return success, * but don't leave the child in dcache.
*/
iput(inode);
d_drop(dentry); return 0;
} /* * setting nlink not necessary except in cases where we failed to get it * from the server or was set bogus. Also, since this is a brand new * inode, no need to grab the i_lock before setting the i_nlink.
*/ if (inode->i_nlink < 2)
set_nlink(inode, 2);
mode &= ~current_umask(); /* must turn on setgid bit if parent dir has it */ if (parent->i_mode & S_ISGID)
mode |= S_ISGID;
if (info->Type == cpu_to_le32(-1)) /* no return info, go query for it */ goto posix_mkdir_get_info; /* * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if * need to set uid/gid.
*/
if (!server->ops->mkdir) {
rc = -ENOSYS; goto mkdir_out;
}
/* BB add setting the equivalent of mode via CreateX w/ACLs */
rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb); if (rc) {
cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc);
d_drop(direntry); goto mkdir_out;
}
/* TODO: skip this for smb2/smb3 */
rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon,
xid);
mkdir_out: /* * Force revalidate to get parent dir info when needed since cached * attributes are invalid now.
*/
CIFS_I(inode)->time = 0;
free_dentry_path(page);
free_xid(xid);
cifs_put_tlink(tlink); return ERR_PTR(rc);
}
/* * Don't bother with rename by filehandle unless file is busy and * source. Note that cross directory moves do not work with * rename by filehandle to various Windows servers.
*/ if (rc == 0 || rc != -EBUSY) goto do_rename_exit;
/* Don't fall back to using SMB on SMB 2+ mount */ if (server->vals->protocol_id != 0) goto do_rename_exit;
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY /* open-file renames don't work across directories */ if (to_dentry->d_parent != from_dentry->d_parent) goto do_rename_exit;
/* * CIFSSMBRenameOpenFile() uses SMB_SET_FILE_RENAME_INFORMATION * which is SMB PASSTHROUGH level.
*/ if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) goto do_rename_exit;
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb, /* open the file to be renamed -- we need DELETE perms */
.desired_access = DELETE,
.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
.disposition = FILE_OPEN,
.path = from_path,
.fid = &fid,
};
cifs_sb = CIFS_SB(source_dir->i_sb); if (unlikely(cifs_forced_shutdown(cifs_sb))) return -EIO;
/* * Prevent any concurrent opens on the target by unhashing the dentry. * VFS already unhashes the target when renaming directories.
*/ if (d_is_positive(target_dentry) && !d_is_dir(target_dentry)) { if (!d_unhashed(target_dentry)) {
d_drop(target_dentry);
rehash = true;
}
}
tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
if (rc == -EACCES) { while (retry_count < 3) {
cifs_close_all_deferred_files(tcon);
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
to_name); if (rc != -EACCES) break;
retry_count++;
}
}
if (!rc)
rehash = false; /* * No-replace is the natural behavior for CIFS, so skip unlink hacks.
*/ if (flags & RENAME_NOREPLACE) goto cifs_rename_exit;
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY if (rc == -EEXIST && tcon->unix_ext) { /* * Are src and dst hardlinks of same inode? We can only tell * with unix extensions enabled.
*/
info_buf_source =
kmalloc_array(2, sizeof(FILE_UNIX_BASIC_INFO),
GFP_KERNEL); if (info_buf_source == NULL) {
rc = -ENOMEM; goto cifs_rename_exit;
}
if (tmprc == 0 && (info_buf_source->UniqueId ==
info_buf_target->UniqueId)) { /* same file, POSIX says that this is a noop */
rc = 0; goto cifs_rename_exit;
}
} /* * else ... BB we could add the same check for Windows by * checking the UniqueId via FILE_INTERNAL_INFO
*/
unlink_target: #endif/* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ if (d_really_is_positive(target_dentry)) { if (!rc) { struct inode *inode = d_inode(target_dentry); /* * Samba and ksmbd servers allow renaming a target * directory that is open, so make sure to update * ->i_nlink and then mark it as delete pending.
*/ if (S_ISDIR(inode->i_mode)) {
drop_cached_dir_by_name(xid, tcon, to_name, cifs_sb);
spin_lock(&inode->i_lock);
i_size_write(inode, 0);
clear_nlink(inode);
spin_unlock(&inode->i_lock);
set_bit(CIFS_INO_DELETE_PENDING, &CIFS_I(inode)->flags);
CIFS_I(inode)->time = 0; /* force reval */
inode_set_ctime_current(inode);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
}
} elseif (rc == -EACCES || rc == -EEXIST) { /* * Rename failed, possibly due to a busy target. * Retry it by unliking the target first.
*/ if (d_is_dir(target_dentry)) {
tmprc = cifs_rmdir(target_dir, target_dentry);
} else {
tmprc = __cifs_unlink(target_dir, target_dentry,
server->vals->protocol_id > SMB10_PROT_ID);
} if (tmprc) { /* * Some servers will return STATUS_ACCESS_DENIED * or STATUS_DIRECTORY_NOT_EMPTY when failing to * rename a non-empty directory. Make sure to * propagate the appropriate error back to * userspace.
*/ if (tmprc == -EEXIST || tmprc == -ENOTEMPTY)
rc = tmprc; goto cifs_rename_exit;
}
rc = cifs_do_rename(xid, source_dentry, from_name,
target_dentry, to_name); if (!rc)
rehash = false;
}
}
/* force revalidate to go get info when needed */
CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
if (test_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags)) returnfalse; if (cifs_i->time == 0) returntrue;
if (CIFS_CACHE_READ(cifs_i)) returnfalse;
if (!lookupCacheEnabled) returntrue;
if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) { if (cfid->time && cifs_i->time > cfid->time) {
close_cached_dir(cfid); returnfalse;
}
close_cached_dir(cfid);
} /* * depending on inode type, check if attribute caching disabled for * files or directories
*/ if (S_ISDIR(inode->i_mode)) { if (!cifs_sb->ctx->acdirmax) returntrue; if (!time_in_range(jiffies, cifs_i->time,
cifs_i->time + cifs_sb->ctx->acdirmax)) returntrue;
} else { /* file */ if (!cifs_sb->ctx->acregmax) returntrue; if (!time_in_range(jiffies, cifs_i->time,
cifs_i->time + cifs_sb->ctx->acregmax)) returntrue;
}
/* hardlinked files w/ noserverino get "special" treatment */ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&
S_ISREG(inode->i_mode) && inode->i_nlink != 1) returntrue;
returnfalse;
}
/** * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks * * @key: currently unused * @mode: the task state to sleep in
*/ staticint
cifs_wait_bit_killable(struct wait_bit_key *key, int mode)
{
schedule(); if (signal_pending_state(mode, current)) return -ERESTARTSYS; return 0;
}
/* swapfiles are not supposed to be shared */ if (IS_SWAPFILE(inode)) return 0;
rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable,
TASK_KILLABLE|TASK_FREEZABLE_UNSAFE); if (rc) return rc;
if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { /* for cache=singleclient, do not invalidate */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) goto skip_invalidate;
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) return -EIO;
/* * We need to be sure that all dirty pages are written and the server * has actual ctime, mtime and file length.
*/ if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_SIZE | STATX_BLOCKS)) &&
!CIFS_CACHE_READ(CIFS_I(inode)) &&
inode->i_mapping && inode->i_mapping->nrpages != 0) {
rc = filemap_fdatawait(inode->i_mapping); if (rc) {
mapping_set_error(inode->i_mapping, rc); return rc;
}
}
if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_FORCE_SYNC)
CIFS_I(inode)->time = 0; /* force revalidate */
/* * If the caller doesn't require syncing, only sync if * necessary (e.g. due to earlier truncate or setattr * invalidating the cached metadata)
*/ if (((flags & AT_STATX_SYNC_TYPE) != AT_STATX_DONT_SYNC) ||
(CIFS_I(inode)->time == 0)) {
rc = cifs_revalidate_dentry_attr(dentry); if (rc) return rc;
}
/* old CIFS Unix Extensions doesn't return create time */ if (CIFS_I(inode)->createtime) {
stat->result_mask |= STATX_BTIME;
stat->btime =
cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime));
}
stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED); if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED)
stat->attributes |= STATX_ATTR_COMPRESSED; if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_ENCRYPTED)
stat->attributes |= STATX_ATTR_ENCRYPTED;
/* * If on a multiuser mount without unix extensions or cifsacl being * enabled, 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) &&
!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
!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 0;
}
if (unlikely(cifs_forced_shutdown(cifs_sb))) return -EIO;
/* * We need to be sure that all dirty pages are written as they * might fill holes on the server.
*/ if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
inode->i_mapping->nrpages != 0) {
rc = filemap_fdatawait(inode->i_mapping); if (rc) {
mapping_set_error(inode->i_mapping, rc); return rc;
}
}
cfile = find_readable_file(cifs_i, false); if (cfile == NULL) return -EINVAL;
/* * 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. * This keeps our caching token (oplock) and avoids timeouts * when the local oplock break takes longer to flush * writebehind data than the SMB timeout for the SetPathInfo * request would allow
*/
open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); if (open_file) {
tcon = tlink_tcon(open_file->tlink);
server = tcon->ses->server; if (server->ops->set_file_size)
rc = server->ops->set_file_size(xid, tcon, open_file,
attrs->ia_size, false); else
rc = -ENOSYS;
cifsFileInfo_put(open_file);
cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc);
} else
rc = -EINVAL;
if (!rc) goto set_size_out;
if (tcon == NULL) {
tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
}
/* * 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 it by handle.
*/ if (server->ops->set_path_size)
rc = server->ops->set_path_size(xid, tcon, full_path,
attrs->ia_size, cifs_sb, false, dentry); else
rc = -ENOSYS;
cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc);
if (tlink)
cifs_put_tlink(tlink);
set_size_out: if (rc == 0) {
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
cifs_setsize(inode, attrs->ia_size); /* * i_blocks is not related to (i_size / i_blksize), but instead * 512 byte (2**9) size is required for calculating num blocks. * Until we can query the server for actual allocation size, * this is best estimate we have for blocks allocated for a file * Number of blocks must be rounded up so size 1 is not 0 blocks
*/
inode->i_blocks = (512 - 1 + attrs->ia_size) >> 9;
/* * The man page of truncate says if the size changed, * then the st_ctime and st_mtime fields for the file * are updated.
*/
attrs->ia_ctime = attrs->ia_mtime = current_time(inode);
attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME;
}
/* * Attempt to flush data before changing attributes. We need to do * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the * ownership or mode then we may also need to do this. Here, we take * the safe way out and just do the flush on all setattr requests. 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(inode->i_mapping); if (is_interrupt_error(rc)) {
rc = -ERESTARTSYS; goto out;
}
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, direntry); if (rc != 0) goto out;
}
/* skip mode change if it's just for clearing setuid/setgid */ if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
attrs->ia_valid &= ~ATTR_MODE;
/* 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);
free_dentry_path(page);
free_xid(xid); return rc;
} #endif/* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
/* * Attempt to flush data before changing attributes. We need to do * this for ATTR_SIZE and ATTR_MTIME. If the flush of the data * 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? Do we need to check for ATTR_MTIME_SET flag?
*/ if (attrs->ia_valid & (ATTR_MTIME | ATTR_SIZE | ATTR_CTIME)) {
rc = filemap_write_and_wait(inode->i_mapping); if (is_interrupt_error(rc)) {
rc = -ERESTARTSYS; goto cifs_setattr_exit;
}
mapping_set_error(inode->i_mapping, rc);
}
/* * In case of CIFS_MOUNT_CIFS_ACL, we cannot support all modes. * Pick up the actual mode bits that were set.
*/ if (mode != attrs->ia_mode)
attrs->ia_mode = mode;
} else if (((mode & S_IWUGO) == 0) &&
(cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
dosattr = cifsInode->cifsAttrs | ATTR_READONLY;
/* fix up mode if we're not using dynperm */ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
attrs->ia_mode = inode->i_mode & ~S_IWUGO;
} elseif ((mode & S_IWUGO) &&
(cifsInode->cifsAttrs & ATTR_READONLY)) {
dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; /* Attributes of 0 are ignored */ if (dosattr == 0)
dosattr |= ATTR_NORMAL;
/* reset local inode permissions to normal */ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
attrs->ia_mode &= ~(S_IALLUGO); if (S_ISDIR(inode->i_mode))
attrs->ia_mode |=
cifs_sb->ctx->dir_mode; else
attrs->ia_mode |=
cifs_sb->ctx->file_mode;
}
} elseif (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { /* ignore mode change - ATTR_READONLY hasn't changed */
attrs->ia_valid &= ~ATTR_MODE;
}
}
if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) ||
((attrs->ia_valid & ATTR_MODE) && dosattr)) {
rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */
/* Even if error on time set, no sense failing the call if the server would set the time to a reasonable value anyway, and this check ensures that we are not being called from sys_utimes in which case we ought to fail the call back to
the user when the server rejects the call */ if ((rc) && (attrs->ia_valid &
(ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE)))
rc = 0;
}
/* do not need local check to inode_check_ok since the server does
that */ if (rc) goto cifs_setattr_exit;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.