pvfs_oplock: only a break level2 oplocks...
[kai/samba.git] / source4 / ntvfs / posix / pvfs_oplock.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - oplock handling
5
6    Copyright (C) Stefan Metzmacher 2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "lib/messaging/messaging.h"
24 #include "lib/messaging/irpc.h"
25 #include "vfs_posix.h"
26
27
28 struct pvfs_oplock {
29         struct pvfs_file_handle *handle;
30         struct pvfs_file *file;
31         uint32_t level;
32         struct messaging_context *msg_ctx;
33 };
34
35 static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
36                                              uint8_t oplock_break)
37 {
38         struct odb_lock *olck;
39         NTSTATUS status;
40
41         if (h->fd == -1) {
42                 return NT_STATUS_FILE_IS_A_DIRECTORY;
43         }
44
45         if (!h->have_opendb_entry) {
46                 return NT_STATUS_FOOBAR;
47         }
48
49         if (!h->oplock) {
50                 return NT_STATUS_FOOBAR;
51         }
52
53         olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
54         if (olck == NULL) {
55                 DEBUG(0,("Unable to lock opendb for oplock update\n"));
56                 return NT_STATUS_FOOBAR;
57         }
58
59         if (oplock_break == OPLOCK_BREAK_TO_NONE) {
60                 h->oplock->level = OPLOCK_NONE;
61         } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
62                 h->oplock->level = OPLOCK_LEVEL_II;
63         } else {
64                 /* fallback to level II in case of a invalid value */
65                 DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
66                 h->oplock->level = OPLOCK_LEVEL_II;
67         }
68         status = odb_update_oplock(olck, h, h->oplock->level);
69         if (!NT_STATUS_IS_OK(status)) {
70                 DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
71                          h->name->full_name, nt_errstr(status)));
72                 talloc_free(olck);
73                 return status;
74         }
75
76         talloc_free(olck);
77
78         /* after a break to none, we no longer have an oplock attached */
79         if (h->oplock->level == OPLOCK_NONE) {
80                 talloc_free(h->oplock);
81                 h->oplock = NULL;
82         }
83
84         return NT_STATUS_OK;
85 }
86
87 /*
88   receive oplock breaks and forward them to the client
89 */
90 static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
91 {
92         NTSTATUS status;
93         struct pvfs_file *f = opl->file;
94         struct pvfs_file_handle *h = opl->handle;
95         struct pvfs_state *pvfs = h->pvfs;
96
97         DEBUG(10,("pvfs_oplock_break: sending oplock break level %d for '%s' %p\n",
98                 level, h->name->original_name, h));
99         status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
100         if (!NT_STATUS_IS_OK(status)) {
101                 DEBUG(0,("pvfs_oplock_break: sending oplock break failed: %s\n",
102                         nt_errstr(status)));
103         }
104 }
105
106 static void pvfs_oplock_break_dispatch(struct messaging_context *msg,
107                                        void *private_data, uint32_t msg_type,
108                                        struct server_id src, DATA_BLOB *data)
109 {
110         struct pvfs_oplock *opl = talloc_get_type(private_data,
111                                                   struct pvfs_oplock);
112         struct opendb_oplock_break opb;
113
114         ZERO_STRUCT(opb);
115
116         /* we need to check that this one is for us. See
117            messaging_send_ptr() for the other side of this.
118          */
119         if (data->length == sizeof(struct opendb_oplock_break)) {
120                 struct opendb_oplock_break *p;
121                 p = (struct opendb_oplock_break *)data->data;
122                 opb = *p;
123         } else {
124                 DEBUG(0,("%s: ignore oplock break with length[%u]\n",
125                         __location__, data->length));
126                 return;
127         }
128         if (opb.file_handle != opl->handle) {
129                 return;
130         }
131
132         /*
133          * maybe we should use ntvfs_setup_async()
134          */
135         pvfs_oplock_break(opl, opb.level);
136 }
137
138 static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
139 {
140         messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
141         return 0;
142 }
143
144 NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
145 {
146         NTSTATUS status;
147         struct pvfs_oplock *opl;
148         uint32_t level = OPLOCK_NONE;
149
150         f->handle->oplock = NULL;
151
152         switch (oplock_granted) {
153         case EXCLUSIVE_OPLOCK_RETURN:
154                 level = OPLOCK_EXCLUSIVE;
155                 break;
156         case BATCH_OPLOCK_RETURN:
157                 level = OPLOCK_BATCH;
158                 break;
159         case LEVEL_II_OPLOCK_RETURN:
160                 level = OPLOCK_LEVEL_II;
161                 break;
162         }
163
164         if (level == OPLOCK_NONE) {
165                 return NT_STATUS_OK;
166         }
167
168         opl = talloc(f->handle, struct pvfs_oplock);
169         NT_STATUS_HAVE_NO_MEMORY(opl);
170
171         opl->handle     = f->handle;
172         opl->file       = f;
173         opl->level      = level;
174         opl->msg_ctx    = f->pvfs->ntvfs->ctx->msg_ctx;
175
176         status = messaging_register(opl->msg_ctx,
177                                     opl,
178                                     MSG_NTVFS_OPLOCK_BREAK,
179                                     pvfs_oplock_break_dispatch);
180         NT_STATUS_NOT_OK_RETURN(status);
181
182         /* destructor */
183         talloc_set_destructor(opl, pvfs_oplock_destructor);
184
185         f->handle->oplock = opl;
186
187         return NT_STATUS_OK;
188 }
189
190 NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
191                              struct ntvfs_request *req, union smb_lock *lck)
192 {
193         struct pvfs_state *pvfs = ntvfs->private_data;
194         struct pvfs_file *f;
195         uint8_t oplock_break;
196         NTSTATUS status;
197
198         f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
199         if (!f) {
200                 return NT_STATUS_INVALID_HANDLE;
201         }
202
203         oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
204
205         status = pvfs_oplock_release_internal(f->handle, oplock_break);
206         if (!NT_STATUS_IS_OK(status)) {
207                 DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
208                         __FUNCTION__, oplock_break, nt_errstr(status)));
209                 return status;
210         }
211
212         return NT_STATUS_OK;
213 }
214
215 NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
216 {
217         struct pvfs_file_handle *h = f->handle;
218         struct odb_lock *olck;
219         NTSTATUS status;
220
221         if (h->oplock && h->oplock->level != OPLOCK_LEVEL_II) {
222                 return NT_STATUS_OK;
223         }
224
225         olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
226         if (olck == NULL) {
227                 DEBUG(0,("Unable to lock opendb for oplock update\n"));
228                 return NT_STATUS_FOOBAR;
229         }
230
231         status = odb_break_oplocks(olck);
232         if (!NT_STATUS_IS_OK(status)) {
233                 DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
234                          h->name->full_name, nt_errstr(status)));
235                 talloc_free(olck);
236                 return status;
237         }
238
239         talloc_free(olck);
240
241         return NT_STATUS_OK;
242 }