Merge commit 'release-4-0-0alpha15' into master4-tmp
[ira/wip.git] / source4 / ntvfs / sysdep / sys_lease_linux.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Stefan Metzmacher 2008
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 /*
21   lease (oplock) implementation using fcntl F_SETLEASE on linux
22 */
23
24 #include "includes.h"
25 #include <tevent.h>
26 #include "system/filesys.h"
27 #include "ntvfs/sysdep/sys_lease.h"
28 #include "ntvfs/ntvfs.h"
29 #include "librpc/gen_ndr/ndr_opendb.h"
30 #include "../lib/util/dlinklist.h"
31 #include "cluster/cluster.h"
32
33 NTSTATUS sys_lease_linux_init(void);
34
35 #define LINUX_LEASE_RT_SIGNAL (SIGRTMIN+1)
36
37 struct linux_lease_pending {
38         struct linux_lease_pending *prev, *next;
39         struct sys_lease_context *ctx;
40         struct opendb_entry e;
41 };
42
43 /* the global linked list of pending leases */
44 static struct linux_lease_pending *leases;
45
46 static void linux_lease_signal_handler(struct tevent_context *ev_ctx,
47                                        struct tevent_signal *se,
48                                        int signum, int count,
49                                        void *_info, void *private_data)
50 {
51         struct sys_lease_context *ctx = talloc_get_type(private_data,
52                                         struct sys_lease_context);
53         siginfo_t *info = (siginfo_t *)_info;
54         struct linux_lease_pending *c;
55         int got_fd = info->si_fd;
56
57         for (c = leases; c; c = c->next) {
58                 int *fd = (int *)c->e.fd;
59
60                 if (got_fd == *fd) {
61                         break;
62                 }
63         }
64
65         if (!c) {
66                 return;
67         }
68
69         ctx->break_send(ctx->msg_ctx, &c->e, OPLOCK_BREAK_TO_NONE);
70 }
71
72 static int linux_lease_pending_destructor(struct linux_lease_pending *p)
73 {
74         int ret;
75         int *fd = (int *)p->e.fd;
76
77         DLIST_REMOVE(leases, p);
78
79         if (*fd == -1) {
80                 return 0;
81         }
82
83         ret = fcntl(*fd, F_SETLEASE, F_UNLCK);
84         if (ret == -1) {
85                 DEBUG(0,("%s: failed to remove oplock: %s\n",
86                         __FUNCTION__, strerror(errno)));
87         }
88
89         return 0;
90 }
91
92 static NTSTATUS linux_lease_init(struct sys_lease_context *ctx)
93 {
94         struct tevent_signal *se;
95
96         se = tevent_add_signal(ctx->event_ctx, ctx,
97                                LINUX_LEASE_RT_SIGNAL, SA_SIGINFO,
98                                linux_lease_signal_handler, ctx);
99         NT_STATUS_HAVE_NO_MEMORY(se);
100
101         return NT_STATUS_OK;
102 }
103
104 static NTSTATUS linux_lease_setup(struct sys_lease_context *ctx,
105                                   struct opendb_entry *e)
106 {
107         int ret;
108         int *fd = (int *)e->fd;
109         struct linux_lease_pending *p;
110
111         if (e->oplock_level == OPLOCK_NONE) {
112                 e->fd = NULL;
113                 return NT_STATUS_OK;
114         } else if (e->oplock_level == OPLOCK_LEVEL_II) {
115                 /*
116                  * the linux kernel doesn't support level2 oplocks
117                  * so fix up the granted oplock level
118                  */
119                 e->oplock_level = OPLOCK_NONE;
120                 e->allow_level_II_oplock = false;
121                 e->fd = NULL;
122                 return NT_STATUS_OK;
123         }
124
125         p = talloc(ctx, struct linux_lease_pending);
126         NT_STATUS_HAVE_NO_MEMORY(p);
127
128         p->ctx = ctx;
129         p->e = *e;
130
131         ret = fcntl(*fd, F_SETSIG, LINUX_LEASE_RT_SIGNAL);
132         if (ret == -1) {
133                 talloc_free(p);
134                 return map_nt_error_from_unix_common(errno);
135         }
136
137         ret = fcntl(*fd, F_SETLEASE, F_WRLCK);
138         if (ret == -1) {
139                 talloc_free(p);
140                 return map_nt_error_from_unix_common(errno);
141         }
142
143         DLIST_ADD(leases, p);
144
145         talloc_set_destructor(p, linux_lease_pending_destructor);
146
147         return NT_STATUS_OK;
148 }
149
150 static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
151                                    struct opendb_entry *e);
152
153 static NTSTATUS linux_lease_update(struct sys_lease_context *ctx,
154                                    struct opendb_entry *e)
155 {
156         struct linux_lease_pending *c;
157
158         for (c = leases; c; c = c->next) {
159                 if (c->e.fd == e->fd) {
160                         break;
161                 }
162         }
163
164         if (!c) {
165                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
166         }
167
168         /*
169          * set the fd pointer to NULL so that the caller
170          * will not call the remove function as the oplock
171          * is already removed
172          */
173         e->fd = NULL;
174
175         talloc_free(c);
176
177         return NT_STATUS_OK;
178 }
179
180 static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
181                                    struct opendb_entry *e)
182 {
183         struct linux_lease_pending *c;
184
185         for (c = leases; c; c = c->next) {
186                 if (c->e.fd == e->fd) {
187                         break;
188                 }
189         }
190
191         if (!c) {
192                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
193         }
194
195         talloc_free(c);
196
197         return NT_STATUS_OK;
198 }
199
200 static struct sys_lease_ops linux_lease_ops = {
201         .name   = "linux",
202         .init   = linux_lease_init,
203         .setup  = linux_lease_setup,
204         .update = linux_lease_update,
205         .remove = linux_lease_remove
206 };
207
208 /*
209   initialialise the linux lease module
210  */
211 NTSTATUS sys_lease_linux_init(void)
212 {
213         /* register ourselves as a system lease module */
214         return sys_lease_register(&linux_lease_ops);
215 }