Skip to content

Commit 86f740f

Browse files
aaptelsmfrench
authored andcommitted
cifs: fix rename() by ensuring source handle opened with DELETE bit
To rename a file in SMB2 we open it with the DELETE access and do a special SetInfo on it. If the handle is missing the DELETE bit the server will fail the SetInfo with STATUS_ACCESS_DENIED. We currently try to reuse any existing opened handle we have with cifs_get_writable_path(). That function looks for handles with WRITE access but doesn't check for DELETE, making rename() fail if it finds a handle to reuse. Simple reproducer below. To select handles with the DELETE bit, this patch adds a flag argument to cifs_get_writable_path() and find_writable_file() and the existing 'bool fsuid_only' argument is converted to a flag. The cifsFileInfo struct only stores the UNIX open mode but not the original SMB access flags. Since the DELETE bit is not mapped in that mode, this patch stores the access mask in cifs_fid on file open, which is accessible from cifsFileInfo. Simple reproducer: #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define E(s) perror(s), exit(1) int main(int argc, char *argv[]) { int fd, ret; if (argc != 3) { fprintf(stderr, "Usage: %s A B\n" "create&open A in write mode, " "rename A to B, close A\n", argv[0]); return 0; } fd = openat(AT_FDCWD, argv[1], O_WRONLY|O_CREAT|O_SYNC, 0666); if (fd == -1) E("openat()"); ret = rename(argv[1], argv[2]); if (ret) E("rename()"); ret = close(fd); if (ret) E("close()"); return ret; } $ gcc -o bugrename bugrename.c $ ./bugrename /mnt/a /mnt/b rename(): Permission denied Fixes: 8de9e86 ("cifs: create a helper to find a writeable handle by path name") CC: Stable <[email protected]> Signed-off-by: Aurelien Aptel <[email protected]> Signed-off-by: Steve French <[email protected]> Reviewed-by: Pavel Shilovsky <[email protected]> Reviewed-by: Paulo Alcantara (SUSE) <[email protected]>
1 parent ec57010 commit 86f740f

File tree

9 files changed

+33
-17
lines changed

9 files changed

+33
-17
lines changed

fs/cifs/cifsglob.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,7 @@ struct cifs_fid {
12811281
__u64 volatile_fid; /* volatile file id for smb2 */
12821282
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
12831283
__u8 create_guid[16];
1284+
__u32 access;
12841285
struct cifs_pending_open *pending_open;
12851286
unsigned int epoch;
12861287
#ifdef CONFIG_CIFS_DEBUG2
@@ -1741,6 +1742,12 @@ static inline bool is_retryable_error(int error)
17411742
return false;
17421743
}
17431744

1745+
1746+
/* cifs_get_writable_file() flags */
1747+
#define FIND_WR_ANY 0
1748+
#define FIND_WR_FSUID_ONLY 1
1749+
#define FIND_WR_WITH_DELETE 2
1750+
17441751
#define MID_FREE 0
17451752
#define MID_REQUEST_ALLOCATED 1
17461753
#define MID_REQUEST_SUBMITTED 2

fs/cifs/cifsproto.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,12 @@ extern bool backup_cred(struct cifs_sb_info *);
134134
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
135135
extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
136136
unsigned int bytes_written);
137-
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
137+
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, int);
138138
extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
139-
bool fsuid_only,
139+
int flags,
140140
struct cifsFileInfo **ret_file);
141141
extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
142+
int flags,
142143
struct cifsFileInfo **ret_file);
143144
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
144145
extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,

fs/cifs/cifssmb.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,7 @@ CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock,
14921492
*oplock = rsp->OplockLevel;
14931493
/* cifs fid stays in le */
14941494
oparms->fid->netfid = rsp->Fid;
1495+
oparms->fid->access = desired_access;
14951496

14961497
/* Let caller know file was created so we can set the mode. */
14971498
/* Do we care about the CreateAction in any other cases? */
@@ -2115,7 +2116,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
21152116
wdata2->tailsz = tailsz;
21162117
wdata2->bytes = cur_len;
21172118

2118-
rc = cifs_get_writable_file(CIFS_I(inode), false,
2119+
rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY,
21192120
&wdata2->cfile);
21202121
if (!wdata2->cfile) {
21212122
cifs_dbg(VFS, "No writable handle to retry writepages rc=%d\n",

fs/cifs/file.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,15 +1958,16 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
19581958

19591959
/* Return -EBADF if no handle is found and general rc otherwise */
19601960
int
1961-
cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only,
1961+
cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags,
19621962
struct cifsFileInfo **ret_file)
19631963
{
19641964
struct cifsFileInfo *open_file, *inv_file = NULL;
19651965
struct cifs_sb_info *cifs_sb;
19661966
bool any_available = false;
19671967
int rc = -EBADF;
19681968
unsigned int refind = 0;
1969-
1969+
bool fsuid_only = flags & FIND_WR_FSUID_ONLY;
1970+
bool with_delete = flags & FIND_WR_WITH_DELETE;
19701971
*ret_file = NULL;
19711972

19721973
/*
@@ -1998,6 +1999,8 @@ cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only,
19981999
continue;
19992000
if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
20002001
continue;
2002+
if (with_delete && !(open_file->fid.access & DELETE))
2003+
continue;
20012004
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
20022005
if (!open_file->invalidHandle) {
20032006
/* found a good writable file */
@@ -2045,12 +2048,12 @@ cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only,
20452048
}
20462049

20472050
struct cifsFileInfo *
2048-
find_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only)
2051+
find_writable_file(struct cifsInodeInfo *cifs_inode, int flags)
20492052
{
20502053
struct cifsFileInfo *cfile;
20512054
int rc;
20522055

2053-
rc = cifs_get_writable_file(cifs_inode, fsuid_only, &cfile);
2056+
rc = cifs_get_writable_file(cifs_inode, flags, &cfile);
20542057
if (rc)
20552058
cifs_dbg(FYI, "couldn't find writable handle rc=%d", rc);
20562059

@@ -2059,6 +2062,7 @@ find_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only)
20592062

20602063
int
20612064
cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
2065+
int flags,
20622066
struct cifsFileInfo **ret_file)
20632067
{
20642068
struct list_head *tmp;
@@ -2085,7 +2089,7 @@ cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
20852089
kfree(full_path);
20862090
cinode = CIFS_I(d_inode(cfile->dentry));
20872091
spin_unlock(&tcon->open_file_lock);
2088-
return cifs_get_writable_file(cinode, 0, ret_file);
2092+
return cifs_get_writable_file(cinode, flags, ret_file);
20892093
}
20902094

20912095
spin_unlock(&tcon->open_file_lock);
@@ -2162,7 +2166,8 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
21622166
if (mapping->host->i_size - offset < (loff_t)to)
21632167
to = (unsigned)(mapping->host->i_size - offset);
21642168

2165-
rc = cifs_get_writable_file(CIFS_I(mapping->host), false, &open_file);
2169+
rc = cifs_get_writable_file(CIFS_I(mapping->host), FIND_WR_ANY,
2170+
&open_file);
21662171
if (!rc) {
21672172
bytes_written = cifs_write(open_file, open_file->pid,
21682173
write_data, to - from, &offset);
@@ -2355,7 +2360,7 @@ static int cifs_writepages(struct address_space *mapping,
23552360
if (cfile)
23562361
cifsFileInfo_put(cfile);
23572362

2358-
rc = cifs_get_writable_file(CIFS_I(inode), false, &cfile);
2363+
rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile);
23592364

23602365
/* in case of an error store it to return later */
23612366
if (rc)

fs/cifs/inode.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,7 +2282,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
22822282
* writebehind data than the SMB timeout for the SetPathInfo
22832283
* request would allow
22842284
*/
2285-
open_file = find_writable_file(cifsInode, true);
2285+
open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
22862286
if (open_file) {
22872287
tcon = tlink_tcon(open_file->tlink);
22882288
server = tcon->ses->server;
@@ -2432,7 +2432,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
24322432
args->ctime = NO_CHANGE_64;
24332433

24342434
args->device = 0;
2435-
open_file = find_writable_file(cifsInode, true);
2435+
open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
24362436
if (open_file) {
24372437
u16 nfid = open_file->fid.netfid;
24382438
u32 npid = open_file->pid;
@@ -2535,7 +2535,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
25352535
rc = 0;
25362536

25372537
if (attrs->ia_valid & ATTR_MTIME) {
2538-
rc = cifs_get_writable_file(cifsInode, false, &wfile);
2538+
rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile);
25392539
if (!rc) {
25402540
tcon = tlink_tcon(wfile->tlink);
25412541
rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid);

fs/cifs/smb1ops.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
766766
struct cifs_tcon *tcon;
767767

768768
/* if the file is already open for write, just use that fileid */
769-
open_file = find_writable_file(cinode, true);
769+
open_file = find_writable_file(cinode, FIND_WR_FSUID_ONLY);
770770
if (open_file) {
771771
fid.netfid = open_file->fid.netfid;
772772
netpid = open_file->pid;

fs/cifs/smb2inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
521521
cifs_i = CIFS_I(inode);
522522
dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
523523
data.Attributes = cpu_to_le32(dosattrs);
524-
cifs_get_writable_path(tcon, name, &cfile);
524+
cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
525525
tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
526526
FILE_WRITE_ATTRIBUTES, FILE_CREATE,
527527
CREATE_NOT_FILE, ACL_NO_MODE,
@@ -577,7 +577,7 @@ smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
577577
{
578578
struct cifsFileInfo *cfile;
579579

580-
cifs_get_writable_path(tcon, from_name, &cfile);
580+
cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
581581

582582
return smb2_set_path_attr(xid, tcon, from_name, to_name,
583583
cifs_sb, DELETE, SMB2_OP_RENAME, cfile);

fs/cifs/smb2ops.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,7 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
13641364

13651365
cfile->fid.persistent_fid = fid->persistent_fid;
13661366
cfile->fid.volatile_fid = fid->volatile_fid;
1367+
cfile->fid.access = fid->access;
13671368
#ifdef CONFIG_CIFS_DEBUG2
13681369
cfile->fid.mid = fid->mid;
13691370
#endif /* CIFS_DEBUG2 */
@@ -3327,7 +3328,7 @@ static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offs
33273328
* some servers (Windows2016) will not reflect recent writes in
33283329
* QUERY_ALLOCATED_RANGES until SMB2_flush is called.
33293330
*/
3330-
wrcfile = find_writable_file(cifsi, false);
3331+
wrcfile = find_writable_file(cifsi, FIND_WR_ANY);
33313332
if (wrcfile) {
33323333
filemap_write_and_wait(inode->i_mapping);
33333334
smb2_flush_file(xid, tcon, &wrcfile->fid);

fs/cifs/smb2pdu.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2771,6 +2771,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
27712771
atomic_inc(&tcon->num_remote_opens);
27722772
oparms->fid->persistent_fid = rsp->PersistentFileId;
27732773
oparms->fid->volatile_fid = rsp->VolatileFileId;
2774+
oparms->fid->access = oparms->desired_access;
27742775
#ifdef CONFIG_CIFS_DEBUG2
27752776
oparms->fid->mid = le64_to_cpu(rsp->sync_hdr.MessageId);
27762777
#endif /* CIFS_DEBUG2 */

0 commit comments

Comments
 (0)