summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2011-01-25 14:23:19 -0800
committerJeremy Allison <jra@samba.org>2011-01-25 14:23:19 -0800
commit44732734cca2328a8aceb2db9b577c923920f644 (patch)
tree05bc735662ba82137e96479322c4d3737bff9693 /source3/smbd
parenta65bce4e38d0b940286c7c93c226651e5fb45082 (diff)
downloadsamba-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.c59
-rw-r--r--source3/smbd/filename.c11
-rw-r--r--source3/smbd/open.c12
-rw-r--r--source3/smbd/smb2_getinfo.c6
-rw-r--r--source3/smbd/trans2.c26
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;