smbd: Factor out remove_stale_share_mode_entries
[mat/samba.git] / source3 / locking / share_mode_lock.c
index 6bc055f70fea626a4d1d2d776eab24c368327a7e..5d7a08ca4bf018232aba3a6f41dcc08967afd0f1 100644 (file)
@@ -46,6 +46,7 @@
 #include "messages.h"
 #include "util_tdb.h"
 #include "../librpc/gen_ndr/ndr_open_files.h"
+#include "source3/lib/dbwrap/dbwrap_watch.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_LOCKING
@@ -76,6 +77,8 @@ static bool locking_init_internal(bool read_only)
        if (!posix_locking_init(read_only))
                return False;
 
+       dbwrap_watch_db(lock_db, server_messaging_context());
+
        return True;
 }
 
@@ -104,10 +107,9 @@ bool locking_end(void)
  Form a static locking key for a dev/inode pair.
 ******************************************************************/
 
-static TDB_DATA locking_key(const struct file_id *id, struct file_id *tmp)
+static TDB_DATA locking_key(const struct file_id *id)
 {
-       *tmp = *id;
-       return make_tdb_data((const uint8_t *)tmp, sizeof(*tmp));
+       return make_tdb_data((const uint8_t *)id, sizeof(*id));
 }
 
 /*******************************************************************
@@ -118,13 +120,11 @@ static struct share_mode_data *parse_share_modes(TALLOC_CTX *mem_ctx,
                                                 const TDB_DATA dbuf)
 {
        struct share_mode_data *d;
-       int i;
-       struct server_id *pids;
-       bool *pid_exists;
        enum ndr_err_code ndr_err;
+       uint32_t i;
        DATA_BLOB blob;
 
-       d = talloc_zero(mem_ctx, struct share_mode_data);
+       d = talloc(mem_ctx, struct share_mode_data);
        if (d == NULL) {
                DEBUG(0, ("talloc failed\n"));
                goto fail;
@@ -136,10 +136,19 @@ static struct share_mode_data *parse_share_modes(TALLOC_CTX *mem_ctx,
        ndr_err = ndr_pull_struct_blob(
                &blob, d, d, (ndr_pull_flags_fn_t)ndr_pull_share_mode_data);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               DEBUG(1, ("ndr_pull_share_mode_lock failed\n"));
+               DEBUG(1, ("ndr_pull_share_mode_lock failed: %s\n",
+                         ndr_errstr(ndr_err)));
                goto fail;
        }
 
+       /*
+        * Initialize the values that are [skip] in the idl. The NDR code does
+        * not initialize them.
+        */
+
+       for (i=0; i<d->num_share_modes; i++) {
+               d->share_modes[i].stale = false;
+       }
        d->modified = false;
        d->fresh = false;
 
@@ -148,43 +157,6 @@ static struct share_mode_data *parse_share_modes(TALLOC_CTX *mem_ctx,
                NDR_PRINT_DEBUG(share_mode_data, d);
        }
 
-       /*
-        * Ensure that each entry has a real process attached.
-        */
-
-       pids = talloc_array(talloc_tos(), struct server_id,
-                           d->num_share_modes);
-       if (pids == NULL) {
-               DEBUG(0, ("talloc failed\n"));
-               goto fail;
-       }
-       pid_exists = talloc_array(talloc_tos(), bool, d->num_share_modes);
-       if (pid_exists == NULL) {
-               DEBUG(0, ("talloc failed\n"));
-               goto fail;
-       }
-
-       for (i=0; i<d->num_share_modes; i++) {
-               pids[i] = d->share_modes[i].pid;
-       }
-       if (!serverids_exist(pids, d->num_share_modes, pid_exists)) {
-               DEBUG(0, ("serverid_exists failed\n"));
-               goto fail;
-       }
-
-       i = 0;
-       while (i < d->num_share_modes) {
-               struct share_mode_entry *e = &d->share_modes[i];
-               if (!pid_exists[i]) {
-                       *e = d->share_modes[d->num_share_modes-1];
-                       d->num_share_modes -= 1;
-                       d->modified = True;
-                       continue;
-               }
-               i += 1;
-       }
-       TALLOC_FREE(pid_exists);
-       TALLOC_FREE(pids);
        return d;
 fail:
        TALLOC_FREE(d);
@@ -205,6 +177,8 @@ static TDB_DATA unparse_share_modes(struct share_mode_data *d)
                NDR_PRINT_DEBUG(share_mode_data, d);
        }
 
+       remove_stale_share_mode_entries(d);
+
        if (d->num_share_modes == 0) {
                DEBUG(10, ("No used share mode found\n"));
                return make_tdb_data(NULL, 0);
@@ -326,15 +300,14 @@ fail:
 ********************************************************************/
 
 static struct share_mode_lock *get_share_mode_lock_internal(
-       TALLOC_CTX *mem_ctx, const struct file_id id,
+       TALLOC_CTX *mem_ctx, struct file_id id,
        const char *servicepath, const struct smb_filename *smb_fname,
        const struct timespec *old_write_time)
 {
        struct share_mode_lock *lck;
        struct share_mode_data *d;
-       struct file_id tmp;
        struct db_record *rec;
-       TDB_DATA key = locking_key(&id, &tmp);
+       TDB_DATA key = locking_key(&id);
        TDB_DATA value;
 
        rec = dbwrap_fetch_locked(lock_db, mem_ctx, key);
@@ -353,7 +326,8 @@ static struct share_mode_lock *get_share_mode_lock_internal(
        }
 
        if (d == NULL) {
-               DEBUG(1, ("Could not get share mode lock\n"));
+               DEBUG(5, ("get_share_mode_lock_internal: "
+                       "Could not get share mode lock\n"));
                TALLOC_FREE(rec);
                return NULL;
        }
@@ -385,12 +359,12 @@ static int the_lock_destructor(struct share_mode_lock *l)
 }
 
 /*******************************************************************
- Get a share_mode_lock, Reference counted to allow nexted calls.
+ Get a share_mode_lock, Reference counted to allow nested calls.
 ********************************************************************/
 
-struct share_mode_lock *get_share_mode_lock_fresh(
+struct share_mode_lock *get_share_mode_lock(
        TALLOC_CTX *mem_ctx,
-       const struct file_id id,
+       struct file_id id,
        const char *servicepath,
        const struct smb_filename *smb_fname,
        const struct timespec *old_write_time)
@@ -428,36 +402,36 @@ fail:
        return NULL;
 }
 
+static void fetch_share_mode_unlocked_parser(
+       TDB_DATA key, TDB_DATA data, void *private_data)
+{
+       struct share_mode_lock *lck = talloc_get_type_abort(
+               private_data, struct share_mode_lock);
+
+       lck->data = parse_share_modes(lck, data);
+}
+
 /*******************************************************************
  Get a share_mode_lock without locking the database or reference
  counting. Used by smbstatus to display existing share modes.
 ********************************************************************/
 
 struct share_mode_lock *fetch_share_mode_unlocked(TALLOC_CTX *mem_ctx,
-                                                 const struct file_id id)
+                                                 struct file_id id)
 {
        struct share_mode_lock *lck;
-       struct file_id tmp;
-       TDB_DATA key = locking_key(&id, &tmp);
-       TDB_DATA data;
+       TDB_DATA key = locking_key(&id);
        NTSTATUS status;
 
-       status = dbwrap_fetch(lock_db, talloc_tos(), key, &data);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(3, ("Could not fetch share entry\n"));
-               return NULL;
-       }
-       if (data.dptr == NULL) {
-               return NULL;
-       }
        lck = talloc(mem_ctx, struct share_mode_lock);
        if (lck == NULL) {
-               TALLOC_FREE(data.dptr);
+               DEBUG(0, ("talloc failed\n"));
                return NULL;
        }
-       lck->data = parse_share_modes(lck, data);
-       TALLOC_FREE(data.dptr);
-       if (lck->data == NULL) {
+       status = dbwrap_parse_record(
+               lock_db, key, fetch_share_mode_unlocked_parser, lck);
+       if (!NT_STATUS_IS_OK(status) ||
+           (lck->data == NULL)) {
                TALLOC_FREE(lck);
                return NULL;
        }
@@ -503,6 +477,10 @@ static int traverse_fn(struct db_record *rec, void *_state)
                DEBUG(1, ("ndr_pull_share_mode_lock failed\n"));
                return 0;
        }
+       if (DEBUGLEVEL > 10) {
+               DEBUG(11, ("parse_share_modes:\n"));
+               NDR_PRINT_DEBUG(share_mode_data, d);
+       }
        for (i=0; i<d->num_share_modes; i++) {
                state->fn(&d->share_modes[i],
                          d->servicepath, d->base_name,
@@ -541,3 +519,102 @@ int share_mode_forall(void (*fn)(const struct share_mode_entry *, const char *,
                return count;
        }
 }
+
+bool share_mode_cleanup_disconnected(struct file_id fid,
+                                    uint64_t open_persistent_id)
+{
+       bool ret = false;
+       TALLOC_CTX *frame = talloc_stackframe();
+       unsigned n;
+       struct share_mode_data *data;
+       struct share_mode_lock *lck;
+       bool ok;
+
+       lck = get_existing_share_mode_lock(frame, fid);
+       if (lck == NULL) {
+               DEBUG(5, ("share_mode_cleanup_disconnected: "
+                         "Could not fetch share mode entry for %s\n",
+                         file_id_string(frame, &fid)));
+               goto done;
+       }
+       data = lck->data;
+
+       for (n=0; n < data->num_share_modes; n++) {
+               struct share_mode_entry *entry = &data->share_modes[n];
+
+               if (!server_id_is_disconnected(&entry->pid)) {
+                       DEBUG(5, ("share_mode_cleanup_disconnected: "
+                                 "file (file-id='%s', servicepath='%s', "
+                                 "base_name='%s%s%s') "
+                                 "is used by server %s ==> do not cleanup\n",
+                                 file_id_string(frame, &fid),
+                                 data->servicepath,
+                                 data->base_name,
+                                 (data->stream_name == NULL)
+                                 ? "" : "', stream_name='",
+                                 (data->stream_name == NULL)
+                                 ? "" : data->stream_name,
+                                 server_id_str(frame, &entry->pid)));
+                       goto done;
+               }
+               if (open_persistent_id != entry->share_file_id) {
+                       DEBUG(5, ("share_mode_cleanup_disconnected: "
+                                 "entry for file "
+                                 "(file-id='%s', servicepath='%s', "
+                                 "base_name='%s%s%s') "
+                                 "has share_file_id %llu but expected %llu"
+                                 "==> do not cleanup\n",
+                                 file_id_string(frame, &fid),
+                                 data->servicepath,
+                                 data->base_name,
+                                 (data->stream_name == NULL)
+                                 ? "" : "', stream_name='",
+                                 (data->stream_name == NULL)
+                                 ? "" : data->stream_name,
+                                 (unsigned long long)entry->share_file_id,
+                                 (unsigned long long)open_persistent_id));
+                       goto done;
+               }
+       }
+
+       ok = brl_cleanup_disconnected(fid, open_persistent_id);
+       if (!ok) {
+               DEBUG(10, ("share_mode_cleanup_disconnected: "
+                          "failed to clean up byte range locks associated "
+                          "with file (file-id='%s', servicepath='%s', "
+                          "base_name='%s%s%s') and open_persistent_id %llu "
+                          "==> do not cleanup\n",
+                          file_id_string(frame, &fid),
+                          data->servicepath,
+                          data->base_name,
+                          (data->stream_name == NULL)
+                          ? "" : "', stream_name='",
+                          (data->stream_name == NULL)
+                          ? "" : data->stream_name,
+                          (unsigned long long)open_persistent_id));
+               goto done;
+       }
+
+       DEBUG(10, ("share_mode_cleanup_disconnected: "
+                  "cleaning up %u entries for file "
+                  "(file-id='%s', servicepath='%s', "
+                  "base_name='%s%s%s') "
+                  "from open_persistent_id %llu\n",
+                  data->num_share_modes,
+                  file_id_string(frame, &fid),
+                  data->servicepath,
+                  data->base_name,
+                  (data->stream_name == NULL)
+                  ? "" : "', stream_name='",
+                  (data->stream_name == NULL)
+                  ? "" : data->stream_name,
+                  (unsigned long long)open_persistent_id));
+
+       data->num_share_modes = 0;
+       data->modified = true;
+
+       ret = true;
+done:
+       talloc_free(frame);
+       return ret;
+}