Line data Source code
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(TALLOC_CTX *);
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 0 : 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 0 : struct sys_lease_context *ctx = talloc_get_type(private_data,
52 : struct sys_lease_context);
53 0 : siginfo_t *info = (siginfo_t *)_info;
54 0 : struct linux_lease_pending *c;
55 0 : int got_fd = info->si_fd;
56 :
57 0 : for (c = leases; c; c = c->next) {
58 0 : int *fd = (int *)c->e.fd;
59 :
60 0 : if (got_fd == *fd) {
61 0 : break;
62 : }
63 : }
64 :
65 0 : if (!c) {
66 0 : return;
67 : }
68 :
69 0 : ctx->break_send(ctx->msg_ctx, &c->e, OPLOCK_BREAK_TO_NONE);
70 : }
71 :
72 0 : static int linux_lease_pending_destructor(struct linux_lease_pending *p)
73 : {
74 0 : int ret;
75 0 : int *fd = (int *)p->e.fd;
76 :
77 0 : DLIST_REMOVE(leases, p);
78 :
79 0 : if (*fd == -1) {
80 0 : return 0;
81 : }
82 :
83 0 : ret = fcntl(*fd, F_SETLEASE, F_UNLCK);
84 0 : if (ret == -1) {
85 0 : DEBUG(0,("%s: failed to remove oplock: %s\n",
86 : __FUNCTION__, strerror(errno)));
87 : }
88 :
89 0 : return 0;
90 : }
91 :
92 0 : static NTSTATUS linux_lease_init(struct sys_lease_context *ctx)
93 : {
94 0 : struct tevent_signal *se;
95 :
96 0 : se = tevent_add_signal(ctx->event_ctx, ctx,
97 : LINUX_LEASE_RT_SIGNAL, SA_SIGINFO,
98 : linux_lease_signal_handler, ctx);
99 0 : NT_STATUS_HAVE_NO_MEMORY(se);
100 :
101 0 : return NT_STATUS_OK;
102 : }
103 :
104 0 : static NTSTATUS linux_lease_setup(struct sys_lease_context *ctx,
105 : struct opendb_entry *e)
106 : {
107 0 : int ret;
108 0 : int *fd = (int *)e->fd;
109 0 : struct linux_lease_pending *p;
110 :
111 0 : if (e->oplock_level == OPLOCK_NONE) {
112 0 : e->fd = NULL;
113 0 : return NT_STATUS_OK;
114 0 : } 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 0 : e->oplock_level = OPLOCK_NONE;
120 0 : e->allow_level_II_oplock = false;
121 0 : e->fd = NULL;
122 0 : return NT_STATUS_OK;
123 : }
124 :
125 0 : p = talloc(ctx, struct linux_lease_pending);
126 0 : NT_STATUS_HAVE_NO_MEMORY(p);
127 :
128 0 : p->ctx = ctx;
129 0 : p->e = *e;
130 :
131 0 : ret = fcntl(*fd, F_SETSIG, LINUX_LEASE_RT_SIGNAL);
132 0 : if (ret == -1) {
133 0 : talloc_free(p);
134 0 : return map_nt_error_from_unix_common(errno);
135 : }
136 :
137 0 : ret = fcntl(*fd, F_SETLEASE, F_WRLCK);
138 0 : if (ret == -1) {
139 0 : talloc_free(p);
140 0 : return map_nt_error_from_unix_common(errno);
141 : }
142 :
143 0 : DLIST_ADD(leases, p);
144 :
145 0 : talloc_set_destructor(p, linux_lease_pending_destructor);
146 :
147 0 : return NT_STATUS_OK;
148 : }
149 :
150 : static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
151 : struct opendb_entry *e);
152 :
153 0 : static NTSTATUS linux_lease_update(struct sys_lease_context *ctx,
154 : struct opendb_entry *e)
155 : {
156 0 : struct linux_lease_pending *c;
157 :
158 0 : for (c = leases; c; c = c->next) {
159 0 : if (c->e.fd == e->fd) {
160 0 : break;
161 : }
162 : }
163 :
164 0 : if (!c) {
165 0 : 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 0 : e->fd = NULL;
174 :
175 0 : talloc_free(c);
176 :
177 0 : return NT_STATUS_OK;
178 : }
179 :
180 0 : static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
181 : struct opendb_entry *e)
182 : {
183 0 : struct linux_lease_pending *c;
184 :
185 0 : for (c = leases; c; c = c->next) {
186 0 : if (c->e.fd == e->fd) {
187 0 : break;
188 : }
189 : }
190 :
191 0 : if (!c) {
192 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
193 : }
194 :
195 0 : talloc_free(c);
196 :
197 0 : 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 : initialise the linux lease module
210 : */
211 4 : NTSTATUS sys_lease_linux_init(TALLOC_CTX *ctx)
212 : {
213 : /* register ourselves as a system lease module */
214 4 : return sys_lease_register(ctx, &linux_lease_ops);
215 : }
|