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 | |
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')
-rw-r--r-- | source3/include/proto.h | 8 | ||||
-rw-r--r-- | source3/include/smb.h | 28 | ||||
-rw-r--r-- | source3/libsmb/smb_share_modes.c | 9 | ||||
-rw-r--r-- | source3/locking/locking.c | 332 | ||||
-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 |
9 files changed, 345 insertions, 146 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h index 372ed16fd5..4c7d4f3d42 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -3089,9 +3089,13 @@ void del_deferred_open_entry(struct share_mode_lock *lck, uint64_t mid, bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp); bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp); NTSTATUS can_set_delete_on_close(files_struct *fsp, uint32 dosmode); -void set_delete_on_close_token(struct share_mode_lock *lck, const UNIX_USER_TOKEN *tok); -void set_delete_on_close_lck(struct share_mode_lock *lck, bool delete_on_close, const UNIX_USER_TOKEN *tok); +const UNIX_USER_TOKEN *get_delete_on_close_token(struct share_mode_lock *lck, uint32_t name_hash); +void set_delete_on_close_lck(files_struct *fsp, + struct share_mode_lock *lck, + bool delete_on_close, + const UNIX_USER_TOKEN *tok); bool set_delete_on_close(files_struct *fsp, bool delete_on_close, const UNIX_USER_TOKEN *tok); +bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash); bool set_sticky_write_time(struct file_id fileid, struct timespec write_time); bool set_write_time(struct file_id fileid, struct timespec write_time); int share_mode_forall(void (*fn)(const struct share_mode_entry *, const char *, diff --git a/source3/include/smb.h b/source3/include/smb.h index 5269c2348f..95c1d62357 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -733,6 +733,12 @@ Offset Data length. #define MSG_SMB_SHARE_MODE_ENTRY_SIZE 72 #endif +struct delete_token_list { + struct delete_token_list *next, *prev; + uint32_t name_hash; + UNIX_USER_TOKEN *delete_token; +}; + struct share_mode_lock { const char *servicepath; /* canonicalized. */ const char *base_name; @@ -740,8 +746,7 @@ struct share_mode_lock { struct file_id id; int num_share_modes; struct share_mode_entry *share_modes; - UNIX_USER_TOKEN *delete_token; - bool delete_on_close; + struct delete_token_list *delete_tokens; struct timespec old_write_time; struct timespec changed_write_time; bool fresh; @@ -758,20 +763,23 @@ struct locking_data { union { struct { int num_share_mode_entries; - bool delete_on_close; struct timespec old_write_time; struct timespec changed_write_time; - uint32 delete_token_size; /* Only valid if either of - the two previous fields - are True. */ + uint32 num_delete_token_entries; } s; struct share_mode_entry dummy; /* Needed for alignment. */ } u; /* The following four entries are implicit - struct share_mode_entry modes[num_share_mode_entries]; - char unix_token[delete_token_size] (divisible by 4). - char share_name[]; - char file_name[]; + + (1) struct share_mode_entry modes[num_share_mode_entries]; + + (2) A num_delete_token_entries of structs { + uint32_t len_delete_token; + char unix_token[len_delete_token] (divisible by 4). + }; + + (3) char share_name[]; + (4) char file_name[]; */ }; diff --git a/source3/libsmb/smb_share_modes.c b/source3/libsmb/smb_share_modes.c index 2eda7a499f..9392349b10 100644 --- a/source3/libsmb/smb_share_modes.c +++ b/source3/libsmb/smb_share_modes.c @@ -257,7 +257,7 @@ int smb_get_share_mode_entries(struct smbdb_ctx *db_ctx, return 0; } - *p_delete_on_close = ld->u.s.delete_on_close; + *p_delete_on_close = ld->u.s.num_delete_token_entries != 0; *pp_list = list; free(db_data.dptr); return list_num; @@ -324,8 +324,7 @@ int smb_create_share_mode_entry_ex(struct smbdb_ctx *db_ctx, ld = (struct locking_data *)db_data.dptr; memset(ld, '\0', sizeof(struct locking_data)); ld->u.s.num_share_mode_entries = 1; - ld->u.s.delete_on_close = 0; - ld->u.s.delete_token_size = 0; + ld->u.s.num_delete_token_entries = 0; shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct locking_data)); create_share_mode_entry(shares, new_entry, name_hash); @@ -371,7 +370,7 @@ int smb_create_share_mode_entry_ex(struct smbdb_ctx *db_ctx, ld = (struct locking_data *)new_data_p; ld->u.s.num_share_mode_entries++; - /* Append the original delete_token and filenames. */ + /* Append the original delete_tokens and filenames. */ memcpy(new_data_p + sizeof(struct locking_data) + (ld->u.s.num_share_mode_entries * sizeof(struct share_mode_entry)), db_data.dptr + sizeof(struct locking_data) + (orig_num_share_modes * sizeof(struct share_mode_entry)), db_data.dsize - sizeof(struct locking_data) - (orig_num_share_modes * sizeof(struct share_mode_entry))); @@ -488,7 +487,7 @@ int smb_delete_share_mode_entry(struct smbdb_ctx *db_ctx, return tdb_delete(db_ctx->smb_tdb, locking_key); } - /* Copy any delete token plus the terminating filenames. */ + /* Copy any delete tokens plus the terminating filenames. */ remaining_ptr = db_data.dptr + sizeof(struct locking_data) + (orig_num_share_modes * sizeof(struct share_mode_entry)); remaining_size = db_data.dsize - (remaining_ptr - db_data.dptr); diff --git a/source3/locking/locking.c b/source3/locking/locking.c index 0d7d4eecf6..f98208fbad 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -533,6 +533,104 @@ static void print_share_mode_table(struct locking_data *data) } } +static int parse_delete_tokens_list(struct share_mode_lock *lck, + struct locking_data *pdata, + const TDB_DATA dbuf) +{ + uint8_t *p = dbuf.dptr + sizeof(struct locking_data) + + (lck->num_share_modes * + sizeof(struct share_mode_entry)); + uint8_t *end_ptr = dbuf.dptr + (dbuf.dsize - 2); + int delete_tokens_size = 0; + int i; + + lck->delete_tokens = NULL; + + for (i = 0; i < pdata->u.s.num_delete_token_entries; i++) { + uint32_t token_len; + struct delete_token_list *pdtl; + + if (end_ptr - p < (sizeof(uint32_t) + sizeof(uint32_t) + + sizeof(uid_t) + sizeof(gid_t))) { + DEBUG(0,("parse_delete_tokens_list: " + "corrupt token list (%u)", + (unsigned int)(end_ptr - p))); + smb_panic("corrupt token list"); + return -1; + } + + memcpy(&token_len, p, sizeof(token_len)); + delete_tokens_size += token_len; + + if (p + token_len > end_ptr || token_len < sizeof(token_len) + + sizeof(pdtl->name_hash) + + sizeof(uid_t) + + sizeof(gid_t)) { + DEBUG(0,("parse_delete_tokens_list: " + "invalid token length (%u)\n", + (unsigned int)token_len )); + smb_panic("invalid token length"); + return -1; + } + + p += sizeof(token_len); + + pdtl = TALLOC_ZERO_P(lck, struct delete_token_list); + if (pdtl == NULL) { + DEBUG(0,("parse_delete_tokens_list: talloc failed")); + return -1; + } + /* Copy out the name_hash. */ + memcpy(&pdtl->name_hash, p, sizeof(pdtl->name_hash)); + p += sizeof(pdtl->name_hash); + + pdtl->delete_token = TALLOC_ZERO_P(pdtl, UNIX_USER_TOKEN); + if (pdtl->delete_token == NULL) { + DEBUG(0,("parse_delete_tokens_list: talloc failed")); + return -1; + } + + /* Copy out the uid and gid. */ + memcpy(&pdtl->delete_token->uid, p, sizeof(uid_t)); + p += sizeof(uid_t); + memcpy(&pdtl->delete_token->gid, p, sizeof(gid_t)); + p += sizeof(gid_t); + + token_len -= (sizeof(token_len) + sizeof(pdtl->name_hash) + + sizeof(uid_t) + sizeof(gid_t)); + + /* Any supplementary groups ? */ + if (token_len) { + int j; + + if (token_len % sizeof(gid_t) != 0) { + DEBUG(0,("parse_delete_tokens_list: " + "corrupt group list (%u)", + (unsigned int)(token_len % sizeof(gid_t)) )); + smb_panic("corrupt group list"); + return -1; + } + + pdtl->delete_token->ngroups = token_len / sizeof(gid_t); + pdtl->delete_token->groups = TALLOC_ARRAY(pdtl->delete_token, gid_t, + pdtl->delete_token->ngroups); + if (pdtl->delete_token->groups == NULL) { + DEBUG(0,("parse_delete_tokens_list: talloc failed")); + return -1; + } + + for (j = 0; j < pdtl->delete_token->ngroups; j++) { + memcpy(&pdtl->delete_token->groups[j], p, sizeof(gid_t)); + p += sizeof(gid_t); + } + } + /* Add to the list. */ + DLIST_ADD(lck->delete_tokens, pdtl); + } + + return delete_tokens_size; +} + /******************************************************************* Get all share mode entries for a dev/inode pair. ********************************************************************/ @@ -540,6 +638,7 @@ static void print_share_mode_table(struct locking_data *data) static bool parse_share_modes(const TDB_DATA dbuf, struct share_mode_lock *lck) { struct locking_data data; + int delete_tokens_size; int i; if (dbuf.dsize < sizeof(struct locking_data)) { @@ -548,20 +647,18 @@ static bool parse_share_modes(const TDB_DATA dbuf, struct share_mode_lock *lck) memcpy(&data, dbuf.dptr, sizeof(data)); - lck->delete_on_close = data.u.s.delete_on_close; lck->old_write_time = data.u.s.old_write_time; lck->changed_write_time = data.u.s.changed_write_time; lck->num_share_modes = data.u.s.num_share_mode_entries; - DEBUG(10, ("parse_share_modes: delete_on_close: %d, owrt: %s, " - "cwrt: %s, tok: %u, num_share_modes: %d\n", - lck->delete_on_close, + DEBUG(10, ("parse_share_modes: owrt: %s, " + "cwrt: %s, ntok: %u, num_share_modes: %d\n", timestring(talloc_tos(), convert_timespec_to_time_t(lck->old_write_time)), timestring(talloc_tos(), convert_timespec_to_time_t( lck->changed_write_time)), - (unsigned int)data.u.s.delete_token_size, + (unsigned int)data.u.s.num_delete_token_entries, lck->num_share_modes)); if ((lck->num_share_modes < 0) || (lck->num_share_modes > 1000000)) { @@ -591,66 +688,25 @@ static bool parse_share_modes(const TDB_DATA dbuf, struct share_mode_lock *lck) } } - /* Get any delete token. */ - if (data.u.s.delete_token_size) { - uint8 *p = dbuf.dptr + sizeof(struct locking_data) + - (lck->num_share_modes * - sizeof(struct share_mode_entry)); - - if ((data.u.s.delete_token_size < sizeof(uid_t) + sizeof(gid_t)) || - ((data.u.s.delete_token_size - sizeof(uid_t)) % sizeof(gid_t)) != 0) { - DEBUG(0, ("parse_share_modes: invalid token size %d\n", - data.u.s.delete_token_size)); - smb_panic("parse_share_modes: invalid token size"); - } - - lck->delete_token = TALLOC_P(lck, UNIX_USER_TOKEN); - if (!lck->delete_token) { - smb_panic("parse_share_modes: talloc failed"); - } - - /* Copy out the uid and gid. */ - memcpy(&lck->delete_token->uid, p, sizeof(uid_t)); - p += sizeof(uid_t); - memcpy(&lck->delete_token->gid, p, sizeof(gid_t)); - p += sizeof(gid_t); - - /* Any supplementary groups ? */ - lck->delete_token->ngroups = (data.u.s.delete_token_size > (sizeof(uid_t) + sizeof(gid_t))) ? - ((data.u.s.delete_token_size - - (sizeof(uid_t) + sizeof(gid_t)))/sizeof(gid_t)) : 0; - - if (lck->delete_token->ngroups) { - /* Make this a talloc child of lck->delete_token. */ - lck->delete_token->groups = TALLOC_ARRAY(lck->delete_token, gid_t, - lck->delete_token->ngroups); - if (!lck->delete_token) { - smb_panic("parse_share_modes: talloc failed"); - } - - for (i = 0; i < lck->delete_token->ngroups; i++) { - memcpy(&lck->delete_token->groups[i], p, sizeof(gid_t)); - p += sizeof(gid_t); - } - } - - } else { - lck->delete_token = NULL; + /* Get any delete tokens. */ + delete_tokens_size = parse_delete_tokens_list(lck, &data, dbuf); + if (delete_tokens_size < 0) { + smb_panic("parse_share_modes: parse_delete_tokens_list failed"); } /* Save off the associated service path and filename. */ lck->servicepath = (const char *)dbuf.dptr + sizeof(struct locking_data) + (lck->num_share_modes * sizeof(struct share_mode_entry)) + - data.u.s.delete_token_size; + delete_tokens_size; lck->base_name = (const char *)dbuf.dptr + sizeof(struct locking_data) + (lck->num_share_modes * sizeof(struct share_mode_entry)) + - data.u.s.delete_token_size + + delete_tokens_size + strlen(lck->servicepath) + 1; lck->stream_name = (const char *)dbuf.dptr + sizeof(struct locking_data) + (lck->num_share_modes * sizeof(struct share_mode_entry)) + - data.u.s.delete_token_size + + delete_tokens_size + strlen(lck->servicepath) + 1 + strlen(lck->base_name) + 1; @@ -686,7 +742,9 @@ static TDB_DATA unparse_share_modes(const struct share_mode_lock *lck) struct locking_data *data; ssize_t offset; ssize_t sp_len, bn_len, sn_len; - uint32 delete_token_size; + uint32_t delete_tokens_size = 0; + struct delete_token_list *pdtl = NULL; + uint32_t num_delete_token_entries = 0; result.dptr = NULL; result.dsize = 0; @@ -705,12 +763,18 @@ static TDB_DATA unparse_share_modes(const struct share_mode_lock *lck) bn_len = strlen(lck->base_name); sn_len = lck->stream_name != NULL ? strlen(lck->stream_name) : 0; - delete_token_size = (lck->delete_token ? - (sizeof(uid_t) + sizeof(gid_t) + (lck->delete_token->ngroups*sizeof(gid_t))) : 0); + for (pdtl = lck->delete_tokens; pdtl; pdtl = pdtl->next) { + num_delete_token_entries++; + delete_tokens_size += (sizeof(uint32_t) + + sizeof(uint32_t) + + sizeof(uid_t) + + sizeof(gid_t) + + pdtl->delete_token->ngroups*sizeof(gid_t)); + } result.dsize = sizeof(*data) + lck->num_share_modes * sizeof(struct share_mode_entry) + - delete_token_size + + delete_tokens_size + sp_len + 1 + bn_len + 1 + sn_len + 1; @@ -723,19 +787,18 @@ static TDB_DATA unparse_share_modes(const struct share_mode_lock *lck) data = (struct locking_data *)result.dptr; ZERO_STRUCTP(data); data->u.s.num_share_mode_entries = lck->num_share_modes; - data->u.s.delete_on_close = lck->delete_on_close; data->u.s.old_write_time = lck->old_write_time; data->u.s.changed_write_time = lck->changed_write_time; - data->u.s.delete_token_size = delete_token_size; + data->u.s.num_delete_token_entries = num_delete_token_entries; - DEBUG(10,("unparse_share_modes: del: %d, owrt: %s cwrt: %s, tok: %u, " - "num: %d\n", data->u.s.delete_on_close, + DEBUG(10,("unparse_share_modes: owrt: %s cwrt: %s, ntok: %u, " + "num: %d\n", timestring(talloc_tos(), convert_timespec_to_time_t(lck->old_write_time)), timestring(talloc_tos(), convert_timespec_to_time_t( lck->changed_write_time)), - (unsigned int)data->u.s.delete_token_size, + (unsigned int)data->u.s.num_delete_token_entries, data->u.s.num_share_mode_entries)); memcpy(result.dptr + sizeof(*data), lck->share_modes, @@ -743,21 +806,33 @@ static TDB_DATA unparse_share_modes(const struct share_mode_lock *lck) offset = sizeof(*data) + sizeof(struct share_mode_entry)*lck->num_share_modes; - /* Store any delete on close token. */ - if (lck->delete_token) { - uint8 *p = result.dptr + offset; + /* Store any delete on close tokens. */ + for (pdtl = lck->delete_tokens; pdtl; pdtl = pdtl->next) { + UNIX_USER_TOKEN *pdt = pdtl->delete_token; + uint32_t token_size = sizeof(uint32_t) + + sizeof(uint32_t) + + sizeof(uid_t) + + sizeof(gid_t) + + (pdt->ngroups * sizeof(gid_t)); + uint8_t *p = result.dptr + offset; - memcpy(p, &lck->delete_token->uid, sizeof(uid_t)); + memcpy(p, &token_size, sizeof(uint32_t)); + p += sizeof(uint32_t); + + memcpy(p, &pdtl->name_hash, sizeof(uint32_t)); + p += sizeof(uint32_t); + + memcpy(p, &pdt->uid, sizeof(uid_t)); p += sizeof(uid_t); - memcpy(p, &lck->delete_token->gid, sizeof(gid_t)); + memcpy(p, &pdt->gid, sizeof(gid_t)); p += sizeof(gid_t); - for (i = 0; i < lck->delete_token->ngroups; i++) { - memcpy(p, &lck->delete_token->groups[i], sizeof(gid_t)); + for (i = 0; i < pdt->ngroups; i++) { + memcpy(p, &pdt->groups[i], sizeof(gid_t)); p += sizeof(gid_t); } - offset = p - result.dptr; + offset += token_size; } safe_strcpy((char *)result.dptr + offset, lck->servicepath, @@ -844,8 +919,7 @@ static bool fill_share_mode_lock(struct share_mode_lock *lck, lck->id = id; lck->num_share_modes = 0; lck->share_modes = NULL; - lck->delete_token = NULL; - lck->delete_on_close = False; + lck->delete_tokens = NULL; ZERO_STRUCT(lck->old_write_time); ZERO_STRUCT(lck->changed_write_time); lck->fresh = False; @@ -1070,7 +1144,7 @@ void get_file_infos(struct file_id id, } if (delete_on_close) { - *delete_on_close = lck->delete_on_close; + *delete_on_close = is_delete_on_close_set(lck, name_hash); } if (write_time) { @@ -1395,10 +1469,6 @@ static UNIX_USER_TOKEN *copy_unix_token(TALLOC_CTX *ctx, const UNIX_USER_TOKEN * { UNIX_USER_TOKEN *cpy; - if (tok == NULL) { - return NULL; - } - cpy = TALLOC_P(ctx, UNIX_USER_TOKEN); if (!cpy) { return NULL; @@ -1419,16 +1489,29 @@ static UNIX_USER_TOKEN *copy_unix_token(TALLOC_CTX *ctx, const UNIX_USER_TOKEN * } /**************************************************************************** - Replace the delete on close token. + Adds a delete on close token. ****************************************************************************/ -void set_delete_on_close_token(struct share_mode_lock *lck, const UNIX_USER_TOKEN *tok) +static bool add_delete_on_close_token(struct share_mode_lock *lck, + uint32_t name_hash, + const UNIX_USER_TOKEN *tok) { - TALLOC_FREE(lck->delete_token); /* Also deletes groups... */ + struct delete_token_list *dtl; - /* Copy the new token (can be NULL). */ - lck->delete_token = copy_unix_token(lck, tok); - lck->modified = True; + dtl = TALLOC_ZERO_P(lck, struct delete_token_list); + if (dtl == NULL) { + return false; + } + + dtl->name_hash = name_hash; + dtl->delete_token = copy_unix_token(lck, tok); + if (dtl->delete_token == NULL) { + TALLOC_FREE(dtl); + return false; + } + DLIST_ADD(lck->delete_tokens, dtl); + lck->modified = true; + return true; } /**************************************************************************** @@ -1442,16 +1525,44 @@ void set_delete_on_close_token(struct share_mode_lock *lck, const UNIX_USER_TOKE lck entry. This function is used when the lock is already granted. ****************************************************************************/ -void set_delete_on_close_lck(struct share_mode_lock *lck, bool delete_on_close, const UNIX_USER_TOKEN *tok) +void set_delete_on_close_lck(files_struct *fsp, + struct share_mode_lock *lck, + bool delete_on_close, + const UNIX_USER_TOKEN *tok) { - if (lck->delete_on_close != delete_on_close) { - set_delete_on_close_token(lck, tok); - lck->delete_on_close = delete_on_close; - if (delete_on_close) { - SMB_ASSERT(lck->delete_token != NULL); + struct delete_token_list *dtl; + bool ret; + + if (delete_on_close) { + SMB_ASSERT(tok != NULL); + } else { + SMB_ASSERT(tok == NULL); + } + + for (dtl = lck->delete_tokens; dtl; dtl = dtl->next) { + if (dtl->name_hash == fsp->name_hash) { + lck->modified = true; + if (delete_on_close == false) { + /* Delete this entry. */ + DLIST_REMOVE(lck->delete_tokens, dtl); + TALLOC_FREE(dtl); + return; + } + /* Replace this token with the + given tok. */ + TALLOC_FREE(dtl->delete_token); + dtl->delete_token = copy_unix_token(dtl, tok); + SMB_ASSERT(dtl->delete_token != NULL); } - lck->modified = True; } + + if (!delete_on_close) { + /* Nothing to delete - not found. */ + return; + } + + ret = add_delete_on_close_token(lck, fsp->name_hash, tok); + SMB_ASSERT(ret); } bool set_delete_on_close(files_struct *fsp, bool delete_on_close, const UNIX_USER_TOKEN *tok) @@ -1469,7 +1580,8 @@ bool set_delete_on_close(files_struct *fsp, bool delete_on_close, const UNIX_USE return False; } - set_delete_on_close_lck(lck, delete_on_close, tok); + set_delete_on_close_lck(fsp, lck, delete_on_close, + delete_on_close ? tok : NULL); if (fsp->is_directory) { SMB_ASSERT(!is_ntfs_stream_smb_fname(fsp->fsp_name)); @@ -1484,6 +1596,28 @@ bool set_delete_on_close(files_struct *fsp, bool delete_on_close, const UNIX_USE return True; } +const UNIX_USER_TOKEN *get_delete_on_close_token(struct share_mode_lock *lck, uint32_t name_hash) +{ + struct delete_token_list *dtl; + + DEBUG(10,("get_delete_on_close_token: name_hash = %u\n", + (unsigned int)name_hash )); + + for (dtl = lck->delete_tokens; dtl; dtl = dtl->next) { + DEBUG(10,("get_delete_on_close_token: dtl->name_hash = %u\n", + (unsigned int)dtl->name_hash )); + if (dtl->name_hash == name_hash) { + return dtl->delete_token; + } + } + return NULL; +} + +bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash) +{ + return (get_delete_on_close_token(lck, name_hash) != NULL); +} + bool set_sticky_write_time(struct file_id fileid, struct timespec write_time) { struct share_mode_lock *lck; @@ -1546,6 +1680,8 @@ static int traverse_fn(struct db_record *rec, void *_state) struct share_mode_entry *shares; const char *sharepath; const char *fname; + const char *del_tokens; + uint32_t total_del_token_size = 0; int i; /* Ensure this is a locking_key record. */ @@ -1554,12 +1690,22 @@ static int traverse_fn(struct db_record *rec, void *_state) data = (struct locking_data *)rec->value.dptr; shares = (struct share_mode_entry *)(rec->value.dptr + sizeof(*data)); + del_tokens = (const char *)rec->value.dptr + sizeof(*data) + + data->u.s.num_share_mode_entries*sizeof(*shares); + + for (i = 0; i < data->u.s.num_delete_token_entries; i++) { + uint32_t del_token_size; + memcpy(&del_token_size, del_tokens, sizeof(uint32_t)); + total_del_token_size += del_token_size; + del_tokens += del_token_size; + } + sharepath = (const char *)rec->value.dptr + sizeof(*data) + data->u.s.num_share_mode_entries*sizeof(*shares) + - data->u.s.delete_token_size; + total_del_token_size; fname = (const char *)rec->value.dptr + sizeof(*data) + data->u.s.num_share_mode_entries*sizeof(*shares) + - data->u.s.delete_token_size + + total_del_token_size + strlen(sharepath) + 1; for (i=0;i<data->u.s.num_share_mode_entries;i++) { 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; |