From fd37f98158161406229b728a7c767121a30e254f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 18 Jan 2007 06:19:24 +0000 Subject: r20873: Some correctness fixes w.r.t. Samba4 torture BASE-DELETE. Allow us to correctly refuse to set delete on close on a non-empty directory. There are still some delete-on-close wrinkles to be fixed, but I understand how to do that better now. I'll fix this tomorrow. Jeremy. (This used to be commit 029635885825a5562e7974a6f5675cce3bf1b5dc) --- source3/lib/dummysmbd.c | 16 ++++++++++++++++ source3/locking/locking.c | 40 ++++++++++++++++++++++++++++++++++++++++ source3/smbd/dir.c | 13 ++++++++----- source3/smbd/nttrans.c | 23 ++++++++++++++--------- source3/smbd/open.c | 10 ++++++---- source3/smbd/posix_acls.c | 5 +++++ source3/smbd/reply.c | 11 ++++++++++- source3/smbd/trans2.c | 14 ++++++++++++-- 8 files changed, 111 insertions(+), 21 deletions(-) (limited to 'source3') diff --git a/source3/lib/dummysmbd.c b/source3/lib/dummysmbd.c index 5bb71e120e..442a612f93 100644 --- a/source3/lib/dummysmbd.c +++ b/source3/lib/dummysmbd.c @@ -37,3 +37,19 @@ BOOL conn_snum_used(int snum) void cancel_pending_lock_requests_by_fid(files_struct *fsp, struct byte_range_lock *br_lck) { } + +NTSTATUS dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOOL expect_close,uint16 spid, + const char *wcard, BOOL wcard_has_wild, uint32 attr, struct dptr_struct **dptr_ret) +{ + return NT_STATUS_OK; +} + +int dptr_CloseDir(struct dptr_struct *dptr) +{ + return 0; +} + +const char *dptr_ReadDirName(struct dptr_struct *dptr, long *poffset, SMB_STRUCT_STAT *pst) +{ + return NULL; +} diff --git a/source3/locking/locking.c b/source3/locking/locking.c index 13c7724656..111fc98bbe 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -1152,6 +1152,46 @@ NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close, return NT_STATUS_ACCESS_DENIED; } + /* Don't allow delete on close for non-empty directories. */ + if (fsp->is_directory) { + long offset = 0; + NTSTATUS status; + SMB_STRUCT_STAT st; + struct dptr_struct *dirptr; + const char *name; + + status = dptr_create(fsp->conn, + fsp->fsp_name, + False, + True, + 0, + "*", + True, + 0, + &dirptr); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* Read 3 entries. Ignore first 2 (they're . and .. ) */ + name = dptr_ReadDirName(dirptr, &offset, &st); + if (!name) { + dptr_CloseDir(dirptr); + return NT_STATUS_ACCESS_DENIED; + } + name = dptr_ReadDirName(dirptr, &offset, &st); + if (!name) { + dptr_CloseDir(dirptr); + return NT_STATUS_ACCESS_DENIED; + } + name = dptr_ReadDirName(dirptr, &offset, &st); + dptr_CloseDir(dirptr); + if (name) { + DEBUG(10,("can_set_delete_on_close: got name %s - can't delete\n", name )); + return NT_STATUS_DIRECTORY_NOT_EMPTY; + } + } + return NT_STATUS_OK; } diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 7be5c03f1b..98356882aa 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -383,7 +383,7 @@ static void dptr_close_oldest(BOOL old) ****************************************************************************/ NTSTATUS dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOOL expect_close,uint16 spid, - const char *wcard, BOOL wcard_has_wild, uint32 attr, int *dptr_hnd_ret) + const char *wcard, BOOL wcard_has_wild, uint32 attr, struct dptr_struct **dptr_ret) { struct dptr_struct *dptr = NULL; struct smb_Dir *dir_hnd; @@ -392,8 +392,6 @@ NTSTATUS dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOO DEBUG(5,("dptr_create dir=%s\n", path)); - *dptr_hnd_ret = -1; - if (!wcard) { return NT_STATUS_INVALID_PARAMETER; } @@ -517,9 +515,8 @@ NTSTATUS dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOO DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n", dptr->dnum,path,expect_close)); - conn->dirptr = dptr; + *dptr_ret = dptr; - *dptr_hnd_ret = dptr->dnum; return NT_STATUS_OK; } @@ -530,6 +527,7 @@ NTSTATUS dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOO int dptr_CloseDir(struct dptr_struct *dptr) { + DLIST_REMOVE(dirptrs, dptr); return CloseDir(dptr->dir_hnd); } @@ -548,6 +546,11 @@ BOOL dptr_has_wild(struct dptr_struct *dptr) return dptr->has_wild; } +int dptr_dnum(struct dptr_struct *dptr) +{ + return dptr->dnum; +} + /**************************************************************************** Return the next visible file name, skipping veto'd and invisible files. ****************************************************************************/ diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index ac7beabb53..51a4093f29 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -652,11 +652,13 @@ int reply_ntcreate_and_X(connection_struct *conn, if (lp_acl_check_permissions(SNUM(conn)) && (create_disposition != FILE_CREATE) && (share_access & FILE_SHARE_DELETE) - && (access_mask & DELETE_ACCESS) - && !can_delete_file_in_directory(conn, fname)) { - restore_case_semantics(conn, file_attributes); - END_PROFILE(SMBntcreateX); - return ERROR_NT(NT_STATUS_ACCESS_DENIED); + && (access_mask & DELETE_ACCESS)) { + if ((dos_mode(conn, fname, &sbuf) & FILE_ATTRIBUTE_READONLY) || + !can_delete_file_in_directory(conn, fname)) { + restore_case_semantics(conn, file_attributes); + END_PROFILE(SMBntcreateX); + return ERROR_NT(NT_STATUS_ACCESS_DENIED); + } } /* @@ -1277,10 +1279,13 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o if (lp_acl_check_permissions(SNUM(conn)) && (create_disposition != FILE_CREATE) && (share_access & FILE_SHARE_DELETE) - && (access_mask & DELETE_ACCESS) - && !can_delete_file_in_directory(conn, fname)) { - restore_case_semantics(conn, file_attributes); - return ERROR_NT(NT_STATUS_ACCESS_DENIED); + && (access_mask & DELETE_ACCESS)) { + if ((dos_mode(conn, fname, &sbuf) & FILE_ATTRIBUTE_READONLY) || + !can_delete_file_in_directory(conn, fname)) { + restore_case_semantics(conn, file_attributes); + END_PROFILE(SMBntcreateX); + return ERROR_NT(NT_STATUS_ACCESS_DENIED); + } } if (ea_len) { diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 4249c6e85b..c0638c0039 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -2110,15 +2110,17 @@ NTSTATUS open_directory(connection_struct *conn, always to be honored on close... See test 19 in Samba4 BASE-DELETE. */ if (create_options & FILE_DELETE_ON_CLOSE) { status = can_set_delete_on_close(fsp, True, 0); - if (!NT_STATUS_IS_OK(status)) { + if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_DIRECTORY_NOT_EMPTY)) { TALLOC_FREE(lck); file_free(fsp); return status; } - set_delete_on_close_token(lck, ¤t_user.ut); - lck->initial_delete_on_close = True; - lck->modified = True; + if (NT_STATUS_IS_OK(status)) { + set_delete_on_close_token(lck, ¤t_user.ut); + lck->initial_delete_on_close = True; + lck->modified = True; + } } TALLOC_FREE(lck); diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c index c5da33c9df..630e270de3 100644 --- a/source3/smbd/posix_acls.c +++ b/source3/smbd/posix_acls.c @@ -4221,6 +4221,11 @@ BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname) if (sbuf.st_mode & S_ISVTX) { SMB_STRUCT_STAT sbuf_file; if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) { + if (errno == ENOENT) { + /* If the file doesn't already exist then + * yes we'll be able to delete it. */ + return True; + } return False; } /* diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 4d139be98f..2075939f5b 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1013,10 +1013,19 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size p = smb_buf(outbuf) + 3; if (status_len == 0) { - nt_status = dptr_create(conn,directory,True,expect_close,SVAL(inbuf,smb_pid), mask, mask_contains_wcard, dirtype,&dptr_num); + nt_status = dptr_create(conn, + directory, + True, + expect_close, + SVAL(inbuf,smb_pid), + mask, + mask_contains_wcard, + dirtype, + &conn->dirptr); if (!NT_STATUS_IS_OK(nt_status)) { return ERROR_NT(nt_status); } + dptr_num = dptr_dnum(conn->dirptr); } else { dirtype = dptr_attr(dptr_num); } diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index e91a2134fd..c9cb2c5b4f 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -1794,13 +1794,23 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd /* Save the wildcard match and attribs we are using on this directory - needed as lanman2 assumes these are being saved between calls */ - ntstatus = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid), mask, mask_contains_wcard, dirtype, &dptr_num); + ntstatus = dptr_create(conn, + directory, + False, + True, + SVAL(inbuf,smb_pid), + mask, + mask_contains_wcard, + dirtype, + &conn->dirptr); + if (!NT_STATUS_IS_OK(ntstatus)) { talloc_destroy(ea_ctx); return ERROR_NT(ntstatus); } - DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, mask, dirtype)); + dptr_num = dptr_dnum(conn->dirptr); + DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n", dptr_num, mask, dirtype)); /* We don't need to check for VOL here as this is returned by a different TRANS2 call. */ -- cgit