diff options
author | Jeremy Allison <jra@samba.org> | 2011-01-25 14:23:19 -0800 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2011-01-25 14:23:19 -0800 |
commit | 44732734cca2328a8aceb2db9b577c923920f644 (patch) | |
tree | 05bc735662ba82137e96479322c4d3737bff9693 /source3/smbd | |
parent | a65bce4e38d0b940286c7c93c226651e5fb45082 (diff) | |
download | samba-44732734cca2328a8aceb2db9b577c923920f644.tar.gz samba-44732734cca2328a8aceb2db9b577c923920f644.tar.bz2 samba-44732734cca2328a8aceb2db9b577c923920f644.zip |
Fix bug #7863 - Unlink may unlink wrong file when hardlinks are involved.
Do this by keeping a linked list of delete on close tokens, one for
each filename that identifies a path to the dev/inode. Use the
jenkins hash of the pathname to identify the correct token.
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/close.c | 59 | ||||
-rw-r--r-- | source3/smbd/filename.c | 11 | ||||
-rw-r--r-- | source3/smbd/open.c | 12 | ||||
-rw-r--r-- | source3/smbd/smb2_getinfo.c | 6 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 26 |
5 files changed, 78 insertions, 36 deletions
diff --git a/source3/smbd/close.c b/source3/smbd/close.c index a6610e58f1..25ed9a3c4d 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -275,6 +275,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; + const UNIX_USER_TOKEN *del_token = NULL; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { @@ -328,7 +329,8 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, fsp_str_dbg(fsp))); } - if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) { + if (fsp->initial_delete_on_close && + !is_delete_on_close_set(lck, fsp->name_hash)) { bool became_user = False; /* Initial delete on close was set and no one else @@ -339,21 +341,23 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, became_user = True; } fsp->delete_on_close = true; - set_delete_on_close_lck(lck, True, get_current_utok(conn)); + set_delete_on_close_lck(fsp, lck, True, get_current_utok(conn)); if (became_user) { unbecome_user(); } } - delete_file = lck->delete_on_close; + delete_file = is_delete_on_close_set(lck, fsp->name_hash); if (delete_file) { int i; - /* See if others still have the file open. If this is the - * case, then don't delete. If all opens are POSIX delete now. */ + /* See if others still have the file open via this pathname. + If this is the case, then don't delete. If all opens are + POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; - if (is_valid_share_mode_entry(e)) { + if (is_valid_share_mode_entry(e) && + e->name_hash == fsp->name_hash) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } @@ -372,9 +376,8 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, * reference to a file. */ - if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) - || !delete_file - || (lck->delete_token == NULL)) { + if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) || + !delete_file) { TALLOC_FREE(lck); return NT_STATUS_OK; } @@ -391,23 +394,26 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, */ fsp->update_write_time_on_close = false; - if (!unix_token_equal(lck->delete_token, get_current_utok(conn))) { + del_token = get_delete_on_close_token(lck, fsp->name_hash); + SMB_ASSERT(del_token != NULL); + + if (!unix_token_equal(del_token, get_current_utok(conn))) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), - (unsigned int)lck->delete_token->uid)); + (unsigned int)del_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } - set_sec_ctx(lck->delete_token->uid, - lck->delete_token->gid, - lck->delete_token->ngroups, - lck->delete_token->groups, + set_sec_ctx(del_token->uid, + del_token->gid, + del_token->ngroups, + del_token->groups, NULL); changed_user = true; @@ -481,7 +487,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, */ fsp->delete_on_close = false; - set_delete_on_close_lck(lck, False, NULL); + set_delete_on_close_lck(fsp, lck, false, NULL); done: @@ -944,6 +950,7 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, bool delete_dir = False; NTSTATUS status = NT_STATUS_OK; NTSTATUS status1 = NT_STATUS_OK; + const UNIX_USER_TOKEN *del_token = NULL; /* * NT can set delete_on_close of the last open @@ -978,14 +985,16 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, } send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx, fsp->fsp_name->base_name); - set_delete_on_close_lck(lck, True, get_current_utok(fsp->conn)); + set_delete_on_close_lck(fsp, lck, true, + get_current_utok(fsp->conn)); fsp->delete_on_close = true; if (became_user) { unbecome_user(); } } - delete_dir = lck->delete_on_close; + del_token = get_delete_on_close_token(lck, fsp->name_hash); + delete_dir = (del_token != NULL); if (delete_dir) { int i; @@ -993,7 +1002,8 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; - if (is_valid_share_mode_entry(e)) { + if (is_valid_share_mode_entry(e) && + e->name_hash == fsp->name_hash) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } @@ -1004,8 +1014,7 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, } if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && - delete_dir && - lck->delete_token) { + delete_dir) { /* Become the user who requested the delete. */ @@ -1013,10 +1022,10 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, smb_panic("close_directory: failed to push sec_ctx.\n"); } - set_sec_ctx(lck->delete_token->uid, - lck->delete_token->gid, - lck->delete_token->ngroups, - lck->delete_token->groups, + set_sec_ctx(del_token->uid, + del_token->gid, + del_token->ngroups, + del_token->groups, NULL); TALLOC_FREE(lck); diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 1194660565..03877218de 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -857,9 +857,18 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx, */ if (VALID_STAT(smb_fname->st)) { bool delete_pending; + uint32_t name_hash; + + status = file_name_hash(conn, + smb_fname_str_dbg(smb_fname), + &name_hash); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + get_file_infos(vfs_file_id_from_sbuf(conn, &smb_fname->st), - 0, + name_hash, &delete_pending, NULL); if (delete_pending) { status = NT_STATUS_DELETE_PENDING; diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 722e461068..0de70451da 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -810,6 +810,7 @@ bool is_stat_open(uint32 access_mask) static NTSTATUS open_mode_check(connection_struct *conn, struct share_mode_lock *lck, + uint32_t name_hash, uint32 access_mask, uint32 share_access, uint32 create_options, @@ -825,7 +826,7 @@ static NTSTATUS open_mode_check(connection_struct *conn, /* A delete on close prohibits everything */ - if (lck->delete_on_close) { + if (is_delete_on_close_set(lck, name_hash)) { return NT_STATUS_DELETE_PENDING; } @@ -1823,7 +1824,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, /* Use the client requested access mask here, not the one we * open with. */ - status = open_mode_check(conn, lck, access_mask, share_access, + status = open_mode_check(conn, lck, fsp->name_hash, + access_mask, share_access, create_options, &file_existed); if (NT_STATUS_IS_OK(status)) { @@ -2045,7 +2047,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, return NT_STATUS_SHARING_VIOLATION; } - status = open_mode_check(conn, lck, access_mask, share_access, + status = open_mode_check(conn, lck, fsp->name_hash, + access_mask, share_access, create_options, &file_existed); if (NT_STATUS_IS_OK(status)) { @@ -2625,7 +2628,8 @@ static NTSTATUS open_directory(connection_struct *conn, return NT_STATUS_SHARING_VIOLATION; } - status = open_mode_check(conn, lck, access_mask, share_access, + status = open_mode_check(conn, lck, fsp->name_hash, + access_mask, share_access, create_options, &dir_existed); if (!NT_STATUS_IS_OK(status)) { diff --git a/source3/smbd/smb2_getinfo.c b/source3/smbd/smb2_getinfo.c index 0ff659346e..e6bf893ac2 100644 --- a/source3/smbd/smb2_getinfo.c +++ b/source3/smbd/smb2_getinfo.c @@ -320,7 +320,8 @@ static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx, fileid = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); - get_file_infos(fileid, 0, &delete_pending, &write_time_ts); + get_file_infos(fileid, fsp->name_hash, + &delete_pending, &write_time_ts); } else { /* * Original code - this is an open file. @@ -336,7 +337,8 @@ static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx, } fileid = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); - get_file_infos(fileid, 0, &delete_pending, &write_time_ts); + get_file_infos(fileid, fsp->name_hash, + &delete_pending, &write_time_ts); } status = smbd_do_qfilepathinfo(conn, state, diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 60664fd229..5865d05e44 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -5103,7 +5103,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn, } fileid = vfs_file_id_from_sbuf(conn, &smb_fname->st); - get_file_infos(fileid, 0, &delete_pending, &write_time_ts); + get_file_infos(fileid, fsp->name_hash, &delete_pending, &write_time_ts); } else { /* * Original code - this is an open file. @@ -5120,10 +5120,11 @@ static void call_trans2qfilepathinfo(connection_struct *conn, return; } fileid = vfs_file_id_from_sbuf(conn, &smb_fname->st); - get_file_infos(fileid, 0, &delete_pending, &write_time_ts); + get_file_infos(fileid, fsp->name_hash, &delete_pending, &write_time_ts); } } else { + uint32_t name_hash; char *fname = NULL; /* qpathinfo */ @@ -5210,10 +5211,19 @@ static void call_trans2qfilepathinfo(connection_struct *conn, } } + status = file_name_hash(conn, + smb_fname_str_dbg(smb_fname_base), + &name_hash); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(smb_fname_base); + reply_nterror(req, status); + return; + } + fileid = vfs_file_id_from_sbuf(conn, &smb_fname_base->st); TALLOC_FREE(smb_fname_base); - get_file_infos(fileid, 0, &delete_pending, NULL); + get_file_infos(fileid, name_hash, &delete_pending, NULL); if (delete_pending) { reply_nterror(req, NT_STATUS_DELETE_PENDING); return; @@ -5244,8 +5254,16 @@ static void call_trans2qfilepathinfo(connection_struct *conn, } } + status = file_name_hash(conn, + smb_fname_str_dbg(smb_fname), + &name_hash); + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + fileid = vfs_file_id_from_sbuf(conn, &smb_fname->st); - get_file_infos(fileid, 0, &delete_pending, &write_time_ts); + get_file_infos(fileid, name_hash, &delete_pending, &write_time_ts); if (delete_pending) { reply_nterror(req, NT_STATUS_DELETE_PENDING); return; |