s3-smbd: expose smbd_set_server_fd()
[samba.git] / source3 / smbd / oplock_irix.c
1 /*
2    Unix SMB/CIFS implementation.
3    IRIX kernel oplock processing
4    Copyright (C) Andrew Tridgell 1992-1998
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #define DBGC_CLASS DBGC_LOCKING
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25
26 #if HAVE_KERNEL_OPLOCKS_IRIX
27
28 struct irix_oplocks_context {
29         struct kernel_oplocks *ctx;
30         int write_fd;
31         int read_fd;
32         struct fd_event *read_fde;
33         bool pending;
34 };
35
36 /****************************************************************************
37  Test to see if IRIX kernel oplocks work.
38 ****************************************************************************/
39
40 static bool irix_oplocks_available(void)
41 {
42         int fd;
43         int pfd[2];
44         TALLOC_CTX *ctx = talloc_stackframe();
45         char *tmpname = NULL;
46
47         set_effective_capability(KERNEL_OPLOCK_CAPABILITY);
48
49         tmpname = talloc_asprintf(ctx,
50                                 "%s/koplock.%d",
51                                 lp_lockdir(),
52                                 (int)sys_getpid());
53         if (!tmpname) {
54                 TALLOC_FREE(ctx);
55                 return False;
56         }
57
58         if(pipe(pfd) != 0) {
59                 DEBUG(0,("check_kernel_oplocks: Unable to create pipe. Error "
60                          "was %s\n",
61                          strerror(errno) ));
62                 TALLOC_FREE(ctx);
63                 return False;
64         }
65
66         if((fd = sys_open(tmpname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600)) < 0) {
67                 DEBUG(0,("check_kernel_oplocks: Unable to open temp test file "
68                          "%s. Error was %s\n",
69                          tmpname, strerror(errno) ));
70                 unlink( tmpname );
71                 close(pfd[0]);
72                 close(pfd[1]);
73                 TALLOC_FREE(ctx);
74                 return False;
75         }
76
77         unlink(tmpname);
78
79         TALLOC_FREE(ctx);
80
81         if(sys_fcntl_long(fd, F_OPLKREG, pfd[1]) == -1) {
82                 DEBUG(0,("check_kernel_oplocks: Kernel oplocks are not "
83                          "available on this machine. Disabling kernel oplock "
84                          "support.\n" ));
85                 close(pfd[0]);
86                 close(pfd[1]);
87                 close(fd);
88                 return False;
89         }
90
91         if(sys_fcntl_long(fd, F_OPLKACK, OP_REVOKE) < 0 ) {
92                 DEBUG(0,("check_kernel_oplocks: Error when removing kernel "
93                          "oplock. Error was %s. Disabling kernel oplock "
94                          "support.\n", strerror(errno) ));
95                 close(pfd[0]);
96                 close(pfd[1]);
97                 close(fd);
98                 return False;
99         }
100
101         close(pfd[0]);
102         close(pfd[1]);
103         close(fd);
104
105         return True;
106 }
107
108 /*
109  * This is bad because the file_id should always be created through the vfs
110  * layer!  Unfortunately, a conn struct isn't available here.
111  */
112 static struct file_id file_id_create_dev(SMB_DEV_T dev, SMB_INO_T inode)
113 {
114         struct file_id key;
115
116         /* the ZERO_STRUCT ensures padding doesn't break using the key as a
117          * blob */
118         ZERO_STRUCT(key);
119
120         key.devid = dev;
121         key.inode = inode;
122
123         return key;
124 }
125
126 /****************************************************************************
127  * Deal with the IRIX kernel <--> smbd
128  * oplock break protocol.
129 ****************************************************************************/
130
131 static files_struct *irix_oplock_receive_message(struct kernel_oplocks *_ctx)
132 {
133         struct irix_oplocks_context *ctx = talloc_get_type(_ctx->private_data,
134                                            struct irix_oplocks_context);
135         oplock_stat_t os;
136         char dummy;
137         struct file_id fileid;
138         files_struct *fsp;
139
140         /*
141          * TODO: is it correct to assume we only get one
142          * oplock break, for each byte we read from the pipe?
143          */
144         ctx->pending = false;
145
146         /*
147          * Read one byte of zero to clear the
148          * kernel break notify message.
149          */
150
151         if(read(ctx->read_fd, &dummy, 1) != 1) {
152                 DEBUG(0,("irix_oplock_receive_message: read of kernel "
153                          "notification failed. Error was %s.\n",
154                          strerror(errno) ));
155                 return NULL;
156         }
157
158         /*
159          * Do a query to get the
160          * device and inode of the file that has the break
161          * request outstanding.
162          */
163
164         if(sys_fcntl_ptr(ctx->read_fd, F_OPLKSTAT, &os) < 0) {
165                 DEBUG(0,("irix_oplock_receive_message: fcntl of kernel "
166                          "notification failed. Error was %s.\n",
167                          strerror(errno) ));
168                 if(errno == EAGAIN) {
169                         /*
170                          * Duplicate kernel break message - ignore.
171                          */
172                         return NULL;
173                 }
174                 return NULL;
175         }
176
177         /*
178          * We only have device and inode info here - we have to guess that this
179          * is the first fsp open with this dev,ino pair.
180          *
181          * NOTE: this doesn't work if any VFS modules overloads
182          *       the file_id_create() hook!
183          */
184
185         fileid = file_id_create_dev((SMB_DEV_T)os.os_dev,
186                                     (SMB_INO_T)os.os_ino);
187         if ((fsp = file_find_di_first(smbd_server_conn, fileid)) == NULL) {
188                 DEBUG(0,("irix_oplock_receive_message: unable to find open "
189                          "file with dev = %x, inode = %.0f\n",
190                          (unsigned int)os.os_dev, (double)os.os_ino ));
191                 return NULL;
192         }
193      
194         DEBUG(5,("irix_oplock_receive_message: kernel oplock break request "
195                  "received for file_id %s gen_id = %ul",
196                  file_id_string_tos(&fsp->file_id),
197                  fsp->fh->gen_id ));
198
199         return fsp;
200 }
201
202 /****************************************************************************
203  Attempt to set an kernel oplock on a file.
204 ****************************************************************************/
205
206 static bool irix_set_kernel_oplock(struct kernel_oplocks *_ctx,
207                                    files_struct *fsp, int oplock_type)
208 {
209         struct irix_oplocks_context *ctx = talloc_get_type(_ctx->private_data,
210                                            struct irix_oplocks_context);
211
212         if (sys_fcntl_long(fsp->fh->fd, F_OPLKREG, ctx->write_fd) == -1) {
213                 if(errno != EAGAIN) {
214                         DEBUG(0,("irix_set_kernel_oplock: Unable to get "
215                                  "kernel oplock on file %s, file_id %s "
216                                  "gen_id = %ul. Error was %s\n", 
217                                  fsp_str_dbg(fsp),
218                                  file_id_string_tos(&fsp->file_id),
219                                  fsp->fh->gen_id,
220                                  strerror(errno) ));
221                 } else {
222                         DEBUG(5,("irix_set_kernel_oplock: Refused oplock on "
223                                  "file %s, fd = %d, file_id = %s, "
224                                  "gen_id = %ul. Another process had the file "
225                                  "open.\n",
226                                  fsp_str_dbg(fsp), fsp->fh->fd,
227                                  file_id_string_tos(&fsp->file_id),
228                                  fsp->fh->gen_id ));
229                 }
230                 return False;
231         }
232         
233         DEBUG(10,("irix_set_kernel_oplock: got kernel oplock on file %s, file_id = %s "
234                   "gen_id = %ul\n",
235                   fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id),
236                   fsp->fh->gen_id));
237
238         return True;
239 }
240
241 /****************************************************************************
242  Release a kernel oplock on a file.
243 ****************************************************************************/
244
245 static void irix_release_kernel_oplock(struct kernel_oplocks *_ctx,
246                                        files_struct *fsp, int oplock_type)
247 {
248         if (DEBUGLVL(10)) {
249                 /*
250                  * Check and print out the current kernel
251                  * oplock state of this file.
252                  */
253                 int state = sys_fcntl_long(fsp->fh->fd, F_OPLKACK, -1);
254                 dbgtext("irix_release_kernel_oplock: file %s, file_id = %s"
255                         "gen_id = %ul, has kernel oplock state "
256                         "of %x.\n", fsp_str_dbg(fsp),
257                         file_id_string_tos(&fsp->file_id),
258                         fsp->fh->gen_id, state );
259         }
260
261         /*
262          * Remove the kernel oplock on this file.
263          */
264         if(sys_fcntl_long(fsp->fh->fd, F_OPLKACK, OP_REVOKE) < 0) {
265                 if( DEBUGLVL( 0 )) {
266                         dbgtext("irix_release_kernel_oplock: Error when "
267                                 "removing kernel oplock on file " );
268                         dbgtext("%s, file_id = %s gen_id = %ul. "
269                                 "Error was %s\n",
270                                 fsp_str_dbg(fsp),
271                                 file_id_string_tos(&fsp->file_id),
272                                 fsp->fh->gen_id,
273                                 strerror(errno) );
274                 }
275         }
276 }
277
278 static void irix_oplocks_read_fde_handler(struct event_context *ev,
279                                           struct fd_event *fde,
280                                           uint16_t flags,
281                                           void *private_data)
282 {
283         struct irix_oplocks_context *ctx = talloc_get_type(private_data,
284                                            struct irix_oplocks_context);
285         files_struct *fsp;
286
287         fsp = irix_oplock_receive_message(ctx->ctx);
288         break_kernel_oplock(fsp->conn->sconn->msg_ctx, fsp);
289 }
290
291 /****************************************************************************
292  Setup kernel oplocks.
293 ****************************************************************************/
294
295 static const struct kernel_oplocks_ops irix_koplocks = {
296         .set_oplock                     = irix_set_kernel_oplock,
297         .release_oplock                 = irix_release_kernel_oplock,
298         .contend_level2_oplocks_begin   = NULL,
299         .contend_level2_oplocks_end     = NULL,
300 };
301
302 struct kernel_oplocks *irix_init_kernel_oplocks(TALLOC_CTX *mem_ctx)
303 {
304         struct kernel_oplocks *_ctx;
305         struct irix_oplocks_context *ctx;
306         int pfd[2];
307
308         if (!irix_oplocks_available())
309                 return NULL;
310
311         _ctx = talloc_zero(mem_ctx, struct kernel_oplocks);
312         if (!_ctx) {
313                 return NULL;
314         }
315
316         ctx = talloc_zero(_ctx, struct irix_oplocks_context);
317         if (!ctx) {
318                 talloc_free(_ctx);
319                 return NULL;
320         }
321         _ctx->ops = &irix_koplocks;
322         _ctx->private_data = ctx;
323         ctx->ctx = _ctx;
324
325         if(pipe(pfd) != 0) {
326                 talloc_free(_ctx);
327                 DEBUG(0,("setup_kernel_oplock_pipe: Unable to create pipe. "
328                          "Error was %s\n", strerror(errno) ));
329                 return False;
330         }
331
332         ctx->read_fd = pfd[0];
333         ctx->write_fd = pfd[1];
334
335         ctx->read_fde = event_add_fd(smbd_event_context(),
336                                      ctx,
337                                      ctx->read_fd,
338                                      EVENT_FD_READ,
339                                      irix_oplocks_read_fde_handler,
340                                      ctx);
341         return _ctx;
342 }
343 #else
344  void oplock_irix_dummy(void);
345  void oplock_irix_dummy(void) {}
346 #endif /* HAVE_KERNEL_OPLOCKS_IRIX */