pvfs_oplock: only a break level2 oplocks...
[samba.git] / source4 / ntvfs / posix / pvfs_oplock.c
index 50b1478f13fb0c179043db2a0607c02bf3f21a0f..3f581e54430bde038d2b3ccc4ce503f3b15f4970 100644 (file)
@@ -32,6 +32,58 @@ struct pvfs_oplock {
        struct messaging_context *msg_ctx;
 };
 
+static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
+                                            uint8_t oplock_break)
+{
+       struct odb_lock *olck;
+       NTSTATUS status;
+
+       if (h->fd == -1) {
+               return NT_STATUS_FILE_IS_A_DIRECTORY;
+       }
+
+       if (!h->have_opendb_entry) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       if (!h->oplock) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+       if (olck == NULL) {
+               DEBUG(0,("Unable to lock opendb for oplock update\n"));
+               return NT_STATUS_FOOBAR;
+       }
+
+       if (oplock_break == OPLOCK_BREAK_TO_NONE) {
+               h->oplock->level = OPLOCK_NONE;
+       } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
+               h->oplock->level = OPLOCK_LEVEL_II;
+       } else {
+               /* fallback to level II in case of a invalid value */
+               DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
+               h->oplock->level = OPLOCK_LEVEL_II;
+       }
+       status = odb_update_oplock(olck, h, h->oplock->level);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
+                        h->name->full_name, nt_errstr(status)));
+               talloc_free(olck);
+               return status;
+       }
+
+       talloc_free(olck);
+
+       /* after a break to none, we no longer have an oplock attached */
+       if (h->oplock->level == OPLOCK_NONE) {
+               talloc_free(h->oplock);
+               h->oplock = NULL;
+       }
+
+       return NT_STATUS_OK;
+}
+
 /*
   receive oplock breaks and forward them to the client
 */
@@ -134,3 +186,57 @@ NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
 
        return NT_STATUS_OK;
 }
+
+NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
+                            struct ntvfs_request *req, union smb_lock *lck)
+{
+       struct pvfs_state *pvfs = ntvfs->private_data;
+       struct pvfs_file *f;
+       uint8_t oplock_break;
+       NTSTATUS status;
+
+       f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
+       if (!f) {
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
+
+       status = pvfs_oplock_release_internal(f->handle, oplock_break);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
+                       __FUNCTION__, oplock_break, nt_errstr(status)));
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
+{
+       struct pvfs_file_handle *h = f->handle;
+       struct odb_lock *olck;
+       NTSTATUS status;
+
+       if (h->oplock && h->oplock->level != OPLOCK_LEVEL_II) {
+               return NT_STATUS_OK;
+       }
+
+       olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+       if (olck == NULL) {
+               DEBUG(0,("Unable to lock opendb for oplock update\n"));
+               return NT_STATUS_FOOBAR;
+       }
+
+       status = odb_break_oplocks(olck);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
+                        h->name->full_name, nt_errstr(status)));
+               talloc_free(olck);
+               return status;
+       }
+
+       talloc_free(olck);
+
+       return NT_STATUS_OK;
+}