Fix bug #7863 - Unlink may unlink wrong file when hardlinks are involved.
authorJeremy Allison <jra@samba.org>
Tue, 25 Jan 2011 22:23:19 +0000 (14:23 -0800)
committerJeremy Allison <jra@samba.org>
Tue, 25 Jan 2011 22:23:19 +0000 (14:23 -0800)
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.

source3/include/proto.h
source3/include/smb.h
source3/libsmb/smb_share_modes.c
source3/locking/locking.c
source3/smbd/close.c
source3/smbd/filename.c
source3/smbd/open.c
source3/smbd/smb2_getinfo.c
source3/smbd/trans2.c

index 372ed16fd51f811ec59750a49f27a5a9615b5fcd..4c7d4f3d4250f394ed92982d4cc3ac2026c00577 100644 (file)
@@ -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 *,
index 5269c2348fc08faa9f468bc705942ce1925d0d98..95c1d62357cac9ae092e7e419c34979b64e8a79a 100644 (file)
@@ -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[];
         */
 };
 
index 2eda7a499fd3a6bb553f95b3a148bc49559e90c7..9392349b101b0fd9fbef2290def03e271cae1aae 100644 (file)
@@ -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);
 
index 0d7d4eecf6006dd00c8ca2871ed1e6b9c20db74a..f98208fbad2cc978577a7719b5e25652c5a8f587 100644 (file)
@@ -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++) {
index a6610e58f1cf6ba6f144ba669ce2f2535bea57f6..25ed9a3c4db7726ecad22a8beaf94df387ac556c 100644 (file)
@@ -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);
index 1194660565a819056b30cc878aec16b9371ffad9..03877218de57a4caba492c990680031246343440 100644 (file)
@@ -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;
index 722e46106889c6058a8c6b9b16a5a9d80ed16d6c..0de70451daa7a66d6ad6e695a6642ca13b60bfbc 100644 (file)
@@ -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)) {
index 0ff659346eddf908a32df1ce45c54cf62beadec0..e6bf893ac25c70973dc300b1ee5762670162c400 100644 (file)
@@ -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,
index 60664fd2295f7be36c74dd49f18b105f11205d86..5865d05e44fe5d326bbbd6984d6e9def15f37f75 100644 (file)
@@ -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;