Merge branch 'v4-0-test' of git://git.samba.org/samba into 4-0-local
[samba.git] / source / 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 /*
36   receive oplock breaks and forward them to the client
37 */
38 static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
39 {
40         NTSTATUS status;
41         struct pvfs_file *f = opl->file;
42         struct pvfs_file_handle *h = opl->handle;
43         struct pvfs_state *pvfs = h->pvfs;
44
45         DEBUG(10,("pvfs_oplock_break: sending oplock break level %d for '%s' %p\n",
46                 level, h->name->original_name, h));
47         status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
48         if (!NT_STATUS_IS_OK(status)) {
49                 DEBUG(0,("pvfs_oplock_break: sending oplock break failed: %s\n",
50                         nt_errstr(status)));
51         }
52 }
53
54 static void pvfs_oplock_break_dispatch(struct messaging_context *msg,
55                                        void *private_data, uint32_t msg_type,
56                                        struct server_id src, DATA_BLOB *data)
57 {
58         struct pvfs_oplock *opl = talloc_get_type(private_data,
59                                                   struct pvfs_oplock);
60         struct opendb_oplock_break opb;
61
62         ZERO_STRUCT(opb);
63
64         /* we need to check that this one is for us. See
65            messaging_send_ptr() for the other side of this.
66          */
67         if (data->length == sizeof(struct opendb_oplock_break)) {
68                 struct opendb_oplock_break *p;
69                 p = (struct opendb_oplock_break *)data->data;
70                 opb = *p;
71         } else {
72                 DEBUG(0,("%s: ignore oplock break with length[%u]\n",
73                         __location__, data->length));
74                 return;
75         }
76         if (opb.file_handle != opl->handle) {
77                 return;
78         }
79
80         /*
81          * maybe we should use ntvfs_setup_async()
82          */
83         pvfs_oplock_break(opl, opb.level);
84 }
85
86 static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
87 {
88         messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
89         return 0;
90 }
91
92 NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
93 {
94         NTSTATUS status;
95         struct pvfs_oplock *opl;
96         uint32_t level = OPLOCK_NONE;
97
98         f->handle->oplock = NULL;
99
100         switch (oplock_granted) {
101         case EXCLUSIVE_OPLOCK_RETURN:
102                 level = OPLOCK_EXCLUSIVE;
103                 break;
104         case BATCH_OPLOCK_RETURN:
105                 level = OPLOCK_BATCH;
106                 break;
107         case LEVEL_II_OPLOCK_RETURN:
108                 level = OPLOCK_LEVEL_II;
109                 break;
110         }
111
112         if (level == OPLOCK_NONE) {
113                 return NT_STATUS_OK;
114         }
115
116         opl = talloc(f->handle, struct pvfs_oplock);
117         NT_STATUS_HAVE_NO_MEMORY(opl);
118
119         opl->handle     = f->handle;
120         opl->file       = f;
121         opl->level      = level;
122         opl->msg_ctx    = f->pvfs->ntvfs->ctx->msg_ctx;
123
124         status = messaging_register(opl->msg_ctx,
125                                     opl,
126                                     MSG_NTVFS_OPLOCK_BREAK,
127                                     pvfs_oplock_break_dispatch);
128         NT_STATUS_NOT_OK_RETURN(status);
129
130         /* destructor */
131         talloc_set_destructor(opl, pvfs_oplock_destructor);
132
133         f->handle->oplock = opl;
134
135         return NT_STATUS_OK;
136 }
137
138 NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
139                              struct ntvfs_request *req, union smb_lock *lck)
140 {
141         struct pvfs_state *pvfs = ntvfs->private_data;
142         struct pvfs_file *f;
143         struct pvfs_file_handle *h;
144         struct odb_lock *olck;
145         uint8_t oplock_break;
146         NTSTATUS status;
147
148         f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
149         if (!f) {
150                 return NT_STATUS_INVALID_HANDLE;
151         }
152
153         h = f->handle;
154
155         if (h->fd == -1) {
156                 return NT_STATUS_FILE_IS_A_DIRECTORY;
157         }
158
159         if (!h->have_opendb_entry) {
160                 return NT_STATUS_FOOBAR;
161         }
162
163         if (!h->oplock) {
164                 return NT_STATUS_FOOBAR;
165         }
166
167         olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
168         if (olck == NULL) {
169                 DEBUG(0,("Unable to lock opendb for oplock update\n"));
170                 return NT_STATUS_FOOBAR;
171         }
172
173         oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
174         if (oplock_break == OPLOCK_BREAK_TO_NONE) {
175                 h->oplock->level = OPLOCK_NONE;
176         } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
177                 h->oplock->level = OPLOCK_LEVEL_II;
178         } else {
179                 /* fallback to level II in case of a invalid value */
180                 DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
181                 h->oplock->level = OPLOCK_LEVEL_II;
182         }
183         status = odb_update_oplock(olck, h, h->oplock->level);
184         if (!NT_STATUS_IS_OK(status)) {
185                 DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
186                          h->name->full_name, nt_errstr(status)));
187                 talloc_free(olck);
188                 return status;
189         }
190
191         talloc_free(olck);
192
193         /* after a break to none, we no longer have an oplock attached */
194         if (h->oplock->level == OPLOCK_NONE) {
195                 talloc_free(h->oplock);
196                 h->oplock = NULL;
197         }
198
199         return NT_STATUS_OK;
200 }
201
202 NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
203 {
204         struct pvfs_file_handle *h = f->handle;
205         struct odb_lock *olck;
206         NTSTATUS status;
207
208         if (h->oplock && h->oplock->level == OPLOCK_EXCLUSIVE) {
209                 return NT_STATUS_OK;
210         }
211
212         olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
213         if (olck == NULL) {
214                 DEBUG(0,("Unable to lock opendb for oplock update\n"));
215                 return NT_STATUS_FOOBAR;
216         }
217
218         if (h->oplock && h->oplock->level == OPLOCK_BATCH) {
219                 status = odb_update_oplock(olck, h, OPLOCK_LEVEL_II);
220                 if (!NT_STATUS_IS_OK(status)) {
221                         DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
222                                  h->name->full_name, nt_errstr(status)));
223                         talloc_free(olck);
224                         return status;
225                 }
226         }
227
228         status = odb_break_oplocks(olck);
229         if (!NT_STATUS_IS_OK(status)) {
230                 DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
231                          h->name->full_name, nt_errstr(status)));
232                 talloc_free(olck);
233                 return status;
234         }
235
236         talloc_free(olck);
237
238         return NT_STATUS_OK;
239 }