From: "Pali Rohár" <pali@kernel.org>
To: Steve French <sfrench@samba.org>,
Paulo Alcantara <pc@manguebit.com>,
ronnie sahlberg <ronniesahlberg@gmail.com>
Cc: linux-cifs@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 08/35] cifs: Improve SMB2+ stat() to work also for paths in DELETE_PENDING state
Date: Sun, 31 Aug 2025 14:35:35 +0200 [thread overview]
Message-ID: <20250831123602.14037-9-pali@kernel.org> (raw)
In-Reply-To: <20250831123602.14037-1-pali@kernel.org>
Paths in DELETE_PENDING state cannot be opened at all. So standard way of
querying path attributes for this case is not possible.
There is an alternative way how to query limited information about file
over SMB2+ dialects without opening file itself. It is by opening the
parent directory, querying specific child with filled search filter and
asking for attributes for that child.
Implement this fallback when standard case in smb2_query_path_info fails
with STATUS_DELETE_PENDING error and stat was asked for path which is not
top level one (because top level does not have parent directory at all).
Depends on "cifs: Change translation of STATUS_DELETE_PENDING to -EBUSY".
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/cifsglob.h | 1 +
fs/smb/client/smb2glob.h | 1 +
fs/smb/client/smb2inode.c | 177 +++++++++++++++++++++++++++++++++++++-
3 files changed, 176 insertions(+), 3 deletions(-)
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index e6830ab3a546..0ecf4988664e 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -2337,6 +2337,7 @@ struct smb2_compound_vars {
struct smb_rqst rqst[MAX_COMPOUND];
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
struct kvec qi_iov;
+ struct kvec qd_iov[SMB2_QUERY_DIRECTORY_IOV_SIZE];
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec close_iov;
diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h
index 224495322a05..1cb219605e75 100644
--- a/fs/smb/client/smb2glob.h
+++ b/fs/smb/client/smb2glob.h
@@ -39,6 +39,7 @@ enum smb2_compound_ops {
SMB2_OP_GET_REPARSE,
SMB2_OP_QUERY_WSL_EA,
SMB2_OP_OPEN_QUERY,
+ SMB2_OP_QUERY_DIRECTORY,
};
/* Used when constructing chained read requests. */
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 2a0316c514e4..460e75614ef1 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -176,6 +176,9 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
{
+ bool has_cifs_mount_server_inum = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM;
+ struct smb2_query_directory_req *qd_rqst = NULL;
+ struct smb2_query_directory_rsp *qd_rsp = NULL;
struct smb2_create_rsp *create_rsp = NULL;
struct smb2_query_info_rsp *qi_rsp = NULL;
struct smb2_compound_vars *vars = NULL;
@@ -344,6 +347,41 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
trace_smb3_posix_query_info_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
+ case SMB2_OP_QUERY_DIRECTORY:
+ rqst[num_rqst].rq_iov = &vars->qd_iov[0];
+ rqst[num_rqst].rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE;
+
+ rc = SMB2_query_directory_init(xid,
+ tcon,
+ server,
+ &rqst[num_rqst],
+ cfile ?
+ cfile->fid.persistent_fid : COMPOUND_FID,
+ cfile ?
+ cfile->fid.volatile_fid : COMPOUND_FID,
+ 0,
+ has_cifs_mount_server_inum ?
+ SMB_FIND_FILE_ID_FULL_DIR_INFO :
+ SMB_FIND_FILE_FULL_DIRECTORY_INFO);
+ if (!rc) {
+ /*
+ * Change the default search wildcard pattern '*'
+ * to the requested file name stored in in_iov[i]
+ * and request for only one single entry.
+ */
+ qd_rqst = rqst[num_rqst].rq_iov[0].iov_base;
+ qd_rqst->Flags |= SMB2_RETURN_SINGLE_ENTRY;
+ qd_rqst->FileNameLength = cpu_to_le16(in_iov[i].iov_len);
+ rqst[num_rqst].rq_iov[1] = in_iov[i];
+ }
+ if (!rc && (!cfile || num_rqst > 1)) {
+ smb2_set_next_command(tcon, &rqst[num_rqst]);
+ smb2_set_related(&rqst[num_rqst]);
+ } else if (rc) {
+ goto finished;
+ }
+ num_rqst++;
+ break;
case SMB2_OP_DELETE:
trace_smb3_delete_enter(xid, tcon->tid, ses->Suid, full_path);
break;
@@ -730,6 +768,64 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
trace_smb3_posix_query_info_compound_done(xid, tcon->tid,
ses->Suid);
break;
+ case SMB2_OP_QUERY_DIRECTORY:
+ if (rc == 0) {
+ qd_rsp = (struct smb2_query_directory_rsp *)
+ rsp_iov[i + 1].iov_base;
+ rc = smb2_validate_iov(le16_to_cpu(qd_rsp->OutputBufferOffset),
+ le32_to_cpu(qd_rsp->OutputBufferLength),
+ &rsp_iov[i + 1],
+ has_cifs_mount_server_inum ?
+ sizeof(SEARCH_ID_FULL_DIR_INFO) :
+ sizeof(FILE_FULL_DIRECTORY_INFO));
+ }
+ if (rc == 0) {
+ /*
+ * Both SEARCH_ID_FULL_DIR_INFO and FILE_FULL_DIRECTORY_INFO
+ * have same member offsets except the UniqueId and FileName.
+ */
+ SEARCH_ID_FULL_DIR_INFO *si =
+ (SEARCH_ID_FULL_DIR_INFO *)qd_rsp->Buffer;
+
+ idata = in_iov[i + 1].iov_base;
+ idata->fi.CreationTime = si->CreationTime;
+ idata->fi.LastAccessTime = si->LastAccessTime;
+ idata->fi.LastWriteTime = si->LastWriteTime;
+ idata->fi.ChangeTime = si->ChangeTime;
+ idata->fi.Attributes = si->ExtFileAttributes;
+ idata->fi.AllocationSize = si->AllocationSize;
+ idata->fi.EndOfFile = si->EndOfFile;
+ idata->fi.EASize = si->EaSize;
+ idata->fi.Directory =
+ !!(le32_to_cpu(si->ExtFileAttributes) & ATTR_DIRECTORY);
+ /*
+ * UniqueId is present only in struct SEARCH_ID_FULL_DIR_INFO.
+ * It is not present in struct FILE_FULL_DIRECTORY_INFO.
+ * struct SEARCH_ID_FULL_DIR_INFO was requested only when
+ * CIFS_MOUNT_SERVER_INUM is set.
+ */
+ if (has_cifs_mount_server_inum)
+ idata->fi.IndexNumber = si->UniqueId;
+ /*
+ * Do not change idata->fi.NumberOfLinks to correctly
+ * trigger the CIFS_FATTR_UNKNOWN_NLINK flag.
+ */
+ /*
+ * Do not change idata->fi.DeletePending as we do not know if
+ * the entry is in the delete pending state. SMB2 QUERY_DIRECTORY
+ * at any level does not provide this information.
+ */
+ }
+ SMB2_query_directory_free(&rqst[num_rqst++]);
+ if (rc)
+ trace_smb3_query_dir_err(xid,
+ cfile ? cfile->fid.persistent_fid : COMPOUND_FID,
+ tcon->tid, ses->Suid, 0, 0, rc);
+ else
+ trace_smb3_query_dir_done(xid,
+ cfile ? cfile->fid.persistent_fid : COMPOUND_FID,
+ tcon->tid, ses->Suid, 0, 0);
+ break;
case SMB2_OP_DELETE:
if (rc)
trace_smb3_delete_err(xid, tcon->tid, ses->Suid, rc);
@@ -1090,9 +1186,9 @@ int smb2_query_path_info(const unsigned int xid,
break;
case -EREMOTE:
break;
- default:
- if (hdr->Status != STATUS_OBJECT_NAME_INVALID)
- break;
+ }
+
+ if (hdr->Status == STATUS_OBJECT_NAME_INVALID) {
rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
full_path, &islink);
if (rc2) {
@@ -1101,6 +1197,81 @@ int smb2_query_path_info(const unsigned int xid,
}
if (islink)
rc = -EREMOTE;
+ } else if (hdr->Status == STATUS_DELETE_PENDING && full_path[0]) {
+ /*
+ * If SMB2 OPEN/CREATE fails with STATUS_DELETE_PENDING error,
+ * it means that the path is in delete pending state and it is
+ * not possible to open it until some other client clears delete
+ * pending state or all other clients close all opened handles
+ * to that path.
+ *
+ * There is an alternative way how to query limited information
+ * about path which is in delete pending state still suitable
+ * for the stat() syscall. It is by opening the parent directory,
+ * querying specific child with filled search filer and asking
+ * for attributes for that child.
+ */
+
+ char *parent_path;
+ const char *basename;
+ __le16 *basename_utf16;
+ int basename_utf16_len;
+ struct cifsFileInfo *parent_cfile;
+
+ basename = strrchr(full_path, CIFS_DIR_SEP(cifs_sb));
+ if (basename) {
+ parent_path = kstrndup(full_path, basename - full_path, GFP_KERNEL);
+ basename++;
+ } else {
+ parent_path = kstrdup("", GFP_KERNEL);
+ basename = full_path;
+ }
+
+ if (!parent_path) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ basename_utf16 = cifs_convert_path_to_utf16(basename, cifs_sb);
+ if (!basename_utf16) {
+ kfree(parent_path);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ basename_utf16_len = 2 * UniStrnlen((wchar_t *)basename_utf16, PATH_MAX);
+
+retry_query_directory:
+ num_cmds = 1;
+ cmds[0] = SMB2_OP_QUERY_DIRECTORY;
+ in_iov[0].iov_base = basename_utf16;
+ in_iov[0].iov_len = basename_utf16_len;
+ in_iov[1].iov_base = data;
+ in_iov[1].iov_len = sizeof(*data);
+ oparms = CIFS_OPARMS(cifs_sb, tcon, parent_path, FILE_READ_DATA,
+ FILE_OPEN, CREATE_NOT_FILE, ACL_NO_MODE);
+ cifs_get_readable_path(tcon, parent_path, &parent_cfile);
+ free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
+ rc = smb2_compound_op(xid, tcon, cifs_sb, parent_path,
+ &oparms, in_iov, cmds, num_cmds,
+ parent_cfile, out_iov, out_buftype, NULL);
+ if (rc == -EOPNOTSUPP && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
+ /*
+ * If querying of server inode numbers is not supported
+ * but is enabled, then disable it and try again.
+ */
+ cifs_autodisable_serverino(cifs_sb);
+ goto retry_query_directory;
+ }
+
+ kfree(parent_path);
+ kfree(basename_utf16);
+
+ hdr = out_iov[0].iov_base;
+ if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
+ goto out;
+
+ data->fi.DeletePending = 1; /* This is code path for STATUS_DELETE_PENDING. */
}
out:
--
2.20.1
next prev parent reply other threads:[~2025-08-31 12:36 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-31 12:35 [PATCH 00/35] cifs: Fix SMB rmdir() and unlink() against Windows SMB servers Pali Rohár
2025-08-31 12:35 ` [PATCH 01/35] cifs: Fix and improve cifs_is_path_accessible() function Pali Rohár
2025-08-31 12:35 ` [PATCH 02/35] cifs: Allow fallback code in smb_set_file_info() also for directories Pali Rohár
2025-08-31 12:35 ` [PATCH 03/35] cifs: Add fallback code path for cifs_mkdir_setinfo() Pali Rohár
2025-08-31 12:35 ` [PATCH 04/35] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo() Pali Rohár
2025-08-31 12:35 ` [PATCH 05/35] cifs: Remove CIFSSMBSetPathInfoFB() fallback function Pali Rohár
2025-08-31 12:35 ` [PATCH 06/35] cifs: Remove cifs_backup_query_path_info() and replace it by cifs_query_path_info() Pali Rohár
2025-08-31 12:35 ` [PATCH 07/35] cifs: Change translation of STATUS_DELETE_PENDING to -EBUSY Pali Rohár
2025-08-31 12:35 ` Pali Rohár [this message]
2025-08-31 12:35 ` [PATCH 09/35] cifs: Improve SMB1 stat() to work also for paths in DELETE_PENDING state Pali Rohár
2025-08-31 12:35 ` [PATCH 10/35] cifs: Improve detect_directory_symlink_target() function Pali Rohár
2025-08-31 12:35 ` [PATCH 11/35] cifs: Fix random name construction for cifs_rename_pending_delete() Pali Rohár
2025-08-31 12:35 ` [PATCH 12/35] cifs: Fix DELETE comments in cifs_rename_pending_delete() Pali Rohár
2025-08-31 12:35 ` [PATCH 13/35] cifs: Avoid dynamic memory allocation of FILE_BASIC_INFO buffer " Pali Rohár
2025-08-31 12:35 ` [PATCH 14/35] cifs: Extend CIFSSMBRenameOpenFile() function for overwrite parameter Pali Rohár
2025-08-31 12:35 ` [PATCH 15/35] cifs: Do not try to overwrite existing sillyname in cifs_rename_pending_delete() Pali Rohár
2025-08-31 12:35 ` [PATCH 16/35] cifs: Add comments for DeletePending assignments in open functions Pali Rohár
2025-08-31 12:35 ` [PATCH 17/35] cifs: Use NT_STATUS_DELETE_PENDING for filling fi.DeletePending in cifs_query_path_info() Pali Rohár
2025-08-31 12:35 ` [PATCH 18/35] cifs: Do not set NumberOfLinks to 1 from open or query calls Pali Rohár
2025-08-31 12:35 ` [PATCH 19/35] cifs: Fix cifs_rename_pending_delete() for files with more hardlinks Pali Rohár
2025-08-31 12:35 ` [PATCH 20/35] cifs: Fix permission logic in cifs_rename_pending_delete() Pali Rohár
2025-08-31 12:35 ` [PATCH 21/35] cifs: Propagate error code from CIFSSMBSetFileInfo() to cifs_rename_pending_delete() Pali Rohár
2025-08-31 12:35 ` [PATCH 22/35] cifs: Improve cifs_rename_pending_delete() to work without the PASSTHRU support Pali Rohár
2025-08-31 12:35 ` [PATCH 23/35] cifs: Fix SMBLegacyOpen() function Pali Rohár
2025-08-31 12:35 ` [PATCH 24/35] cifs: Add a new callback set_file_disp() for setting file disposition (delete pending state) Pali Rohár
2025-08-31 12:35 ` [PATCH 25/35] cifs: Add a new callback rename_opened_file() for renaming an opened file Pali Rohár
2025-08-31 12:35 ` [PATCH 26/35] cifs: Add SMB2+ support into cifs_rename_pending_delete() function Pali Rohár
2025-08-31 12:35 ` [PATCH 27/35] cifs: Move SMB1 usage of CIFSPOSIXDelFile() from inode.c to cifssmb.c Pali Rohár
2025-08-31 12:35 ` [PATCH 28/35] cifs: Fix smb2_unlink() to fail on directory Pali Rohár
2025-08-31 12:35 ` [PATCH 29/35] cifs: Fix smb2_rmdir() on reparse point Pali Rohár
2025-08-31 12:35 ` [PATCH 30/35] cifs: Simplify SMB2_OP_DELETE API usage Pali Rohár
2025-08-31 12:35 ` [PATCH 31/35] cifs: Deduplicate smb2_unlink() and smb2_rmdir() into one common function Pali Rohár
2025-08-31 12:35 ` [PATCH 32/35] cifs: Use cifs_rename_pending_delete() fallback also for rmdir() Pali Rohár
2025-08-31 12:36 ` [PATCH 33/35] cifs: Add a new open flag CREATE_OPTION_EXCLUSIVE to open with deny all shared reservation Pali Rohár
2025-08-31 12:36 ` [PATCH 34/35] cifs: Use CREATE_OPTION_EXCLUSIVE when opening file/dir for SMB2+ non-POSIX unlink/rmdir Pali Rohár
2025-08-31 12:36 ` [PATCH 35/35] cifs: Use CREATE_OPTION_EXCLUSIVE when doing SMB1 rmdir on NT server Pali Rohár
2025-09-01 7:55 ` [PATCH 00/35] cifs: Fix SMB rmdir() and unlink() against Windows SMB servers Stefan Metzmacher
2025-09-01 17:02 ` Pali Rohár
2025-09-02 15:17 ` Stefan Metzmacher
2025-09-02 16:30 ` Pali Rohár
2025-09-07 11:05 ` Pali Rohár
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250831123602.14037-9-pali@kernel.org \
--to=pali@kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=pc@manguebit.com \
--cc=ronniesahlberg@gmail.com \
--cc=sfrench@samba.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).