r5102: This is a major simplification of the logic for controlling top level
[abartlet/samba.git/.git] / source4 / ntvfs / common / opendb.c
index a66549b76474daf2c6e93f0fb9cfdfa90a2fd7a1..3c206528dd3f0b0f79e77827539529e1356898ab 100644 (file)
 */
 
 #include "includes.h"
+#include "messages.h"
+#include "librpc/gen_ndr/ndr_security.h"
 
 struct odb_context {
        struct tdb_wrap *w;
-       servid_t server;
-       uint16_t tid;
+       uint32_t server;
        struct messaging_context *messaging_ctx;
 };
 
@@ -52,12 +53,14 @@ struct odb_context {
    following form
 */
 struct odb_entry {
-       servid_t server;
-       uint16_t tid;
-       uint16_t fnum;
+       uint32_t server;
+       void     *file_handle;
+       uint32_t stream_id;
        uint32_t share_access;
        uint32_t create_options;
        uint32_t access_mask;
+       void     *notify_ptr;
+       BOOL     pending;
 };
 
 
@@ -75,18 +78,18 @@ struct odb_lock {
   talloc_free(). We need the messaging_ctx to allow for pending open
   notifications.
 */
-struct odb_context *odb_init(TALLOC_CTX *mem_ctx, servid_t server, uint16_t tid
+struct odb_context *odb_init(TALLOC_CTX *mem_ctx, uint32_t server
                             struct messaging_context *messaging_ctx)
 {
        char *path;
        struct odb_context *odb;
 
-       odb = talloc_p(mem_ctx, struct odb_context);
+       odb = talloc(mem_ctx, struct odb_context);
        if (odb == NULL) {
                return NULL;
        }
 
-       path = lock_path(odb, "openfiles.tdb");
+       path = smbd_tmp_path(odb, "openfiles.tdb");
        odb->w = tdb_wrap_open(odb, path, 0,  
                               TDB_DEFAULT,
                               O_RDWR|O_CREAT, 0600);
@@ -97,7 +100,6 @@ struct odb_context *odb_init(TALLOC_CTX *mem_ctx, servid_t server, uint16_t tid,
        }
 
        odb->server = server;
-       odb->tid = tid;
        odb->messaging_ctx = messaging_ctx;
 
        return odb;
@@ -122,12 +124,12 @@ struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
 {
        struct odb_lock *lck;
 
-       lck = talloc_p(mem_ctx, struct odb_lock);
+       lck = talloc(mem_ctx, struct odb_lock);
        if (lck == NULL) {
                return NULL;
        }
 
-       lck->odb = odb;
+       lck->odb = talloc_reference(lck, odb);
        lck->key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
        lck->key.dsize = file_key->length;
        if (lck->key.dptr == NULL) {
@@ -152,24 +154,52 @@ static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
 {
 #define CHECK_MASK(am, sa, right, share) if (((am) & (right)) && !((sa) & (share))) return True
 
+       if (e1->pending || e2->pending) return False;
+
        /* if either open involves no read.write or delete access then
           it can't conflict */
-       if (!(e1->access_mask & (SA_RIGHT_FILE_WRITE_DATA | SA_RIGHT_FILE_READ_DATA | STD_RIGHT_DELETE_ACCESS))) {
+       if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
+                                SEC_FILE_APPEND_DATA |
+                                SEC_FILE_READ_DATA |
+                                SEC_FILE_EXECUTE |
+                                SEC_STD_DELETE))) {
                return False;
        }
-       if (!(e2->access_mask & (SA_RIGHT_FILE_WRITE_DATA | SA_RIGHT_FILE_READ_DATA | STD_RIGHT_DELETE_ACCESS))) {
+       if (!(e2->access_mask & (SEC_FILE_WRITE_DATA |
+                                SEC_FILE_APPEND_DATA |
+                                SEC_FILE_READ_DATA |
+                                SEC_FILE_EXECUTE |
+                                SEC_STD_DELETE))) {
                return False;
        }
 
-       /* check the basic share access */
-       CHECK_MASK(e1->access_mask, e2->share_access, SA_RIGHT_FILE_WRITE_DATA, NTCREATEX_SHARE_ACCESS_WRITE);
-       CHECK_MASK(e2->access_mask, e1->share_access, SA_RIGHT_FILE_WRITE_DATA, NTCREATEX_SHARE_ACCESS_WRITE);
-
-       CHECK_MASK(e1->access_mask, e2->share_access, SA_RIGHT_FILE_READ_DATA, NTCREATEX_SHARE_ACCESS_READ);
-       CHECK_MASK(e2->access_mask, e1->share_access, SA_RIGHT_FILE_READ_DATA, NTCREATEX_SHARE_ACCESS_READ);
+       /* data IO access masks. This is skipped if the two open handles
+          are on different streams (as in that case the masks don't
+          interact) */
+       if (e1->stream_id != e2->stream_id) {
+               return False;
+       }
 
-       CHECK_MASK(e1->access_mask, e2->share_access, STD_RIGHT_DELETE_ACCESS, NTCREATEX_SHARE_ACCESS_DELETE);
-       CHECK_MASK(e2->access_mask, e1->share_access, STD_RIGHT_DELETE_ACCESS, NTCREATEX_SHARE_ACCESS_DELETE);
+       CHECK_MASK(e1->access_mask, e2->share_access, 
+                  SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
+                  NTCREATEX_SHARE_ACCESS_WRITE);
+       CHECK_MASK(e2->access_mask, e1->share_access, 
+                  SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
+                  NTCREATEX_SHARE_ACCESS_WRITE);
+       
+       CHECK_MASK(e1->access_mask, e2->share_access, 
+                  SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
+                  NTCREATEX_SHARE_ACCESS_READ);
+       CHECK_MASK(e2->access_mask, e1->share_access, 
+                  SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
+                  NTCREATEX_SHARE_ACCESS_READ);
+
+       CHECK_MASK(e1->access_mask, e2->share_access, 
+                  SEC_STD_DELETE,
+                  NTCREATEX_SHARE_ACCESS_DELETE);
+       CHECK_MASK(e2->access_mask, e1->share_access, 
+                  SEC_STD_DELETE,
+                  NTCREATEX_SHARE_ACCESS_DELETE);
 
        /* if a delete is pending then a second open is not allowed */
        if ((e1->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) ||
@@ -184,25 +214,27 @@ static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
   register an open file in the open files database. This implements the share_access
   rules
 */
-NTSTATUS odb_open_file(struct odb_lock *lck, uint16_t fnum, 
+NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
+                      uint32_t stream_id,
                       uint32_t share_access, uint32_t create_options,
                       uint32_t access_mask)
 {
        struct odb_context *odb = lck->odb;
        TDB_DATA dbuf;
        struct odb_entry e;
-       char *tp;
        int i, count;
        struct odb_entry *elist;
                
        dbuf = tdb_fetch(odb->w->tdb, lck->key);
 
        e.server         = odb->server;
-       e.tid            = odb->tid;
-       e.fnum           = fnum;
+       e.file_handle    = file_handle;
+       e.stream_id      = stream_id;
        e.share_access   = share_access;
        e.create_options = create_options;
        e.access_mask    = access_mask;
+       e.notify_ptr     = NULL;
+       e.pending        = False;
 
        /* check the existing file opens to see if they
           conflict */
@@ -216,13 +248,62 @@ NTSTATUS odb_open_file(struct odb_lock *lck, uint16_t fnum,
                }
        }
 
-       tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
-       if (tp == NULL) {
+       elist = realloc_p(dbuf.dptr, struct odb_entry, count+1);
+       if (elist == NULL) {
                if (dbuf.dptr) free(dbuf.dptr);
                return NT_STATUS_NO_MEMORY;
        }
 
-       dbuf.dptr = tp;
+       dbuf.dptr = (char *)elist;
+       dbuf.dsize = (count+1) * sizeof(struct odb_entry);
+
+       memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
+              &e, sizeof(struct odb_entry));
+
+       if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
+               free(dbuf.dptr);
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+       free(dbuf.dptr);
+       return NT_STATUS_OK;
+}
+
+
+/*
+  register a pending open file in the open files database
+*/
+NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
+{
+       struct odb_context *odb = lck->odb;
+       TDB_DATA dbuf;
+       struct odb_entry e;
+       struct odb_entry *elist;
+       int count;
+               
+       dbuf = tdb_fetch(odb->w->tdb, lck->key);
+
+       e.server         = odb->server;
+       e.file_handle    = NULL;
+       e.stream_id      = 0;
+       e.share_access   = 0;
+       e.create_options = 0;
+       e.access_mask    = 0;
+       e.notify_ptr     = private;
+       e.pending        = True;
+
+       /* check the existing file opens to see if they
+          conflict */
+       elist = (struct odb_entry *)dbuf.dptr;
+       count = dbuf.dsize / sizeof(struct odb_entry);
+
+       elist = realloc_p(dbuf.dptr, struct odb_entry, count+1);
+       if (elist == NULL) {
+               if (dbuf.dptr) free(dbuf.dptr);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       dbuf.dptr = (char *)elist;
        dbuf.dsize = (count+1) * sizeof(struct odb_entry);
 
        memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
@@ -241,7 +322,71 @@ NTSTATUS odb_open_file(struct odb_lock *lck, uint16_t fnum,
 /*
   remove a opendb entry
 */
-NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum)
+NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
+{
+       struct odb_context *odb = lck->odb;
+       TDB_DATA dbuf;
+       struct odb_entry *elist;
+       int i, count;
+       NTSTATUS status;
+
+       dbuf = tdb_fetch(odb->w->tdb, lck->key);
+
+       if (dbuf.dptr == NULL) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       elist = (struct odb_entry *)dbuf.dptr;
+       count = dbuf.dsize / sizeof(struct odb_entry);
+
+       /* send any pending notifications, removing them once sent */
+       for (i=0;i<count;i++) {
+               if (elist[i].pending) {
+                       messaging_send_ptr(odb->messaging_ctx, elist[i].server, 
+                                          MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr);
+                       memmove(&elist[i], &elist[i+1], sizeof(struct odb_entry)*(count-(i+1)));
+                       i--;
+                       count--;
+               }
+       }
+
+       /* find the entry, and delete it */
+       for (i=0;i<count;i++) {
+               if (file_handle == elist[i].file_handle &&
+                   odb->server == elist[i].server) {
+                       if (i < count-1) {
+                               memmove(elist+i, elist+i+1, 
+                                       (count - (i+1)) * sizeof(struct odb_entry));
+                       }
+                       break;
+               }
+       }
+
+       status = NT_STATUS_OK;
+
+       if (i == count) {
+               status = NT_STATUS_UNSUCCESSFUL;
+       } else if (count == 1) {
+               if (tdb_delete(odb->w->tdb, lck->key) != 0) {
+                       status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+       } else {
+               dbuf.dsize = (count-1) * sizeof(struct odb_entry);
+               if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
+                       status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+       }
+
+       free(dbuf.dptr);
+
+       return status;
+}
+
+
+/*
+  remove a pending opendb entry
+*/
+NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
 {
        struct odb_context *odb = lck->odb;
        TDB_DATA dbuf;
@@ -260,9 +405,8 @@ NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum)
 
        /* find the entry, and delete it */
        for (i=0;i<count;i++) {
-               if (fnum == elist[i].fnum &&
-                   odb->server == elist[i].server &&
-                   odb->tid == elist[i].tid) {
+               if (private == elist[i].notify_ptr &&
+                   odb->server == elist[i].server) {
                        if (i < count-1) {
                                memmove(elist+i, elist+i+1, 
                                        (count - (i+1)) * sizeof(struct odb_entry));
@@ -296,7 +440,7 @@ NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum)
   update create options on an open file
 */
 NTSTATUS odb_set_create_options(struct odb_lock *lck, 
-                               uint16_t fnum, uint32_t create_options)
+                               void *file_handle, uint32_t create_options)
 {
        struct odb_context *odb = lck->odb;
        TDB_DATA dbuf;
@@ -314,9 +458,8 @@ NTSTATUS odb_set_create_options(struct odb_lock *lck,
 
        /* find the entry, and modify it */
        for (i=0;i<count;i++) {
-               if (fnum == elist[i].fnum &&
-                   odb->server == elist[i].server &&
-                   odb->tid == elist[i].tid) {
+               if (file_handle == elist[i].file_handle &&
+                   odb->server == elist[i].server) {
                        elist[i].create_options = create_options;
                        break;
                }
@@ -348,7 +491,7 @@ NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key,
        int i, count;
        struct odb_entry e;
 
-       kbuf.dptr = key->data;
+       kbuf.dptr = (char *)key->data;
        kbuf.dsize = key->length;
 
        dbuf = tdb_fetch(odb->w->tdb, kbuf);
@@ -365,11 +508,13 @@ NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key,
        }
 
        e.server         = odb->server;
-       e.tid            = odb->tid;
-       e.fnum           = -1;
+       e.file_handle    = NULL;
+       e.stream_id      = 0;
        e.share_access   = share_access;
        e.create_options = create_options;
        e.access_mask    = access_mask;
+       e.notify_ptr     = NULL;
+       e.pending        = False;
 
        for (i=0;i<count;i++) {
                if (share_conflict(elist+i, &e)) {