[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250831123602.14037-1-pali@kernel.org>
Date: Sun, 31 Aug 2025 14:35:27 +0200
From: Pali Rohár <pali@...nel.org>
To: Steve French <sfrench@...ba.org>,
Paulo Alcantara <pc@...guebit.com>,
ronnie sahlberg <ronniesahlberg@...il.com>
Cc: linux-cifs@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH 00/35] cifs: Fix SMB rmdir() and unlink() against Windows SMB servers
This patch series improves Linux rmdir() and unlink() syscalls called on
SMB mounts exported from Windows SMB servers which do not implement
POSIX semantics of the file and directory removal.
This patch series should have no impact and no function change when
communicating with the POSIX SMB servers, as they should implement
proper rmdir and unlink logic.
When issuing remove path command against non-POSIX / Windows SMB server,
it let the directory entry which is being removed in the directory until
all users / clients close all handles / references to that path.
POSIX requires from rmdir() and unlink() syscalls that after successful
call, the requested path / directory entry is released and allows to
create a new file or directory with that name. This is currently not
working against non-POSIX / Windows SMB servers.
To workaround this problem fix and improve existing cifs silly rename
code and extend it also to SMB2 and SMB3 dialects when communicating
with Windows SMB servers. Silly rename is applied only when it is
necessary (when some other client has opened file or directory).
If no other client has the file / dir open then silly rename is not
used.
With this patch series, after successful rmdir() or unlink() call from
Linux userspace application, the path is released, it is not visible in
the followup readdir() call, also stat() properly returns ENOENT and it
is possible to call creat() or mkdir() on the same path to create new
node. Silly rename is used to ensure that path is released.
Before this change, the original path was visible in the readdir() call
and create() or mkdir() was failing with EEXIST, even rmdir() or
unlink() on that path returned success.
Test cases when "path" is opened on Windows server by other client, so
path cannot be removed.
1.
unlink("path");
mkdir("path");
before: unlink returns success but mkdir returns EEXIST.
after: unlink returns success, mkdir creates new directory and the
orignal one is silly renamed.
2.
unlink("path")
open("path") without O_CREAT
mkdir("path")
before: unlink returns success, open returns ENOENT and mkdir returns
EEXIST.
after: unlink returns success, open returns ENOENT, mkdir creates
new directory and the original path is silly renamed.
3.
unlink("path")
stat("path")
mkdir("path")
before: unlink returns success, stat returns ENOENT and mkdir returns
EEXIST.
after: unlink returns success, stat returns ENOENT, mkdir creates new
directory and the original path is silly renamed.
4.
unlink("path")
stat("path")
open("path") with O_CREAT | O_EXCL
before: unlink returns success, stat returns ENOENT and open returns
EEXIST.
after: unlink returns success, stat returns ENOENT, open creates new
file and the original path is silly renamed.
5.
unlink("path")
readdir("parent_of_path")
before: unlink returns success but readdir returns entry corresponding
to path.
after: unlink returns success, original path is silly renamed and
readdir returns the entry under new silly name.
Test case when "tmp_path" on the Windows server was created (or opened)
as a exclusive temporary file with the DELETE_PENDING flag set:
Before:
stat("tmp_path") - returns ENOENT
mkdir("tmp_path") - returns EEXIST
open("tmp_path") without O_CREAT - returns ENOENT
open("tmp_path") with O_CREAT | O_EXCL - return EEXIST
unlink("tmp_path") - returns ENOENT
readdir(parent_of_tmp_path) - returns the "tmp_path" entry
After:
stat("tmp_path") - success
mkdir("tmp_path") - returns EEXIST
open("tmp_path") without O_CREAT - returns EBUSY
open("tmp_path") with O_CREAT | O_EXCL - return EEXIST
unlink("tmp_path") - returns EBUSY
readdir(parent_of_tmp_path) - returns the "tmp_path" entry
The difference is in stat(), open() and unlink() syscalls. Now Linux
applications can stat such files, so file attributes are now visible in
"ls -l -a" output, but trying to open / modify / delete them fails with
EBUSY instead of ENOENT.
Pali Rohár (35):
cifs: Fix and improve cifs_is_path_accessible() function
cifs: Allow fallback code in smb_set_file_info() also for directories
cifs: Add fallback code path for cifs_mkdir_setinfo()
cifs: Remove code for querying FILE_INFO_STANDARD via
CIFSSMBQPathInfo()
cifs: Remove CIFSSMBSetPathInfoFB() fallback function
cifs: Remove cifs_backup_query_path_info() and replace it by
cifs_query_path_info()
cifs: Change translation of STATUS_DELETE_PENDING to -EBUSY
cifs: Improve SMB2+ stat() to work also for paths in DELETE_PENDING
state
cifs: Improve SMB1 stat() to work also for paths in DELETE_PENDING
state
cifs: Improve detect_directory_symlink_target() function
cifs: Fix random name construction for cifs_rename_pending_delete()
cifs: Fix DELETE comments in cifs_rename_pending_delete()
cifs: Avoid dynamic memory allocation of FILE_BASIC_INFO buffer in
cifs_rename_pending_delete()
cifs: Extend CIFSSMBRenameOpenFile() function for overwrite parameter
cifs: Do not try to overwrite existing sillyname in
cifs_rename_pending_delete()
cifs: Add comments for DeletePending assignments in open functions
cifs: Use NT_STATUS_DELETE_PENDING for filling fi.DeletePending in
cifs_query_path_info()
cifs: Do not set NumberOfLinks to 1 from open or query calls
cifs: Fix cifs_rename_pending_delete() for files with more hardlinks
cifs: Fix permission logic in cifs_rename_pending_delete()
cifs: Propagate error code from CIFSSMBSetFileInfo() to
cifs_rename_pending_delete()
cifs: Improve cifs_rename_pending_delete() to work without the
PASSTHRU support
cifs: Fix SMBLegacyOpen() function
cifs: Add a new callback set_file_disp() for setting file disposition
(delete pending state)
cifs: Add a new callback rename_opened_file() for renaming an opened
file
cifs: Add SMB2+ support into cifs_rename_pending_delete() function.
cifs: Move SMB1 usage of CIFSPOSIXDelFile() from inode.c to cifssmb.c
cifs: Fix smb2_unlink() to fail on directory
cifs: Fix smb2_rmdir() on reparse point
cifs: Simplify SMB2_OP_DELETE API usage
cifs: Deduplicate smb2_unlink() and smb2_rmdir() into one common
function
cifs: Use cifs_rename_pending_delete() fallback also for rmdir()
cifs: Add a new open flag CREATE_OPTION_EXCLUSIVE to open with deny
all shared reservation
cifs: Use CREATE_OPTION_EXCLUSIVE when opening file/dir for SMB2+
non-POSIX unlink/rmdir
cifs: Use CREATE_OPTION_EXCLUSIVE when doing SMB1 rmdir on NT server
fs/smb/client/cifsglob.h | 11 +-
fs/smb/client/cifspdu.h | 5 +
fs/smb/client/cifsproto.h | 6 +-
fs/smb/client/cifssmb.c | 228 +++++++++++++----------
fs/smb/client/inode.c | 346 ++++++++++++++++-------------------
fs/smb/client/netmisc.c | 2 +-
fs/smb/client/reparse.c | 75 +++-----
fs/smb/client/smb1ops.c | 84 +++++++--
fs/smb/client/smb2glob.h | 1 +
fs/smb/client/smb2inode.c | 264 +++++++++++++++++++++++---
fs/smb/client/smb2maperror.c | 2 +-
fs/smb/client/smb2ops.c | 24 +++
fs/smb/client/smb2pdu.c | 51 +++++-
fs/smb/client/smb2proto.h | 6 +
14 files changed, 704 insertions(+), 401 deletions(-)
--
2.20.1
Powered by blists - more mailing lists