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);
}
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.