]>
Commit | Line | Data |
---|---|---|
ac2f15b3 A |
1 | /* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */ |
2 | /* $FreeBSD: src/usr.sbin/rpc.lockd/lockd_lock.c,v 1.10 2002/03/22 19:57:09 alfred Exp $ */ | |
3 | ||
4 | /* | |
5 | * Copyright (c) 2001 Andrew P. Lentvorski, Jr. | |
6 | * Copyright (c) 2000 Manuel Bouyer. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed by the University of | |
19 | * California, Berkeley and its contributors. | |
20 | * 4. Neither the name of the University nor the names of its contributors | |
21 | * may be used to endorse or promote products derived from this software | |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | * | |
36 | */ | |
37 | ||
38 | #define LOCKD_DEBUG | |
39 | ||
40 | #include <stdio.h> | |
41 | #ifdef LOCKD_DEBUG | |
42 | #include <stdarg.h> | |
43 | #endif | |
44 | #include <stdlib.h> | |
45 | #include <unistd.h> | |
46 | #include <fcntl.h> | |
47 | #include <syslog.h> | |
48 | #include <errno.h> | |
49 | #include <string.h> | |
50 | #include <signal.h> | |
51 | #include <rpc/rpc.h> | |
52 | #include <sys/types.h> | |
53 | #include <sys/stat.h> | |
54 | #include <sys/socket.h> | |
55 | #include <sys/param.h> | |
56 | #include <sys/mount.h> | |
57 | #include <sys/wait.h> | |
58 | #include <rpcsvc/sm_inter.h> | |
59 | #include <rpcsvc/nlm_prot.h> | |
60 | ||
61 | #include "lockd.h" | |
62 | #include "lockd_lock.h" | |
63 | ||
64 | #define MAXOBJECTSIZE 64 | |
65 | #define MAXBUFFERSIZE 1024 | |
66 | ||
ac2f15b3 A |
67 | /* |
68 | * A set of utilities for managing file locking | |
69 | * | |
70 | * XXX: All locks are in a linked list, a better structure should be used | |
71 | * to improve search/access effeciency. | |
72 | */ | |
73 | ||
74 | /* struct describing a lock */ | |
75 | struct file_lock { | |
76 | LIST_ENTRY(file_lock) nfslocklist; | |
77 | netobj filehandle; /* NFS filehandle */ | |
78 | struct sockaddr *addr; | |
79 | struct nlm4_holder client; /* lock holder */ | |
7902cf7e | 80 | u_int64_t granted_cookie; |
ac2f15b3 A |
81 | int nsm_status; /* status from the remote lock manager */ |
82 | int status; /* lock status, see below */ | |
83 | int flags; /* lock flags, see lockd_lock.h */ | |
84 | int blocking; /* blocking lock or not */ | |
ccaf7288 | 85 | char client_name[SM_MAXSTRLEN]; /* client_name is really variable length and must be last! */ |
ac2f15b3 A |
86 | }; |
87 | ||
88 | LIST_HEAD(nfslocklist_head, file_lock); | |
89 | struct nfslocklist_head nfslocklist_head = LIST_HEAD_INITIALIZER(nfslocklist_head); | |
90 | ||
91 | LIST_HEAD(blockedlocklist_head, file_lock); | |
92 | struct blockedlocklist_head blockedlocklist_head = LIST_HEAD_INITIALIZER(blockedlocklist_head); | |
93 | ||
94 | /* struct describing a share reservation */ | |
95 | struct file_share { | |
96 | LIST_ENTRY(file_share) nfssharelist; | |
97 | netobj oh; /* share holder */ | |
ac2f15b3 A |
98 | short mode; |
99 | short access; | |
ccaf7288 | 100 | char client_name[SM_MAXSTRLEN]; /* name is really variable length and must be last! */ |
ac2f15b3 A |
101 | }; |
102 | LIST_HEAD(nfssharelist_head, file_share); | |
103 | ||
104 | /* Struct describing a file with share reservations */ | |
105 | struct sharefile { | |
106 | LIST_ENTRY(sharefile) sharefilelist; | |
107 | netobj filehandle; /* Local access filehandle */ | |
108 | int fd; /* file descriptor: remains open until no more shares */ | |
109 | int refcount; | |
110 | struct nfssharelist_head sharelist_head; | |
111 | }; | |
112 | LIST_HEAD(nfssharefilelist_head, sharefile); | |
113 | struct nfssharefilelist_head nfssharefilelist_head = LIST_HEAD_INITIALIZER(nfssharefilelist_head); | |
114 | ||
115 | /* lock status */ | |
116 | #define LKST_LOCKED 1 /* lock is locked */ | |
117 | /* XXX: Is this flag file specific or lock specific? */ | |
118 | #define LKST_WAITING 2 /* file is already locked by another host */ | |
119 | #define LKST_PROCESSING 3 /* child is trying to aquire the lock */ | |
120 | #define LKST_DYING 4 /* must dies when we get news from the child */ | |
121 | ||
122 | /* struct describing a monitored host */ | |
123 | struct host { | |
7902cf7e | 124 | TAILQ_ENTRY(host) hostlst; |
ac2f15b3 | 125 | int refcnt; |
7902cf7e | 126 | time_t lastuse; |
ccaf7288 | 127 | struct sockaddr addr; |
6720d03d A |
128 | char *name; /* host name provided by client via caller_name */ |
129 | char *revname; /* host name mapped from addr */ | |
ac2f15b3 A |
130 | }; |
131 | /* list of hosts we monitor */ | |
7902cf7e A |
132 | TAILQ_HEAD(hostlst_head, host); |
133 | struct hostlst_head hostlst_head = TAILQ_HEAD_INITIALIZER(hostlst_head); | |
134 | struct hostlst_head hostlst_unref = TAILQ_HEAD_INITIALIZER(hostlst_unref); | |
135 | ||
136 | int host_expire = 60; /* seconds */ | |
137 | time_t currsec; | |
138 | u_int64_t send_granted_cookie = 0; | |
ac2f15b3 A |
139 | |
140 | /* | |
141 | * File monitoring handlers | |
142 | * XXX: These might be able to be removed when kevent support | |
143 | * is placed into the hardware lock/unlock routines. (ie. | |
144 | * let the kernel do all the file monitoring) | |
145 | */ | |
146 | ||
147 | /* Struct describing a monitored file */ | |
148 | struct monfile { | |
149 | LIST_ENTRY(monfile) monfilelist; | |
150 | netobj filehandle; /* Local access filehandle */ | |
151 | int fd; /* file descriptor: remains open until unlock! */ | |
152 | int refcount; | |
153 | int exclusive; | |
154 | }; | |
155 | ||
156 | /* List of files we monitor */ | |
157 | LIST_HEAD(monfilelist_head, monfile); | |
158 | struct monfilelist_head monfilelist_head = LIST_HEAD_INITIALIZER(monfilelist_head); | |
159 | ||
160 | static int debugdelay = 0; | |
161 | ||
162 | enum nfslock_status { NFS_GRANTED = 0, NFS_GRANTED_DUPLICATE, | |
163 | NFS_DENIED, NFS_DENIED_NOLOCK, | |
164 | NFS_RESERR }; | |
165 | ||
166 | enum hwlock_status { HW_GRANTED = 0, HW_GRANTED_DUPLICATE, | |
167 | HW_DENIED, HW_DENIED_NOLOCK, | |
168 | HW_STALEFH, HW_READONLY, HW_RESERR }; | |
169 | ||
170 | enum partialfilelock_status { PFL_GRANTED=0, PFL_GRANTED_DUPLICATE, PFL_DENIED, | |
171 | PFL_NFSDENIED, PFL_NFSBLOCKED, PFL_NFSDENIED_NOLOCK, PFL_NFSRESERR, | |
2b484d24 A |
172 | PFL_HWDENIED, PFL_HWBLOCKED, PFL_HWDENIED_NOLOCK, PFL_HWRESERR, |
173 | PFL_HWDENIED_STALEFH, PFL_HWDENIED_READONLY }; | |
ac2f15b3 A |
174 | |
175 | enum LFLAGS {LEDGE_LEFT, LEDGE_LBOUNDARY, LEDGE_INSIDE, LEDGE_RBOUNDARY, LEDGE_RIGHT}; | |
176 | enum RFLAGS {REDGE_LEFT, REDGE_LBOUNDARY, REDGE_INSIDE, REDGE_RBOUNDARY, REDGE_RIGHT}; | |
177 | /* XXX: WARNING! I HAVE OVERLOADED THIS STATUS ENUM! SPLIT IT APART INTO TWO */ | |
178 | enum split_status {SPL_DISJOINT=0, SPL_LOCK1=1, SPL_LOCK2=2, SPL_CONTAINED=4, SPL_RESERR=8}; | |
179 | ||
180 | enum partialfilelock_status lock_partialfilelock(struct file_lock *fl); | |
181 | ||
7902cf7e | 182 | int send_granted(struct file_lock *fl, int opcode); |
ac2f15b3 A |
183 | void siglock(void); |
184 | void sigunlock(void); | |
7902cf7e | 185 | void destroy_lock_host(struct host *ihp); |
ccaf7288 | 186 | static void monitor_lock_host(const char *hostname, const struct sockaddr *addr); |
ac2f15b3 A |
187 | |
188 | void copy_nlm4_lock_to_nlm4_holder(const struct nlm4_lock *src, | |
189 | const bool_t exclusive, struct nlm4_holder *dest); | |
190 | struct file_lock * allocate_file_lock(const netobj *lockowner, | |
ccaf7288 A |
191 | const netobj *filehandle, const struct sockaddr *addr, |
192 | const char *caller_name); | |
ac2f15b3 A |
193 | void deallocate_file_lock(struct file_lock *fl); |
194 | void fill_file_lock(struct file_lock *fl, | |
ccaf7288 A |
195 | const bool_t exclusive, const int32_t svid, |
196 | const u_int64_t offset, const u_int64_t len, | |
ac2f15b3 A |
197 | const int state, const int status, const int flags, const int blocking); |
198 | int regions_overlap(const u_int64_t start1, const u_int64_t len1, | |
199 | const u_int64_t start2, const u_int64_t len2);; | |
200 | enum split_status region_compare(const u_int64_t starte, const u_int64_t lene, | |
201 | const u_int64_t startu, const u_int64_t lenu, | |
202 | u_int64_t *start1, u_int64_t *len1, u_int64_t *start2, u_int64_t *len2); | |
203 | int same_netobj(const netobj *n0, const netobj *n1); | |
204 | int same_filelock_identity(const struct file_lock *fl0, | |
205 | const struct file_lock *fl2); | |
206 | ||
207 | static void debuglog(char const *fmt, ...); | |
208 | void dump_static_object(const unsigned char* object, const int sizeof_object, | |
209 | unsigned char* hbuff, const int sizeof_hbuff, | |
210 | unsigned char* cbuff, const int sizeof_cbuff); | |
211 | void dump_netobj(const struct netobj *nobj); | |
212 | void dump_filelock(const struct file_lock *fl); | |
213 | struct file_lock * get_lock_matching_unlock(const struct file_lock *fl); | |
214 | enum nfslock_status test_nfslock(const struct file_lock *fl, | |
215 | struct file_lock **conflicting_fl); | |
216 | enum nfslock_status lock_nfslock(struct file_lock *fl); | |
217 | enum nfslock_status delete_nfslock(struct file_lock *fl); | |
218 | enum nfslock_status unlock_nfslock(const struct file_lock *fl, | |
219 | struct file_lock **released_lock, struct file_lock **left_lock, | |
220 | struct file_lock **right_lock); | |
221 | enum hwlock_status lock_hwlock(struct file_lock *fl); | |
222 | enum split_status split_nfslock(const struct file_lock *exist_lock, | |
223 | const struct file_lock *unlock_lock, struct file_lock **left_lock, | |
224 | struct file_lock **right_lock); | |
225 | void add_blockingfilelock(struct file_lock *fl); | |
226 | enum hwlock_status unlock_hwlock(const struct file_lock *fl); | |
227 | enum hwlock_status test_hwlock(const struct file_lock *fl, | |
228 | struct file_lock **conflicting_fl); | |
229 | void remove_blockingfilelock(struct file_lock *fl); | |
230 | void clear_blockingfilelock(const char *hostname); | |
7902cf7e | 231 | void retry_blockingfilelocklist(netobj *fh); |
ac2f15b3 A |
232 | enum partialfilelock_status unlock_partialfilelock( |
233 | const struct file_lock *fl); | |
234 | void clear_partialfilelock(const char *hostname); | |
235 | enum partialfilelock_status test_partialfilelock( | |
236 | const struct file_lock *fl, struct file_lock **conflicting_fl); | |
2b484d24 A |
237 | enum nlm4_stats do_test(struct file_lock *fl, struct file_lock **conflicting_fl); |
238 | enum nlm4_stats do_unlock(struct file_lock *fl); | |
239 | enum nlm4_stats do_lock(struct file_lock *fl); | |
ac2f15b3 A |
240 | void do_clear(const char *hostname); |
241 | ||
242 | ||
243 | void | |
244 | debuglog(char const *fmt, ...) | |
245 | { | |
246 | va_list ap; | |
247 | ||
248 | if (debug_level < 1) { | |
249 | return; | |
250 | } | |
251 | ||
252 | sleep(debugdelay); | |
253 | ||
254 | va_start(ap, fmt); | |
255 | vsyslog(LOG_DEBUG, fmt, ap); | |
256 | va_end(ap); | |
257 | } | |
258 | ||
259 | void | |
260 | dump_static_object(object, size_object, hbuff, size_hbuff, cbuff, size_cbuff) | |
261 | const unsigned char *object; | |
262 | const int size_object; | |
263 | unsigned char *hbuff; | |
264 | const int size_hbuff; | |
265 | unsigned char *cbuff; | |
266 | const int size_cbuff; | |
267 | { | |
268 | int i, objectsize; | |
269 | ||
270 | if (debug_level < 2) { | |
271 | return; | |
272 | } | |
273 | ||
274 | objectsize = size_object; | |
275 | ||
276 | if (objectsize == 0) { | |
277 | debuglog("object is size 0\n"); | |
278 | } else { | |
279 | if (objectsize > MAXOBJECTSIZE) { | |
280 | debuglog("Object of size %d being clamped" | |
281 | "to size %d\n", objectsize, MAXOBJECTSIZE); | |
282 | objectsize = MAXOBJECTSIZE; | |
283 | } | |
284 | ||
285 | if (hbuff != NULL) { | |
286 | if (size_hbuff < objectsize*2+1) { | |
287 | debuglog("Hbuff not large enough." | |
288 | " Increase size\n"); | |
289 | } else { | |
290 | for(i=0;i<objectsize;i++) { | |
291 | sprintf(hbuff+i*2,"%02x",*(object+i)); | |
292 | } | |
293 | *(hbuff+i*2) = '\0'; | |
294 | } | |
295 | } | |
296 | ||
297 | if (cbuff != NULL) { | |
298 | if (size_cbuff < objectsize+1) { | |
299 | debuglog("Cbuff not large enough." | |
300 | " Increase Size\n"); | |
301 | } | |
302 | ||
303 | for(i=0;i<objectsize;i++) { | |
304 | if (*(object+i) >= 32 && *(object+i) <= 127) { | |
305 | *(cbuff+i) = *(object+i); | |
306 | } else { | |
307 | *(cbuff+i) = '.'; | |
308 | } | |
309 | } | |
310 | *(cbuff+i) = '\0'; | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | void | |
316 | dump_netobj(const struct netobj *nobj) | |
317 | { | |
318 | char hbuff[MAXBUFFERSIZE*2]; | |
319 | char cbuff[MAXBUFFERSIZE]; | |
320 | ||
321 | if (debug_level < 2) { | |
322 | return; | |
323 | } | |
324 | ||
325 | if (nobj == NULL) { | |
326 | debuglog("Null netobj pointer\n"); | |
327 | } | |
328 | else if (nobj->n_len == 0) { | |
329 | debuglog("Size zero netobj\n"); | |
330 | } else { | |
331 | dump_static_object(nobj->n_bytes, nobj->n_len, | |
332 | hbuff, sizeof(hbuff), cbuff, sizeof(cbuff)); | |
333 | debuglog("netobj: len: %d data: %s ::: %s\n", | |
334 | nobj->n_len, hbuff, cbuff); | |
335 | } | |
336 | } | |
337 | ||
338 | /* #define DUMP_FILELOCK_VERBOSE */ | |
339 | void | |
340 | dump_filelock(const struct file_lock *fl) | |
341 | { | |
342 | #ifdef DUMP_FILELOCK_VERBOSE | |
343 | char hbuff[MAXBUFFERSIZE*2]; | |
344 | char cbuff[MAXBUFFERSIZE]; | |
345 | #endif | |
346 | ||
347 | if (debug_level < 2) { | |
348 | return; | |
349 | } | |
350 | ||
351 | if (fl != NULL) { | |
352 | debuglog("Dumping file lock structure @ %p\n", fl); | |
353 | ||
354 | #ifdef DUMP_FILELOCK_VERBOSE | |
355 | dump_static_object((unsigned char *)&fl->filehandle.n_bytes, | |
356 | fl->filehandle.n_len, hbuff, sizeof(hbuff), | |
357 | cbuff, sizeof(cbuff)); | |
358 | debuglog("Filehandle: %8s ::: %8s\n", hbuff, cbuff); | |
359 | #endif | |
360 | ||
361 | debuglog("Dumping nlm4_holder:\n" | |
362 | "exc: %x svid: %x offset:len %llx:%llx\n", | |
363 | fl->client.exclusive, fl->client.svid, | |
364 | fl->client.l_offset, fl->client.l_len); | |
365 | ||
366 | #ifdef DUMP_FILELOCK_VERBOSE | |
367 | debuglog("Dumping client identity:\n"); | |
368 | dump_netobj(&fl->client.oh); | |
369 | ||
ac2f15b3 A |
370 | debuglog("nsm: %d status: %d flags: %d locker: %d" |
371 | " fd: %d\n", fl->nsm_status, fl->status, | |
372 | fl->flags, fl->locker, fl->fd); | |
373 | #endif | |
374 | } else { | |
375 | debuglog("NULL file lock structure\n"); | |
376 | } | |
377 | } | |
378 | ||
379 | void | |
380 | copy_nlm4_lock_to_nlm4_holder(src, exclusive, dest) | |
381 | const struct nlm4_lock *src; | |
382 | const bool_t exclusive; | |
383 | struct nlm4_holder *dest; | |
384 | { | |
385 | ||
386 | dest->exclusive = exclusive; | |
387 | dest->oh.n_len = src->oh.n_len; | |
388 | dest->oh.n_bytes = src->oh.n_bytes; | |
389 | dest->svid = src->svid; | |
390 | dest->l_offset = src->l_offset; | |
391 | dest->l_len = src->l_len; | |
392 | } | |
393 | ||
394 | ||
ccaf7288 A |
395 | size_t |
396 | strnlen(const char *s, size_t len) | |
397 | { | |
398 | size_t n; | |
399 | ||
400 | for (n = 0; s[n] != 0 && n < len; n++) | |
401 | ; | |
402 | return n; | |
403 | } | |
404 | ||
ac2f15b3 A |
405 | /* |
406 | * allocate_file_lock: Create a lock with the given parameters | |
407 | */ | |
408 | ||
409 | struct file_lock * | |
ccaf7288 A |
410 | allocate_file_lock(const netobj *lockowner, const netobj *filehandle, |
411 | const struct sockaddr *addr, const char *caller_name) | |
ac2f15b3 A |
412 | { |
413 | struct file_lock *newfl; | |
ccaf7288 A |
414 | size_t n; |
415 | ||
416 | /* Beware of rubbish input! */ | |
417 | n = strnlen(caller_name, SM_MAXSTRLEN); | |
418 | if (n == SM_MAXSTRLEN) { | |
419 | return NULL; | |
420 | } | |
ac2f15b3 | 421 | |
ccaf7288 | 422 | newfl = malloc(sizeof(*newfl) - sizeof(newfl->client_name) + n + 1); |
ac2f15b3 A |
423 | if (newfl == NULL) { |
424 | return NULL; | |
425 | } | |
ccaf7288 A |
426 | bzero(newfl, sizeof(*newfl) - sizeof(newfl->client_name)); |
427 | memcpy(newfl->client_name, caller_name, n); | |
428 | newfl->client_name[n] = 0; | |
ac2f15b3 A |
429 | |
430 | newfl->client.oh.n_bytes = malloc(lockowner->n_len); | |
431 | if (newfl->client.oh.n_bytes == NULL) { | |
432 | free(newfl); | |
433 | return NULL; | |
434 | } | |
435 | newfl->client.oh.n_len = lockowner->n_len; | |
436 | bcopy(lockowner->n_bytes, newfl->client.oh.n_bytes, lockowner->n_len); | |
437 | ||
ac2f15b3 A |
438 | newfl->filehandle.n_bytes = malloc(filehandle->n_len); |
439 | if (newfl->filehandle.n_bytes == NULL) { | |
ac2f15b3 A |
440 | free(newfl->client.oh.n_bytes); |
441 | free(newfl); | |
442 | return NULL; | |
443 | } | |
444 | newfl->filehandle.n_len = filehandle->n_len; | |
445 | bcopy(filehandle->n_bytes, newfl->filehandle.n_bytes, filehandle->n_len); | |
446 | ||
ccaf7288 A |
447 | newfl->addr = malloc(addr->sa_len); |
448 | if (newfl->addr == NULL) { | |
449 | free(newfl->client.oh.n_bytes); | |
450 | free(newfl); | |
451 | return NULL; | |
452 | } | |
453 | memcpy(newfl->addr, addr, addr->sa_len); | |
454 | ||
ac2f15b3 A |
455 | return newfl; |
456 | } | |
457 | ||
458 | /* | |
459 | * file_file_lock: Force creation of a valid file lock | |
460 | */ | |
461 | void | |
462 | fill_file_lock(struct file_lock *fl, | |
ccaf7288 A |
463 | const bool_t exclusive, const int32_t svid, |
464 | const u_int64_t offset, const u_int64_t len, | |
ac2f15b3 A |
465 | const int state, const int status, const int flags, const int blocking) |
466 | { | |
ac2f15b3 A |
467 | fl->client.exclusive = exclusive; |
468 | fl->client.svid = svid; | |
469 | fl->client.l_offset = offset; | |
470 | fl->client.l_len = len; | |
471 | ||
ac2f15b3 A |
472 | fl->nsm_status = state; |
473 | fl->status = status; | |
474 | fl->flags = flags; | |
475 | fl->blocking = blocking; | |
476 | } | |
477 | ||
478 | /* | |
479 | * deallocate_file_lock: Free all storage associated with a file lock | |
480 | */ | |
481 | void | |
482 | deallocate_file_lock(struct file_lock *fl) | |
483 | { | |
ccaf7288 | 484 | free(fl->addr); |
ac2f15b3 | 485 | free(fl->client.oh.n_bytes); |
ac2f15b3 A |
486 | free(fl->filehandle.n_bytes); |
487 | free(fl); | |
488 | } | |
489 | ||
490 | /* | |
491 | * regions_overlap(): This function examines the two provided regions for | |
492 | * overlap. | |
493 | */ | |
494 | int | |
495 | regions_overlap(start1, len1, start2, len2) | |
496 | const u_int64_t start1, len1, start2, len2; | |
497 | { | |
498 | u_int64_t d1,d2,d3,d4; | |
499 | enum split_status result; | |
500 | ||
501 | debuglog("Entering region overlap with vals: %llu:%llu--%llu:%llu\n", | |
502 | start1, len1, start2, len2); | |
503 | ||
504 | result = region_compare(start1, len1, start2, len2, | |
505 | &d1, &d2, &d3, &d4); | |
506 | ||
507 | debuglog("Exiting region overlap with val: %d\n",result); | |
508 | ||
509 | if (result == SPL_DISJOINT) { | |
510 | return 0; | |
511 | } else { | |
512 | return 1; | |
513 | } | |
514 | ||
515 | return (result); | |
516 | } | |
517 | ||
518 | /* | |
519 | * region_compare(): Examine lock regions and split appropriately | |
520 | * | |
521 | * XXX: Fix 64 bit overflow problems | |
522 | * XXX: Check to make sure I got *ALL* the cases. | |
523 | * XXX: This DESPERATELY needs a regression test. | |
524 | */ | |
525 | enum split_status | |
526 | region_compare(starte, lene, startu, lenu, | |
527 | start1, len1, start2, len2) | |
528 | const u_int64_t starte, lene, startu, lenu; | |
529 | u_int64_t *start1, *len1, *start2, *len2; | |
530 | { | |
531 | /* | |
532 | * Please pay attention to the sequential exclusions | |
533 | * of the if statements!!! | |
534 | */ | |
535 | enum LFLAGS lflags; | |
536 | enum RFLAGS rflags; | |
537 | enum split_status retval; | |
538 | ||
539 | retval = SPL_DISJOINT; | |
540 | ||
541 | if (lene == 0 && lenu == 0) { | |
542 | /* Examine left edge of locker */ | |
543 | if (startu < starte) { | |
544 | lflags = LEDGE_LEFT; | |
545 | } else if (startu == starte) { | |
546 | lflags = LEDGE_LBOUNDARY; | |
547 | } else { | |
548 | lflags = LEDGE_INSIDE; | |
549 | } | |
550 | ||
551 | rflags = REDGE_RBOUNDARY; /* Both are infiinite */ | |
552 | ||
553 | if (lflags == LEDGE_INSIDE) { | |
554 | *start1 = starte; | |
555 | *len1 = startu - starte; | |
556 | } | |
557 | ||
558 | if (lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) { | |
559 | retval = SPL_CONTAINED; | |
560 | } else { | |
561 | retval = SPL_LOCK1; | |
562 | } | |
563 | } else if (lene == 0 && lenu != 0) { | |
564 | /* Established lock is infinite */ | |
565 | /* Examine left edge of unlocker */ | |
566 | if (startu < starte) { | |
567 | lflags = LEDGE_LEFT; | |
568 | } else if (startu == starte) { | |
569 | lflags = LEDGE_LBOUNDARY; | |
570 | } else if (startu > starte) { | |
571 | lflags = LEDGE_INSIDE; | |
572 | } | |
573 | ||
574 | /* Examine right edge of unlocker */ | |
575 | if (startu + lenu < starte) { | |
576 | /* Right edge of unlocker left of established lock */ | |
577 | rflags = REDGE_LEFT; | |
578 | return SPL_DISJOINT; | |
579 | } else if (startu + lenu == starte) { | |
580 | /* Right edge of unlocker on start of established lock */ | |
581 | rflags = REDGE_LBOUNDARY; | |
582 | return SPL_DISJOINT; | |
583 | } else { /* Infinifty is right of finity */ | |
584 | /* Right edge of unlocker inside established lock */ | |
585 | rflags = REDGE_INSIDE; | |
586 | } | |
587 | ||
588 | if (lflags == LEDGE_INSIDE) { | |
589 | *start1 = starte; | |
590 | *len1 = startu - starte; | |
591 | retval |= SPL_LOCK1; | |
592 | } | |
593 | ||
594 | if (rflags == REDGE_INSIDE) { | |
595 | /* Create right lock */ | |
596 | *start2 = startu+lenu; | |
597 | *len2 = 0; | |
598 | retval |= SPL_LOCK2; | |
599 | } | |
600 | } else if (lene != 0 && lenu == 0) { | |
601 | /* Unlocker is infinite */ | |
602 | /* Examine left edge of unlocker */ | |
603 | if (startu < starte) { | |
604 | lflags = LEDGE_LEFT; | |
605 | retval = SPL_CONTAINED; | |
606 | return retval; | |
607 | } else if (startu == starte) { | |
608 | lflags = LEDGE_LBOUNDARY; | |
609 | retval = SPL_CONTAINED; | |
610 | return retval; | |
611 | } else if ((startu > starte) && (startu < starte + lene - 1)) { | |
612 | lflags = LEDGE_INSIDE; | |
613 | } else if (startu == starte + lene - 1) { | |
614 | lflags = LEDGE_RBOUNDARY; | |
615 | } else { /* startu > starte + lene -1 */ | |
616 | lflags = LEDGE_RIGHT; | |
617 | return SPL_DISJOINT; | |
618 | } | |
619 | ||
620 | rflags = REDGE_RIGHT; /* Infinity is right of finity */ | |
621 | ||
622 | if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) { | |
623 | *start1 = starte; | |
624 | *len1 = startu - starte; | |
625 | retval |= SPL_LOCK1; | |
626 | return retval; | |
627 | } | |
628 | ||
629 | } else { | |
630 | /* Both locks are finite */ | |
631 | ||
632 | /* Examine left edge of unlocker */ | |
633 | if (startu < starte) { | |
634 | lflags = LEDGE_LEFT; | |
635 | } else if (startu == starte) { | |
636 | lflags = LEDGE_LBOUNDARY; | |
637 | } else if ((startu > starte) && (startu < starte + lene - 1)) { | |
638 | lflags = LEDGE_INSIDE; | |
639 | } else if (startu == starte + lene - 1) { | |
640 | lflags = LEDGE_RBOUNDARY; | |
641 | } else { /* startu > starte + lene -1 */ | |
642 | lflags = LEDGE_RIGHT; | |
643 | return SPL_DISJOINT; | |
644 | } | |
645 | ||
646 | /* Examine right edge of unlocker */ | |
647 | if (startu + lenu < starte) { | |
648 | /* Right edge of unlocker left of established lock */ | |
649 | rflags = REDGE_LEFT; | |
650 | return SPL_DISJOINT; | |
651 | } else if (startu + lenu == starte) { | |
652 | /* Right edge of unlocker on start of established lock */ | |
653 | rflags = REDGE_LBOUNDARY; | |
654 | return SPL_DISJOINT; | |
655 | } else if (startu + lenu < starte + lene) { | |
656 | /* Right edge of unlocker inside established lock */ | |
657 | rflags = REDGE_INSIDE; | |
658 | } else if (startu + lenu == starte + lene) { | |
659 | /* Right edge of unlocker on right edge of established lock */ | |
660 | rflags = REDGE_RBOUNDARY; | |
661 | } else { /* startu + lenu > starte + lene */ | |
662 | /* Right edge of unlocker is right of established lock */ | |
663 | rflags = REDGE_RIGHT; | |
664 | } | |
665 | ||
666 | if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) { | |
667 | /* Create left lock */ | |
668 | *start1 = starte; | |
669 | *len1 = (startu - starte); | |
670 | retval |= SPL_LOCK1; | |
671 | } | |
672 | ||
673 | if (rflags == REDGE_INSIDE) { | |
674 | /* Create right lock */ | |
675 | *start2 = startu+lenu; | |
676 | *len2 = starte+lene-(startu+lenu); | |
677 | retval |= SPL_LOCK2; | |
678 | } | |
679 | ||
680 | if ((lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) && | |
681 | (rflags == REDGE_RBOUNDARY || rflags == REDGE_RIGHT)) { | |
682 | retval = SPL_CONTAINED; | |
683 | } | |
684 | } | |
685 | ||
686 | return retval; | |
687 | } | |
688 | ||
689 | /* | |
690 | * same_netobj: Compares the apprpriate bits of a netobj for identity | |
691 | */ | |
692 | int | |
693 | same_netobj(const netobj *n0, const netobj *n1) | |
694 | { | |
695 | int retval; | |
696 | ||
697 | retval = 0; | |
698 | ||
699 | debuglog("Entering netobj identity check\n"); | |
700 | ||
701 | if (n0->n_len == n1->n_len) { | |
702 | debuglog("Preliminary length check passed\n"); | |
703 | retval = !bcmp(n0->n_bytes, n1->n_bytes, n0->n_len); | |
704 | debuglog("netobj %smatch\n", retval ? "" : "mis"); | |
705 | } | |
706 | ||
707 | return (retval); | |
708 | } | |
709 | ||
710 | /* | |
711 | * same_filelock_identity: Compares the appropriate bits of a file_lock | |
712 | */ | |
713 | int | |
714 | same_filelock_identity(fl0, fl1) | |
715 | const struct file_lock *fl0, *fl1; | |
716 | { | |
717 | int retval; | |
718 | ||
719 | retval = 0; | |
720 | ||
721 | debuglog("Checking filelock identity\n"); | |
722 | ||
723 | /* | |
724 | * Check process ids and host information. | |
725 | */ | |
726 | retval = (fl0->client.svid == fl1->client.svid && | |
727 | same_netobj(&(fl0->client.oh), &(fl1->client.oh))); | |
728 | ||
729 | debuglog("Exiting checking filelock identity: retval: %d\n",retval); | |
730 | ||
731 | return (retval); | |
732 | } | |
733 | ||
734 | /* | |
735 | * Below here are routines associated with manipulating the NFS | |
736 | * lock list. | |
737 | */ | |
738 | ||
739 | /* | |
740 | * get_lock_matching_unlock: Return a lock which matches the given unlock lock | |
7902cf7e | 741 | * or NULL otherwise |
ac2f15b3 A |
742 | * XXX: It is a shame that this duplicates so much code from test_nfslock. |
743 | */ | |
744 | struct file_lock * | |
745 | get_lock_matching_unlock(const struct file_lock *fl) | |
746 | { | |
747 | struct file_lock *ifl; /* Iterator */ | |
748 | ||
749 | debuglog("Entering lock_matching_unlock\n"); | |
750 | debuglog("********Dump of fl*****************\n"); | |
751 | dump_filelock(fl); | |
752 | ||
753 | LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { | |
754 | debuglog("Pointer to file lock: %p\n",ifl); | |
755 | ||
756 | debuglog("****Dump of ifl****\n"); | |
757 | dump_filelock(ifl); | |
758 | debuglog("*******************\n"); | |
759 | ||
760 | /* | |
761 | * XXX: It is conceivable that someone could use the NLM RPC | |
762 | * system to directly access filehandles. This may be a | |
763 | * security hazard as the filehandle code may bypass normal | |
764 | * file access controls | |
765 | */ | |
766 | if (fl->filehandle.n_len != ifl->filehandle.n_len) | |
767 | continue; | |
768 | if (bcmp(fl->filehandle.n_bytes, ifl->filehandle.n_bytes, | |
769 | fl->filehandle.n_len)) | |
770 | continue; | |
771 | ||
772 | debuglog("matching_unlock: Filehandles match, " | |
773 | "checking regions\n"); | |
774 | ||
775 | /* Filehandles match, check for region overlap */ | |
776 | if (!regions_overlap(fl->client.l_offset, fl->client.l_len, | |
777 | ifl->client.l_offset, ifl->client.l_len)) | |
778 | continue; | |
779 | ||
780 | debuglog("matching_unlock: Region overlap" | |
781 | " found %llu : %llu -- %llu : %llu\n", | |
782 | fl->client.l_offset,fl->client.l_len, | |
783 | ifl->client.l_offset,ifl->client.l_len); | |
784 | ||
785 | /* Regions overlap, check the identity */ | |
786 | if (!same_filelock_identity(fl,ifl)) | |
787 | continue; | |
788 | ||
789 | debuglog("matching_unlock: Duplicate lock id. Granting\n"); | |
790 | return (ifl); | |
791 | } | |
792 | ||
793 | debuglog("Exiting lock_matching_unlock\n"); | |
794 | ||
795 | return (NULL); | |
796 | } | |
797 | ||
798 | /* | |
799 | * test_nfslock: check for NFS lock in lock list | |
800 | * | |
801 | * This routine makes the following assumptions: | |
802 | * 1) Nothing will adjust the lock list during a lookup | |
803 | * | |
804 | * This routine has an intersting quirk which bit me hard. | |
805 | * The conflicting_fl is the pointer to the conflicting lock. | |
806 | * However, to modify the "*pointer* to the conflicting lock" rather | |
807 | * that the "conflicting lock itself" one must pass in a "pointer to | |
808 | * the pointer of the conflicting lock". Gross. | |
809 | */ | |
810 | ||
811 | enum nfslock_status | |
812 | test_nfslock(const struct file_lock *fl, struct file_lock **conflicting_fl) | |
813 | { | |
814 | struct file_lock *ifl; /* Iterator */ | |
815 | enum nfslock_status retval; | |
816 | ||
817 | debuglog("Entering test_nfslock\n"); | |
818 | ||
819 | retval = NFS_GRANTED; | |
820 | (*conflicting_fl) = NULL; | |
821 | ||
822 | debuglog("Entering lock search loop\n"); | |
823 | ||
824 | debuglog("***********************************\n"); | |
825 | debuglog("Dumping match filelock\n"); | |
826 | debuglog("***********************************\n"); | |
827 | dump_filelock(fl); | |
828 | debuglog("***********************************\n"); | |
829 | ||
830 | LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { | |
831 | if (retval == NFS_DENIED) | |
832 | break; | |
833 | ||
834 | debuglog("Top of lock loop\n"); | |
835 | debuglog("Pointer to file lock: %p\n",ifl); | |
836 | ||
837 | debuglog("***********************************\n"); | |
838 | debuglog("Dumping test filelock\n"); | |
839 | debuglog("***********************************\n"); | |
840 | dump_filelock(ifl); | |
841 | debuglog("***********************************\n"); | |
842 | ||
843 | /* | |
844 | * XXX: It is conceivable that someone could use the NLM RPC | |
845 | * system to directly access filehandles. This may be a | |
846 | * security hazard as the filehandle code may bypass normal | |
847 | * file access controls | |
848 | */ | |
849 | if (fl->filehandle.n_len != ifl->filehandle.n_len) | |
850 | continue; | |
851 | if (bcmp(fl->filehandle.n_bytes, ifl->filehandle.n_bytes, | |
852 | fl->filehandle.n_len)) | |
853 | continue; | |
854 | ||
855 | debuglog("test_nfslock: filehandle match found\n"); | |
856 | ||
857 | /* Filehandles match, check for region overlap */ | |
858 | if (!regions_overlap(fl->client.l_offset, fl->client.l_len, | |
859 | ifl->client.l_offset, ifl->client.l_len)) | |
860 | continue; | |
861 | ||
862 | debuglog("test_nfslock: Region overlap found" | |
863 | " %llu : %llu -- %llu : %llu\n", | |
864 | fl->client.l_offset,fl->client.l_len, | |
865 | ifl->client.l_offset,ifl->client.l_len); | |
866 | ||
867 | /* Regions overlap, check the exclusivity */ | |
868 | if (!(fl->client.exclusive || ifl->client.exclusive)) | |
869 | continue; | |
870 | ||
871 | debuglog("test_nfslock: Exclusivity failure: %d %d\n", | |
872 | fl->client.exclusive, | |
873 | ifl->client.exclusive); | |
874 | ||
875 | if (same_filelock_identity(fl,ifl)) { | |
876 | debuglog("test_nfslock: Duplicate id. Granting\n"); | |
877 | (*conflicting_fl) = ifl; | |
878 | retval = NFS_GRANTED_DUPLICATE; | |
879 | } else { | |
880 | /* locking attempt fails */ | |
881 | debuglog("test_nfslock: Lock attempt failed\n"); | |
882 | debuglog("Desired lock\n"); | |
883 | dump_filelock(fl); | |
884 | debuglog("Conflicting lock\n"); | |
885 | dump_filelock(ifl); | |
886 | (*conflicting_fl) = ifl; | |
887 | retval = NFS_DENIED; | |
888 | } | |
889 | } | |
890 | ||
891 | debuglog("Dumping file locks\n"); | |
892 | debuglog("Exiting test_nfslock\n"); | |
893 | ||
894 | return (retval); | |
895 | } | |
896 | ||
897 | /* | |
898 | * lock_nfslock: attempt to create a lock in the NFS lock list | |
899 | * | |
900 | * This routine tests whether the lock will be granted and then adds | |
901 | * the entry to the lock list if so. | |
902 | * | |
903 | * Argument fl gets modified as its list housekeeping entries get modified | |
904 | * upon insertion into the NFS lock list | |
905 | * | |
906 | * This routine makes several assumptions: | |
907 | * 1) It is perfectly happy to grant a duplicate lock from the same pid. | |
908 | * While this seems to be intuitively wrong, it is required for proper | |
909 | * Posix semantics during unlock. It is absolutely imperative to not | |
910 | * unlock the main lock before the two child locks are established. Thus, | |
911 | * one has be be able to create duplicate locks over an existing lock | |
912 | * 2) It currently accepts duplicate locks from the same id,pid | |
913 | */ | |
914 | ||
915 | enum nfslock_status | |
916 | lock_nfslock(struct file_lock *fl) | |
917 | { | |
918 | enum nfslock_status retval; | |
919 | struct file_lock *dummy_fl; | |
920 | ||
921 | dummy_fl = NULL; | |
922 | ||
923 | debuglog("Entering lock_nfslock...\n"); | |
924 | ||
925 | retval = test_nfslock(fl,&dummy_fl); | |
926 | ||
927 | if (retval == NFS_GRANTED || retval == NFS_GRANTED_DUPLICATE) { | |
928 | debuglog("Inserting lock...\n"); | |
929 | dump_filelock(fl); | |
930 | LIST_INSERT_HEAD(&nfslocklist_head, fl, nfslocklist); | |
931 | } | |
932 | ||
933 | debuglog("Exiting lock_nfslock...\n"); | |
934 | ||
935 | return (retval); | |
936 | } | |
937 | ||
938 | /* | |
939 | * delete_nfslock: delete an NFS lock list entry | |
940 | * | |
941 | * This routine is used to delete a lock out of the NFS lock list | |
942 | * without regard to status, underlying locks, regions or anything else | |
943 | * | |
944 | * Note that this routine *does not deallocate memory* of the lock. | |
945 | * It just disconnects it from the list. The lock can then be used | |
946 | * by other routines without fear of trashing the list. | |
947 | */ | |
948 | ||
949 | enum nfslock_status | |
950 | delete_nfslock(struct file_lock *fl) | |
951 | { | |
952 | ||
953 | LIST_REMOVE(fl, nfslocklist); | |
954 | ||
955 | return (NFS_GRANTED); | |
956 | } | |
957 | ||
958 | enum split_status | |
959 | split_nfslock(exist_lock, unlock_lock, left_lock, right_lock) | |
960 | const struct file_lock *exist_lock, *unlock_lock; | |
961 | struct file_lock **left_lock, **right_lock; | |
962 | { | |
963 | u_int64_t start1, len1, start2, len2; | |
964 | enum split_status spstatus; | |
965 | ||
966 | spstatus = region_compare(exist_lock->client.l_offset, exist_lock->client.l_len, | |
967 | unlock_lock->client.l_offset, unlock_lock->client.l_len, | |
968 | &start1, &len1, &start2, &len2); | |
969 | ||
970 | if ((spstatus & SPL_LOCK1) != 0) { | |
ccaf7288 | 971 | *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle, exist_lock->addr, exist_lock->client_name); |
ac2f15b3 A |
972 | if (*left_lock == NULL) { |
973 | debuglog("Unable to allocate resource for split 1\n"); | |
974 | return SPL_RESERR; | |
975 | } | |
976 | ||
977 | fill_file_lock(*left_lock, | |
ac2f15b3 A |
978 | exist_lock->client.exclusive, exist_lock->client.svid, |
979 | start1, len1, | |
ccaf7288 | 980 | exist_lock->nsm_status, |
ac2f15b3 A |
981 | exist_lock->status, exist_lock->flags, exist_lock->blocking); |
982 | } | |
983 | ||
984 | if ((spstatus & SPL_LOCK2) != 0) { | |
ccaf7288 | 985 | *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle, exist_lock->addr, exist_lock->client_name); |
ac2f15b3 A |
986 | if (*right_lock == NULL) { |
987 | debuglog("Unable to allocate resource for split 1\n"); | |
988 | if (*left_lock != NULL) { | |
989 | deallocate_file_lock(*left_lock); | |
990 | } | |
991 | return SPL_RESERR; | |
992 | } | |
993 | ||
994 | fill_file_lock(*right_lock, | |
ac2f15b3 A |
995 | exist_lock->client.exclusive, exist_lock->client.svid, |
996 | start2, len2, | |
ccaf7288 | 997 | exist_lock->nsm_status, |
ac2f15b3 A |
998 | exist_lock->status, exist_lock->flags, exist_lock->blocking); |
999 | } | |
1000 | ||
1001 | return spstatus; | |
1002 | } | |
1003 | ||
1004 | enum nfslock_status | |
1005 | unlock_nfslock(fl, released_lock, left_lock, right_lock) | |
1006 | const struct file_lock *fl; | |
1007 | struct file_lock **released_lock; | |
1008 | struct file_lock **left_lock; | |
1009 | struct file_lock **right_lock; | |
1010 | { | |
1011 | struct file_lock *mfl; /* Matching file lock */ | |
1012 | enum nfslock_status retval; | |
1013 | enum split_status spstatus; | |
1014 | ||
1015 | debuglog("Entering unlock_nfslock\n"); | |
1016 | ||
1017 | *released_lock = NULL; | |
1018 | *left_lock = NULL; | |
1019 | *right_lock = NULL; | |
1020 | ||
1021 | retval = NFS_DENIED_NOLOCK; | |
1022 | ||
ccaf7288 | 1023 | debuglog("Attempting to match lock...\n"); |
ac2f15b3 A |
1024 | mfl = get_lock_matching_unlock(fl); |
1025 | ||
1026 | if (mfl != NULL) { | |
1027 | debuglog("Unlock matched. Querying for split\n"); | |
1028 | ||
1029 | spstatus = split_nfslock(mfl, fl, left_lock, right_lock); | |
1030 | ||
1031 | debuglog("Split returned %d %p %p %p %p\n",spstatus,mfl,fl,*left_lock,*right_lock); | |
1032 | debuglog("********Split dumps********"); | |
1033 | dump_filelock(mfl); | |
1034 | dump_filelock(fl); | |
1035 | dump_filelock(*left_lock); | |
1036 | dump_filelock(*right_lock); | |
1037 | debuglog("********End Split dumps********"); | |
1038 | ||
1039 | if (spstatus == SPL_RESERR) { | |
1040 | if (*left_lock != NULL) { | |
1041 | deallocate_file_lock(*left_lock); | |
1042 | *left_lock = NULL; | |
1043 | } | |
1044 | ||
1045 | if (*right_lock != NULL) { | |
1046 | deallocate_file_lock(*right_lock); | |
1047 | *right_lock = NULL; | |
1048 | } | |
1049 | ||
1050 | return NFS_RESERR; | |
1051 | } | |
1052 | ||
1053 | /* Insert new locks from split if required */ | |
1054 | if (*left_lock != NULL) { | |
1055 | debuglog("Split left activated\n"); | |
1056 | LIST_INSERT_HEAD(&nfslocklist_head, *left_lock, nfslocklist); | |
1057 | } | |
1058 | ||
1059 | if (*right_lock != NULL) { | |
1060 | debuglog("Split right activated\n"); | |
1061 | LIST_INSERT_HEAD(&nfslocklist_head, *right_lock, nfslocklist); | |
1062 | } | |
1063 | ||
1064 | /* Unlock the lock since it matches identity */ | |
1065 | LIST_REMOVE(mfl, nfslocklist); | |
1066 | *released_lock = mfl; | |
1067 | retval = NFS_GRANTED; | |
1068 | } | |
1069 | ||
1070 | debuglog("Exiting unlock_nfslock\n"); | |
1071 | ||
1072 | return retval; | |
1073 | } | |
1074 | ||
1075 | /* | |
1076 | * Below here are the routines for manipulating the file lock directly | |
1077 | * on the disk hardware itself | |
1078 | */ | |
1079 | enum hwlock_status | |
1080 | lock_hwlock(struct file_lock *fl) | |
1081 | { | |
1082 | struct monfile *imf,*nmf; | |
1083 | int lflags, flerror; | |
2b484d24 | 1084 | fhandle_t fh; |
ac2f15b3 A |
1085 | |
1086 | /* Scan to see if filehandle already present */ | |
1087 | LIST_FOREACH(imf, &monfilelist_head, monfilelist) { | |
1088 | if ((fl->filehandle.n_len == imf->filehandle.n_len) && | |
1089 | (bcmp(fl->filehandle.n_bytes, imf->filehandle.n_bytes, | |
1090 | fl->filehandle.n_len) == 0)) { | |
1091 | /* imf is the correct filehandle */ | |
1092 | break; | |
1093 | } | |
1094 | } | |
1095 | ||
1096 | /* | |
1097 | * Filehandle already exists (we control the file) | |
1098 | * *AND* NFS has already cleared the lock for availability | |
1099 | * Grant it and bump the refcount. | |
1100 | */ | |
1101 | if (imf != NULL) { | |
1102 | ++(imf->refcount); | |
1103 | return (HW_GRANTED); | |
1104 | } | |
1105 | ||
1106 | /* No filehandle found, create and go */ | |
1107 | nmf = malloc(sizeof(struct monfile)); | |
1108 | if (nmf == NULL) { | |
1109 | debuglog("hwlock resource allocation failure\n"); | |
1110 | return (HW_RESERR); | |
1111 | } | |
1112 | nmf->filehandle.n_bytes = malloc(fl->filehandle.n_len); | |
ccaf7288 | 1113 | if (nmf->filehandle.n_bytes == NULL) { |
ac2f15b3 A |
1114 | debuglog("hwlock resource allocation failure\n"); |
1115 | free(nmf); | |
1116 | return (HW_RESERR); | |
1117 | } | |
1118 | ||
2b484d24 A |
1119 | if (fl->filehandle.n_len > NFS_MAX_FH_SIZE) { |
1120 | debuglog("hwlock: bad fh length %d (from %16s): %32s\n", | |
1121 | fl->filehandle.n_len, fl->client_name, strerror(errno)); | |
1122 | free(nmf->filehandle.n_bytes); | |
1123 | free(nmf); | |
1124 | return (HW_STALEFH); | |
1125 | } | |
1126 | fh.fh_len = fl->filehandle.n_len; | |
1127 | bcopy(fl->filehandle.n_bytes, fh.fh_data, fh.fh_len); | |
1128 | ||
ac2f15b3 | 1129 | /* XXX: Is O_RDWR always the correct mode? */ |
2b484d24 | 1130 | nmf->fd = fhopen(&fh, O_RDWR); |
ac2f15b3 A |
1131 | if (nmf->fd < 0) { |
1132 | debuglog("fhopen failed (from %16s): %32s\n", | |
1133 | fl->client_name, strerror(errno)); | |
ccaf7288 | 1134 | free(nmf->filehandle.n_bytes); |
ac2f15b3 A |
1135 | free(nmf); |
1136 | switch (errno) { | |
1137 | case ESTALE: | |
1138 | return (HW_STALEFH); | |
1139 | case EROFS: | |
1140 | return (HW_READONLY); | |
1141 | default: | |
1142 | return (HW_RESERR); | |
1143 | } | |
1144 | } | |
1145 | ||
1146 | /* File opened correctly, fill the monitor struct */ | |
1147 | nmf->filehandle.n_len = fl->filehandle.n_len; | |
1148 | bcopy(fl->filehandle.n_bytes, nmf->filehandle.n_bytes, fl->filehandle.n_len); | |
1149 | nmf->refcount = 1; | |
1150 | nmf->exclusive = fl->client.exclusive; | |
1151 | ||
1152 | lflags = (nmf->exclusive == 1) ? | |
1153 | (LOCK_EX | LOCK_NB) : (LOCK_SH | LOCK_NB); | |
1154 | ||
1155 | flerror = flock(nmf->fd, lflags); | |
1156 | ||
1157 | if (flerror != 0) { | |
1158 | debuglog("flock failed (from %16s): %32s\n", | |
1159 | fl->client_name, strerror(errno)); | |
1160 | close(nmf->fd); | |
ccaf7288 | 1161 | free(nmf->filehandle.n_bytes); |
ac2f15b3 A |
1162 | free(nmf); |
1163 | switch (errno) { | |
1164 | case EAGAIN: | |
1165 | return (HW_DENIED); | |
1166 | case ESTALE: | |
1167 | return (HW_STALEFH); | |
1168 | case EROFS: | |
1169 | return (HW_READONLY); | |
1170 | default: | |
1171 | return (HW_RESERR); | |
1172 | break; | |
1173 | } | |
1174 | } | |
1175 | ||
1176 | /* File opened and locked */ | |
1177 | LIST_INSERT_HEAD(&monfilelist_head, nmf, monfilelist); | |
1178 | ||
1179 | debuglog("flock succeeded (from %16s)\n", fl->client_name); | |
1180 | return (HW_GRANTED); | |
1181 | } | |
1182 | ||
1183 | enum hwlock_status | |
1184 | unlock_hwlock(const struct file_lock *fl) | |
1185 | { | |
1186 | struct monfile *imf; | |
1187 | ||
1188 | debuglog("Entering unlock_hwlock\n"); | |
1189 | debuglog("Entering loop interation\n"); | |
1190 | ||
1191 | /* Scan to see if filehandle already present */ | |
1192 | LIST_FOREACH(imf, &monfilelist_head, monfilelist) { | |
1193 | if ((fl->filehandle.n_len == imf->filehandle.n_len) && | |
1194 | (bcmp(fl->filehandle.n_bytes, imf->filehandle.n_bytes, | |
1195 | fl->filehandle.n_len) == 0)) { | |
1196 | /* imf is the correct filehandle */ | |
1197 | break; | |
1198 | } | |
1199 | } | |
1200 | ||
1201 | debuglog("Completed iteration. Proceeding\n"); | |
1202 | ||
1203 | if (imf == NULL) { | |
1204 | /* No lock found */ | |
1205 | debuglog("Exiting unlock_hwlock (HW_DENIED_NOLOCK)\n"); | |
1206 | return (HW_DENIED_NOLOCK); | |
1207 | } | |
1208 | ||
1209 | /* Lock found */ | |
1210 | --imf->refcount; | |
1211 | ||
1212 | if (imf->refcount < 0) { | |
1213 | debuglog("Negative hardware reference count\n"); | |
1214 | } | |
1215 | ||
1216 | if (imf->refcount <= 0) { | |
1217 | close(imf->fd); | |
1218 | LIST_REMOVE(imf, monfilelist); | |
ccaf7288 | 1219 | free(imf->filehandle.n_bytes); |
ac2f15b3 A |
1220 | free(imf); |
1221 | } | |
1222 | debuglog("Exiting unlock_hwlock (HW_GRANTED)\n"); | |
1223 | return (HW_GRANTED); | |
1224 | } | |
1225 | ||
1226 | enum hwlock_status | |
1227 | test_hwlock(fl, conflicting_fl) | |
1228 | const struct file_lock *fl __unused; | |
1229 | struct file_lock **conflicting_fl __unused; | |
1230 | { | |
1231 | ||
1232 | /* | |
1233 | * XXX: lock tests on hardware are not required until | |
1234 | * true partial file testing is done on the underlying file | |
1235 | */ | |
1236 | return (HW_RESERR); | |
1237 | } | |
1238 | ||
1239 | ||
1240 | ||
1241 | /* | |
1242 | * Below here are routines for manipulating blocked lock requests | |
1243 | * They should only be called from the XXX_partialfilelock routines | |
1244 | * if at all possible | |
1245 | */ | |
1246 | ||
1247 | void | |
1248 | add_blockingfilelock(struct file_lock *fl) | |
1249 | { | |
7902cf7e | 1250 | struct file_lock *ifl, *nfl; |
ac2f15b3 A |
1251 | |
1252 | debuglog("Entering add_blockingfilelock\n"); | |
1253 | ||
7902cf7e A |
1254 | /* |
1255 | * Check for a duplicate lock request. | |
1256 | * If found, deallocate the older request. | |
1257 | */ | |
1258 | ifl = LIST_FIRST(&blockedlocklist_head); | |
1259 | for (; ifl != NULL; ifl = nfl) { | |
1260 | debuglog("Pointer to file lock: %p\n",ifl); | |
1261 | debuglog("****Dump of ifl****\n"); | |
1262 | dump_filelock(ifl); | |
1263 | debuglog("*******************\n"); | |
1264 | ||
1265 | nfl = LIST_NEXT(ifl, nfslocklist); | |
1266 | ||
1267 | if (fl->filehandle.n_len != ifl->filehandle.n_len) | |
1268 | continue; | |
1269 | if (bcmp(fl->filehandle.n_bytes, ifl->filehandle.n_bytes, | |
1270 | fl->filehandle.n_len)) | |
1271 | continue; | |
1272 | ||
1273 | /* Filehandles match, check region */ | |
1274 | if ((fl->client.l_offset != ifl->client.l_offset) || | |
1275 | (fl->client.l_len != ifl->client.l_len)) | |
1276 | continue; | |
1277 | ||
1278 | /* Regions match, check the identity */ | |
1279 | if (!same_filelock_identity(fl,ifl)) | |
1280 | continue; | |
1281 | ||
1282 | debuglog("add_blockingfilelock: removing duplicate lock request.\n"); | |
1283 | remove_blockingfilelock(ifl); | |
1284 | deallocate_file_lock(ifl); | |
1285 | break; | |
1286 | } | |
1287 | ||
ac2f15b3 A |
1288 | /* |
1289 | * Clear the blocking flag so that it can be reused without | |
1290 | * adding it to the blocking queue a second time | |
1291 | */ | |
1292 | ||
1293 | fl->blocking = 0; | |
1294 | LIST_INSERT_HEAD(&blockedlocklist_head, fl, nfslocklist); | |
1295 | ||
1296 | debuglog("Exiting add_blockingfilelock\n"); | |
1297 | } | |
1298 | ||
1299 | void | |
1300 | remove_blockingfilelock(struct file_lock *fl) | |
1301 | { | |
1302 | ||
1303 | debuglog("Entering remove_blockingfilelock\n"); | |
1304 | ||
1305 | LIST_REMOVE(fl, nfslocklist); | |
1306 | ||
1307 | debuglog("Exiting remove_blockingfilelock\n"); | |
1308 | } | |
1309 | ||
1310 | void | |
1311 | clear_blockingfilelock(const char *hostname) | |
1312 | { | |
1313 | struct file_lock *ifl,*nfl; | |
1314 | ||
1315 | /* | |
1316 | * Normally, LIST_FOREACH is called for, but since | |
1317 | * the current element *is* the iterator, deleting it | |
1318 | * would mess up the iteration. Thus, a next element | |
1319 | * must be used explicitly | |
1320 | */ | |
1321 | ||
1322 | ifl = LIST_FIRST(&blockedlocklist_head); | |
1323 | ||
1324 | while (ifl != NULL) { | |
1325 | nfl = LIST_NEXT(ifl, nfslocklist); | |
1326 | ||
1327 | if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) { | |
1328 | remove_blockingfilelock(ifl); | |
1329 | deallocate_file_lock(ifl); | |
1330 | } | |
1331 | ||
1332 | ifl = nfl; | |
1333 | } | |
1334 | } | |
1335 | ||
7902cf7e A |
1336 | int need_retry_blocked_locks = 0; /* need to call retry_blockingfilelocklist() */ |
1337 | ||
ac2f15b3 | 1338 | void |
7902cf7e | 1339 | retry_blockingfilelocklist(netobj *fh) |
ac2f15b3 | 1340 | { |
7902cf7e A |
1341 | /* |
1342 | * If fh is given, then retry just the locks with the | |
1343 | * same filehandle in the blocked list. | |
1344 | * Otherwise, simply retry all locks in the blocked list. | |
1345 | */ | |
ac2f15b3 A |
1346 | struct file_lock *ifl, *nfl, *pfl; /* Iterator */ |
1347 | enum partialfilelock_status pflstatus; | |
7902cf7e | 1348 | int rv; |
ac2f15b3 A |
1349 | |
1350 | debuglog("Entering retry_blockingfilelocklist\n"); | |
1351 | ||
7902cf7e A |
1352 | need_retry_blocked_locks = 0; |
1353 | ||
ac2f15b3 A |
1354 | pfl = NULL; |
1355 | ifl = LIST_FIRST(&blockedlocklist_head); | |
1356 | debuglog("Iterator choice %p\n",ifl); | |
1357 | ||
1358 | while (ifl != NULL) { | |
1359 | /* | |
1360 | * SUBTLE BUG: The next element must be worked out before the | |
1361 | * current element has been moved | |
1362 | */ | |
1363 | nfl = LIST_NEXT(ifl, nfslocklist); | |
1364 | debuglog("Iterator choice %p\n",ifl); | |
1365 | debuglog("Prev iterator choice %p\n",pfl); | |
1366 | debuglog("Next iterator choice %p\n",nfl); | |
1367 | ||
7902cf7e A |
1368 | /* if given a filehandle, only retry locks for the same filehandle */ |
1369 | if (fh && !same_netobj(fh, &ifl->filehandle)) { | |
1370 | ifl = nfl; | |
1371 | continue; | |
1372 | } | |
1373 | ||
ac2f15b3 A |
1374 | /* |
1375 | * SUBTLE BUG: The file_lock must be removed from the | |
1376 | * old list so that it's list pointers get disconnected | |
1377 | * before being allowed to participate in the new list | |
1378 | * which will automatically add it in if necessary. | |
1379 | */ | |
1380 | ||
1381 | LIST_REMOVE(ifl, nfslocklist); | |
1382 | pflstatus = lock_partialfilelock(ifl); | |
1383 | ||
1384 | if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE) { | |
1385 | debuglog("Granted blocked lock\n"); | |
1386 | /* lock granted and is now being used */ | |
7902cf7e A |
1387 | rv = send_granted(ifl, 0); |
1388 | if (rv) { | |
1389 | /* | |
1390 | * Uh oh... the NLM_GRANTED message failed. | |
1391 | * About the only thing we can do is drop the lock. | |
1392 | * Note: this could be bad if the error was only | |
1393 | * transient. Hopefully, if the client is still | |
1394 | * waiting for the lock, they will resend the request. | |
1395 | */ | |
1396 | do_unlock(ifl); | |
1397 | /* ifl is NO LONGER VALID AT THIS POINT */ | |
1398 | } | |
2b484d24 A |
1399 | } else if (pflstatus == PFL_HWDENIED_STALEFH) { |
1400 | /* | |
1401 | * Uh oh... | |
1402 | * It would be nice if we could inform the client of | |
1403 | * this error. Unfortunately, there's no way to do | |
1404 | * that in the NLM protocol (can't send "granted" | |
1405 | * message with an error and there's no "never going | |
1406 | * to be granted" message). | |
1407 | * | |
1408 | * Since there's no chance of this blocked request ever | |
1409 | * succeeding, we drop the lock request rather than | |
1410 | * needlessly keeping it around just to rot forever in | |
1411 | * the blocked lock list. | |
1412 | * | |
1413 | * Hopefully, if the client is still waiting for the lock, | |
1414 | * they will resend the request (and get an error then). | |
1415 | * | |
1416 | * XXX Note: PFL_HWDENIED_READONLY could potentially | |
1417 | * be handled this way as well, although that would | |
1418 | * only be an issue if a file system changed from | |
1419 | * read-write to read-only out from under a blocked | |
1420 | * lock request, and that's far less likely than a | |
1421 | * file disappearing out from under such a request. | |
1422 | */ | |
1423 | deallocate_file_lock(ifl); | |
1424 | /* ifl is NO LONGER VALID AT THIS POINT */ | |
ac2f15b3 A |
1425 | } else { |
1426 | /* Reinsert lock back into same place in blocked list */ | |
1427 | debuglog("Replacing blocked lock\n"); | |
1428 | if (pfl != NULL) | |
1429 | LIST_INSERT_AFTER(pfl, ifl, nfslocklist); | |
1430 | else | |
1431 | /* ifl is the only elem. in the list */ | |
1432 | LIST_INSERT_HEAD(&blockedlocklist_head, ifl, nfslocklist); | |
1433 | } | |
1434 | ||
2b484d24 A |
1435 | if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE || |
1436 | pflstatus == PFL_HWDENIED_STALEFH) { | |
1437 | /* If ifl was permanently removed from the list, (e.g it */ | |
1438 | /* was granted or dropped), pfl should remain where it's at. */ | |
7902cf7e A |
1439 | } else { |
1440 | /* If ifl was left in the list, (e.g it was reinserted back */ | |
1441 | /* in place), pfl should simply be moved forward to be ifl */ | |
1442 | pfl = ifl; | |
1443 | } | |
ac2f15b3 A |
1444 | /* Valid increment behavior regardless of state of ifl */ |
1445 | ifl = nfl; | |
ac2f15b3 A |
1446 | } |
1447 | ||
1448 | debuglog("Exiting retry_blockingfilelocklist\n"); | |
1449 | } | |
1450 | ||
1451 | /* | |
1452 | * Below here are routines associated with manipulating all | |
1453 | * aspects of the partial file locking system (list, hardware, etc.) | |
1454 | */ | |
1455 | ||
1456 | /* | |
1457 | * Please note that lock monitoring must be done at this level which | |
1458 | * keeps track of *individual* lock requests on lock and unlock | |
1459 | * | |
1460 | * XXX: Split unlocking is going to make the unlock code miserable | |
1461 | */ | |
1462 | ||
1463 | /* | |
1464 | * lock_partialfilelock: | |
1465 | * | |
1466 | * Argument fl gets modified as its list housekeeping entries get modified | |
1467 | * upon insertion into the NFS lock list | |
1468 | * | |
1469 | * This routine makes several assumptions: | |
1470 | * 1) It (will) pass locks through to flock to lock the entire underlying file | |
1471 | * and then parcel out NFS locks if it gets control of the file. | |
1472 | * This matches the old rpc.lockd file semantics (except where it | |
1473 | * is now more correct). It is the safe solution, but will cause | |
1474 | * overly restrictive blocking if someone is trying to use the | |
1475 | * underlying files without using NFS. This appears to be an | |
1476 | * acceptable tradeoff since most people use standalone NFS servers. | |
1477 | * XXX: The right solution is probably kevent combined with fcntl | |
1478 | * | |
1479 | * 2) Nothing modifies the lock lists between testing and granting | |
1480 | * I have no idea whether this is a useful assumption or not | |
1481 | */ | |
1482 | ||
1483 | enum partialfilelock_status | |
1484 | lock_partialfilelock(struct file_lock *fl) | |
1485 | { | |
1486 | enum partialfilelock_status retval; | |
1487 | enum nfslock_status lnlstatus; | |
1488 | enum hwlock_status hwstatus; | |
1489 | ||
1490 | debuglog("Entering lock_partialfilelock\n"); | |
1491 | ||
1492 | retval = PFL_DENIED; | |
1493 | ||
1494 | /* | |
1495 | * Execute the NFS lock first, if possible, as it is significantly | |
1496 | * easier and less expensive to undo than the filesystem lock | |
1497 | */ | |
1498 | ||
1499 | lnlstatus = lock_nfslock(fl); | |
1500 | ||
1501 | switch (lnlstatus) { | |
1502 | case NFS_GRANTED: | |
1503 | case NFS_GRANTED_DUPLICATE: | |
1504 | /* | |
1505 | * At this point, the NFS lock is allocated and active. | |
1506 | * Remember to clean it up if the hardware lock fails | |
1507 | */ | |
1508 | hwstatus = lock_hwlock(fl); | |
1509 | ||
1510 | switch (hwstatus) { | |
1511 | case HW_GRANTED: | |
1512 | case HW_GRANTED_DUPLICATE: | |
1513 | debuglog("HW GRANTED\n"); | |
1514 | /* | |
1515 | * XXX: Fixme: Check hwstatus for duplicate when | |
1516 | * true partial file locking and accounting is | |
1517 | * done on the hardware | |
1518 | */ | |
1519 | if (lnlstatus == NFS_GRANTED_DUPLICATE) { | |
1520 | retval = PFL_GRANTED_DUPLICATE; | |
1521 | } else { | |
1522 | retval = PFL_GRANTED; | |
1523 | } | |
1524 | if (fl->flags & LOCK_MON) | |
6720d03d | 1525 | monitor_lock_host_by_name(fl->client_name, fl->addr); |
ac2f15b3 A |
1526 | break; |
1527 | case HW_RESERR: | |
1528 | debuglog("HW RESERR\n"); | |
1529 | retval = PFL_HWRESERR; | |
1530 | break; | |
1531 | case HW_DENIED: | |
1532 | debuglog("HW DENIED\n"); | |
1533 | retval = PFL_HWDENIED; | |
1534 | break; | |
2b484d24 A |
1535 | case HW_DENIED_NOLOCK: |
1536 | debuglog("HW DENIED NOLOCK\n"); | |
1537 | retval = PFL_HWDENIED_NOLOCK; | |
1538 | break; | |
1539 | case HW_STALEFH: | |
1540 | debuglog("HW STALE FH\n"); | |
1541 | retval = PFL_HWDENIED_STALEFH; | |
1542 | break; | |
1543 | case HW_READONLY: | |
1544 | debuglog("HW READONLY\n"); | |
1545 | retval = PFL_HWDENIED_READONLY; | |
1546 | break; | |
ac2f15b3 A |
1547 | default: |
1548 | debuglog("Unmatched hwstatus %d\n",hwstatus); | |
1549 | break; | |
1550 | } | |
1551 | ||
1552 | if (retval != PFL_GRANTED && | |
1553 | retval != PFL_GRANTED_DUPLICATE) { | |
1554 | /* Clean up the NFS lock */ | |
1555 | debuglog("Deleting trial NFS lock\n"); | |
1556 | delete_nfslock(fl); | |
1557 | } | |
1558 | break; | |
1559 | case NFS_DENIED: | |
1560 | retval = PFL_NFSDENIED; | |
1561 | break; | |
1562 | case NFS_RESERR: | |
1563 | retval = PFL_NFSRESERR; | |
1564 | default: | |
1565 | debuglog("Unmatched lnlstatus %d\n"); | |
1566 | retval = PFL_NFSDENIED_NOLOCK; | |
1567 | break; | |
1568 | } | |
1569 | ||
1570 | /* | |
1571 | * By the time fl reaches here, it is completely free again on | |
1572 | * failure. The NFS lock done before attempting the | |
1573 | * hardware lock has been backed out | |
1574 | */ | |
1575 | ||
1576 | if (retval == PFL_NFSDENIED || retval == PFL_HWDENIED) { | |
1577 | /* Once last chance to check the lock */ | |
1578 | if (fl->blocking == 1) { | |
ccaf7288 A |
1579 | if (retval == PFL_NFSDENIED) { |
1580 | /* Queue the lock */ | |
1581 | debuglog("BLOCKING LOCK RECEIVED\n"); | |
1582 | retval = PFL_NFSBLOCKED; | |
1583 | add_blockingfilelock(fl); | |
1584 | dump_filelock(fl); | |
1585 | } else { | |
1586 | /* retval is okay as PFL_HWDENIED */ | |
1587 | debuglog("BLOCKING LOCK DENIED IN HARDWARE\n"); | |
1588 | dump_filelock(fl); | |
1589 | } | |
ac2f15b3 A |
1590 | } else { |
1591 | /* Leave retval alone, it's already correct */ | |
1592 | debuglog("Lock denied. Non-blocking failure\n"); | |
1593 | dump_filelock(fl); | |
1594 | } | |
1595 | } | |
1596 | ||
1597 | debuglog("Exiting lock_partialfilelock\n"); | |
1598 | ||
1599 | return retval; | |
1600 | } | |
1601 | ||
1602 | /* | |
1603 | * unlock_partialfilelock: | |
1604 | * | |
1605 | * Given a file_lock, unlock all locks which match. | |
1606 | * | |
1607 | * Note that a given lock might have to unlock ITSELF! See | |
1608 | * clear_partialfilelock for example. | |
1609 | */ | |
1610 | ||
1611 | enum partialfilelock_status | |
1612 | unlock_partialfilelock(const struct file_lock *fl) | |
1613 | { | |
1614 | struct file_lock *lfl,*rfl,*releasedfl,*selffl; | |
1615 | enum partialfilelock_status retval; | |
1616 | enum nfslock_status unlstatus; | |
1617 | enum hwlock_status unlhwstatus, lhwstatus; | |
1618 | ||
1619 | debuglog("Entering unlock_partialfilelock\n"); | |
1620 | ||
1621 | selffl = NULL; | |
1622 | lfl = NULL; | |
1623 | rfl = NULL; | |
1624 | releasedfl = NULL; | |
1625 | retval = PFL_DENIED; | |
1626 | ||
1627 | /* | |
1628 | * There are significant overlap and atomicity issues | |
1629 | * with partially releasing a lock. For example, releasing | |
1630 | * part of an NFS shared lock does *not* always release the | |
1631 | * corresponding part of the file since there is only one | |
1632 | * rpc.lockd UID but multiple users could be requesting it | |
1633 | * from NFS. Also, an unlock request should never allow | |
1634 | * another process to gain a lock on the remaining parts. | |
1635 | * ie. Always apply the new locks before releasing the | |
1636 | * old one | |
1637 | */ | |
1638 | ||
1639 | /* | |
1640 | * Loop is required since multiple little locks | |
1641 | * can be allocated and then deallocated with one | |
1642 | * big unlock. | |
1643 | * | |
1644 | * The loop is required to be here so that the nfs & | |
1645 | * hw subsystems do not need to communicate with one | |
1646 | * one another | |
1647 | */ | |
1648 | ||
1649 | do { | |
1650 | debuglog("Value of releasedfl: %p\n",releasedfl); | |
1651 | /* lfl&rfl are created *AND* placed into the NFS lock list if required */ | |
1652 | unlstatus = unlock_nfslock(fl, &releasedfl, &lfl, &rfl); | |
1653 | debuglog("Value of releasedfl: %p\n",releasedfl); | |
1654 | ||
1655 | ||
1656 | /* XXX: This is grungy. It should be refactored to be cleaner */ | |
1657 | if (lfl != NULL) { | |
1658 | lhwstatus = lock_hwlock(lfl); | |
1659 | if (lhwstatus != HW_GRANTED && | |
1660 | lhwstatus != HW_GRANTED_DUPLICATE) { | |
1661 | debuglog("HW duplicate lock failure for left split\n"); | |
1662 | } | |
1663 | if (lfl->flags & LOCK_MON) | |
6720d03d | 1664 | monitor_lock_host_by_name(lfl->client_name, lfl->addr); |
ac2f15b3 A |
1665 | } |
1666 | ||
1667 | if (rfl != NULL) { | |
1668 | lhwstatus = lock_hwlock(rfl); | |
1669 | if (lhwstatus != HW_GRANTED && | |
1670 | lhwstatus != HW_GRANTED_DUPLICATE) { | |
1671 | debuglog("HW duplicate lock failure for right split\n"); | |
1672 | } | |
1673 | if (rfl->flags & LOCK_MON) | |
6720d03d | 1674 | monitor_lock_host_by_name(rfl->client_name, rfl->addr); |
ac2f15b3 A |
1675 | } |
1676 | ||
1677 | switch (unlstatus) { | |
1678 | case NFS_GRANTED: | |
1679 | /* Attempt to unlock on the hardware */ | |
1680 | debuglog("NFS unlock granted. Attempting hardware unlock\n"); | |
1681 | ||
1682 | /* This call *MUST NOT* unlock the two newly allocated locks */ | |
1683 | unlhwstatus = unlock_hwlock(fl); | |
1684 | debuglog("HW unlock returned with code %d\n",unlhwstatus); | |
1685 | ||
1686 | switch (unlhwstatus) { | |
1687 | case HW_GRANTED: | |
1688 | debuglog("HW unlock granted\n"); | |
1689 | if (releasedfl->flags & LOCK_MON) | |
1690 | unmonitor_lock_host(releasedfl->client_name); | |
1691 | retval = PFL_GRANTED; | |
1692 | break; | |
1693 | case HW_DENIED_NOLOCK: | |
1694 | /* Huh?!?! This shouldn't happen */ | |
1695 | debuglog("HW unlock denied no lock\n"); | |
1696 | retval = PFL_HWRESERR; | |
1697 | /* Break out of do-while */ | |
1698 | unlstatus = NFS_RESERR; | |
1699 | break; | |
1700 | default: | |
1701 | debuglog("HW unlock failed\n"); | |
1702 | retval = PFL_HWRESERR; | |
1703 | /* Break out of do-while */ | |
1704 | unlstatus = NFS_RESERR; | |
1705 | break; | |
1706 | } | |
1707 | ||
1708 | debuglog("Exiting with status retval: %d\n",retval); | |
1709 | ||
1710 | // XXX sending granted messages before unlock response | |
1711 | // XXX causes unlock response to be corrupted? | |
1712 | // XXX Workaround is to move this to nlm_prot_svc.c | |
1713 | // XXX after the unlock response is sent. | |
1714 | // retry_blockingfilelocklist(); | |
7902cf7e | 1715 | need_retry_blocked_locks = 1; |
ac2f15b3 A |
1716 | break; |
1717 | case NFS_DENIED_NOLOCK: | |
1718 | retval = PFL_GRANTED; | |
1719 | debuglog("All locks cleaned out\n"); | |
1720 | break; | |
1721 | default: | |
1722 | retval = PFL_NFSRESERR; | |
1723 | debuglog("NFS unlock failure\n"); | |
1724 | dump_filelock(fl); | |
1725 | break; | |
1726 | } | |
1727 | ||
1728 | if (releasedfl != NULL) { | |
1729 | if (fl == releasedfl) { | |
1730 | /* | |
1731 | * XXX: YECHHH!!! Attempt to unlock self succeeded | |
1732 | * but we can't deallocate the space yet. This is what | |
1733 | * happens when you don't write malloc and free together | |
1734 | */ | |
1735 | debuglog("Attempt to unlock self\n"); | |
1736 | selffl = releasedfl; | |
1737 | } else { | |
1738 | /* | |
1739 | * XXX: this deallocation *still* needs to migrate closer | |
1740 | * to the allocation code way up in get_lock or the allocation | |
1741 | * code needs to migrate down (violation of "When you write | |
1742 | * malloc you must write free") | |
1743 | */ | |
1744 | ||
1745 | deallocate_file_lock(releasedfl); | |
1746 | } | |
1747 | } | |
1748 | ||
1749 | } while (unlstatus == NFS_GRANTED); | |
1750 | ||
1751 | if (selffl != NULL) { | |
1752 | /* | |
1753 | * This statement wipes out the incoming file lock (fl) | |
1754 | * in spite of the fact that it is declared const | |
1755 | */ | |
1756 | debuglog("WARNING! Destroying incoming lock pointer\n"); | |
1757 | deallocate_file_lock(selffl); | |
1758 | } | |
1759 | ||
1760 | debuglog("Exiting unlock_partialfilelock\n"); | |
1761 | ||
1762 | return retval; | |
1763 | } | |
1764 | ||
1765 | /* | |
1766 | * clear_partialfilelock | |
1767 | * | |
1768 | * Normally called in response to statd state number change. | |
1769 | * Wipe out all locks held by a host. As a bonus, the act of | |
1770 | * doing so should automatically clear their statd entries and | |
1771 | * unmonitor the host. | |
1772 | */ | |
1773 | ||
1774 | void | |
1775 | clear_partialfilelock(const char *hostname) | |
1776 | { | |
1777 | struct file_lock *ifl, *nfl; | |
2b484d24 | 1778 | enum partialfilelock_status pfsret; |
6720d03d A |
1779 | struct host *ihp; |
1780 | ||
1781 | /* | |
1782 | * Check if the name we got from statd is | |
1783 | * actually one reverse-mapped from the client's | |
1784 | * address. If so, use the name provided as | |
1785 | * the caller_name in lock requests instead so that | |
1786 | * we can correctly identify the client's locks. | |
1787 | */ | |
1788 | TAILQ_FOREACH(ihp, &hostlst_head, hostlst) { | |
1789 | if (ihp->revname && strncmp(hostname, ihp->revname, | |
1790 | SM_MAXSTRLEN) == 0) { | |
1791 | hostname = ihp->name; | |
1792 | debuglog("Clearing locks for %s (%s)\n", | |
1793 | hostname, ihp->revname); | |
1794 | break; | |
1795 | } | |
1796 | } | |
ac2f15b3 A |
1797 | |
1798 | /* Clear blocking file lock list */ | |
1799 | clear_blockingfilelock(hostname); | |
1800 | ||
1801 | /* do all required unlocks */ | |
1802 | /* Note that unlock can smash the current pointer to a lock */ | |
1803 | ||
1804 | /* | |
1805 | * Normally, LIST_FOREACH is called for, but since | |
1806 | * the current element *is* the iterator, deleting it | |
1807 | * would mess up the iteration. Thus, a next element | |
1808 | * must be used explicitly | |
1809 | */ | |
2b484d24 | 1810 | restart: |
ac2f15b3 A |
1811 | ifl = LIST_FIRST(&nfslocklist_head); |
1812 | ||
1813 | while (ifl != NULL) { | |
1814 | nfl = LIST_NEXT(ifl, nfslocklist); | |
1815 | ||
1816 | if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) { | |
1817 | /* Unlock destroys ifl out from underneath */ | |
2b484d24 A |
1818 | pfsret = unlock_partialfilelock(ifl); |
1819 | if (pfsret != PFL_GRANTED) { | |
1820 | /* Uh oh... there was some sort of problem. */ | |
1821 | /* If we restart the loop, we may get */ | |
1822 | /* stuck here forever getting errors. */ | |
1823 | /* So, let's just abort the whole scan. */ | |
1824 | syslog(LOG_WARNING, "lock clearing for %s failed: %d", | |
1825 | hostname, pfsret); | |
1826 | break; | |
1827 | } | |
ac2f15b3 | 1828 | /* ifl is NO LONGER VALID AT THIS POINT */ |
2b484d24 A |
1829 | /* Note: the unlock may deallocate several existing locks. */ |
1830 | /* Therefore, we need to restart the scanning of the list, */ | |
1831 | /* because nfl could be pointing to a freed lock. */ | |
1832 | goto restart; | |
ac2f15b3 A |
1833 | } |
1834 | ifl = nfl; | |
1835 | } | |
1836 | } | |
1837 | ||
1838 | /* | |
1839 | * test_partialfilelock: | |
1840 | */ | |
1841 | enum partialfilelock_status | |
1842 | test_partialfilelock(const struct file_lock *fl, | |
1843 | struct file_lock **conflicting_fl) | |
1844 | { | |
1845 | enum partialfilelock_status retval; | |
1846 | enum nfslock_status teststatus; | |
1847 | ||
1848 | debuglog("Entering testpartialfilelock...\n"); | |
1849 | ||
1850 | retval = PFL_DENIED; | |
1851 | ||
1852 | teststatus = test_nfslock(fl, conflicting_fl); | |
1853 | debuglog("test_partialfilelock: teststatus %d\n",teststatus); | |
1854 | ||
1855 | if (teststatus == NFS_GRANTED || teststatus == NFS_GRANTED_DUPLICATE) { | |
1856 | /* XXX: Add the underlying filesystem locking code */ | |
1857 | retval = (teststatus == NFS_GRANTED) ? | |
1858 | PFL_GRANTED : PFL_GRANTED_DUPLICATE; | |
1859 | debuglog("Dumping locks...\n"); | |
1860 | dump_filelock(fl); | |
1861 | dump_filelock(*conflicting_fl); | |
1862 | debuglog("Done dumping locks...\n"); | |
1863 | } else { | |
1864 | retval = PFL_NFSDENIED; | |
1865 | debuglog("NFS test denied.\n"); | |
1866 | dump_filelock(fl); | |
1867 | debuglog("Conflicting.\n"); | |
1868 | dump_filelock(*conflicting_fl); | |
1869 | } | |
1870 | ||
1871 | debuglog("Exiting testpartialfilelock...\n"); | |
1872 | ||
1873 | return retval; | |
1874 | } | |
1875 | ||
1876 | /* | |
1877 | * Below here are routines associated with translating the partial file locking | |
1878 | * codes into useful codes to send back to the NFS RPC messaging system | |
1879 | */ | |
1880 | ||
1881 | /* | |
1882 | * These routines translate the (relatively) useful return codes back onto | |
1883 | * the few return codes which the nlm subsystems wishes to trasmit | |
1884 | */ | |
1885 | ||
2b484d24 | 1886 | enum nlm4_stats |
ac2f15b3 A |
1887 | do_test(struct file_lock *fl, struct file_lock **conflicting_fl) |
1888 | { | |
1889 | enum partialfilelock_status pfsret; | |
2b484d24 | 1890 | enum nlm4_stats retval; |
ac2f15b3 A |
1891 | |
1892 | debuglog("Entering do_test...\n"); | |
1893 | ||
1894 | pfsret = test_partialfilelock(fl,conflicting_fl); | |
1895 | ||
1896 | switch (pfsret) { | |
1897 | case PFL_GRANTED: | |
1898 | debuglog("PFL test lock granted\n"); | |
1899 | dump_filelock(fl); | |
1900 | dump_filelock(*conflicting_fl); | |
1901 | retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; | |
1902 | break; | |
1903 | case PFL_GRANTED_DUPLICATE: | |
1904 | debuglog("PFL test lock granted--duplicate id detected\n"); | |
1905 | dump_filelock(fl); | |
1906 | dump_filelock(*conflicting_fl); | |
1907 | debuglog("Clearing conflicting_fl for call semantics\n"); | |
1908 | *conflicting_fl = NULL; | |
1909 | retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; | |
1910 | break; | |
1911 | case PFL_NFSDENIED: | |
1912 | case PFL_HWDENIED: | |
1913 | debuglog("PFL test lock denied\n"); | |
1914 | dump_filelock(fl); | |
1915 | dump_filelock(*conflicting_fl); | |
1916 | retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; | |
1917 | break; | |
1918 | case PFL_NFSRESERR: | |
1919 | case PFL_HWRESERR: | |
1920 | debuglog("PFL test lock resource fail\n"); | |
1921 | dump_filelock(fl); | |
1922 | dump_filelock(*conflicting_fl); | |
1923 | retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; | |
1924 | break; | |
1925 | default: | |
1926 | debuglog("PFL test lock *FAILED*\n"); | |
1927 | dump_filelock(fl); | |
1928 | dump_filelock(*conflicting_fl); | |
1929 | retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; | |
1930 | break; | |
1931 | } | |
1932 | ||
1933 | debuglog("Exiting do_test...\n"); | |
1934 | ||
1935 | return retval; | |
1936 | } | |
1937 | ||
1938 | /* | |
1939 | * do_lock: Try to acquire a lock | |
1940 | * | |
1941 | * This routine makes a distinction between NLM versions. I am pretty | |
1942 | * convinced that this should be abstracted out and bounced up a level | |
1943 | */ | |
1944 | ||
2b484d24 | 1945 | enum nlm4_stats |
ac2f15b3 A |
1946 | do_lock(struct file_lock *fl) |
1947 | { | |
1948 | enum partialfilelock_status pfsret; | |
2b484d24 | 1949 | enum nlm4_stats retval; |
ac2f15b3 A |
1950 | |
1951 | debuglog("Entering do_lock...\n"); | |
1952 | ||
1953 | pfsret = lock_partialfilelock(fl); | |
1954 | ||
1955 | switch (pfsret) { | |
1956 | case PFL_GRANTED: | |
1957 | debuglog("PFL lock granted"); | |
1958 | dump_filelock(fl); | |
1959 | retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; | |
1960 | break; | |
1961 | case PFL_GRANTED_DUPLICATE: | |
1962 | debuglog("PFL lock granted--duplicate id detected"); | |
1963 | dump_filelock(fl); | |
1964 | retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; | |
1965 | break; | |
1966 | case PFL_NFSDENIED: | |
1967 | case PFL_HWDENIED: | |
1968 | debuglog("PFL_NFS lock denied"); | |
1969 | dump_filelock(fl); | |
1970 | retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; | |
1971 | break; | |
1972 | case PFL_NFSBLOCKED: | |
1973 | case PFL_HWBLOCKED: | |
1974 | debuglog("PFL_NFS blocking lock denied. Queued.\n"); | |
1975 | dump_filelock(fl); | |
1976 | retval = (fl->flags & LOCK_V4) ? nlm4_blocked : nlm_blocked; | |
1977 | break; | |
1978 | case PFL_NFSRESERR: | |
1979 | case PFL_HWRESERR: | |
2b484d24 A |
1980 | case PFL_NFSDENIED_NOLOCK: |
1981 | case PFL_HWDENIED_NOLOCK: | |
ac2f15b3 A |
1982 | debuglog("PFL lock resource alocation fail\n"); |
1983 | dump_filelock(fl); | |
1984 | retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; | |
1985 | break; | |
2b484d24 A |
1986 | case PFL_HWDENIED_STALEFH: |
1987 | debuglog("PFL_NFS lock denied STALEFH"); | |
1988 | dump_filelock(fl); | |
1989 | retval = (fl->flags & LOCK_V4) ? nlm4_stale_fh : nlm_denied; | |
1990 | break; | |
1991 | case PFL_HWDENIED_READONLY: | |
1992 | debuglog("PFL_NFS lock denied READONLY"); | |
1993 | dump_filelock(fl); | |
1994 | retval = (fl->flags & LOCK_V4) ? nlm4_rofs : nlm_denied; | |
1995 | break; | |
ac2f15b3 A |
1996 | default: |
1997 | debuglog("PFL lock *FAILED*"); | |
1998 | dump_filelock(fl); | |
1999 | retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; | |
2000 | break; | |
2001 | } | |
2002 | ||
2003 | debuglog("Exiting do_lock...\n"); | |
2004 | ||
2005 | return retval; | |
2006 | } | |
2007 | ||
2b484d24 | 2008 | enum nlm4_stats |
ac2f15b3 A |
2009 | do_unlock(struct file_lock *fl) |
2010 | { | |
2011 | enum partialfilelock_status pfsret; | |
2b484d24 | 2012 | enum nlm4_stats retval; |
ac2f15b3 A |
2013 | |
2014 | debuglog("Entering do_unlock...\n"); | |
2015 | pfsret = unlock_partialfilelock(fl); | |
2016 | ||
2017 | switch (pfsret) { | |
2018 | case PFL_GRANTED: | |
2019 | debuglog("PFL unlock granted"); | |
2020 | dump_filelock(fl); | |
2021 | retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; | |
2022 | break; | |
2023 | case PFL_NFSDENIED: | |
2024 | case PFL_HWDENIED: | |
2025 | debuglog("PFL_NFS unlock denied"); | |
2026 | dump_filelock(fl); | |
2027 | retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied; | |
2028 | break; | |
2029 | case PFL_NFSDENIED_NOLOCK: | |
2030 | case PFL_HWDENIED_NOLOCK: | |
2031 | debuglog("PFL_NFS no lock found\n"); | |
2032 | retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; | |
2033 | break; | |
2034 | case PFL_NFSRESERR: | |
2035 | case PFL_HWRESERR: | |
2036 | debuglog("PFL unlock resource failure"); | |
2037 | dump_filelock(fl); | |
2038 | retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; | |
2039 | break; | |
2040 | default: | |
2041 | debuglog("PFL unlock *FAILED*"); | |
2042 | dump_filelock(fl); | |
2043 | retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied; | |
2044 | break; | |
2045 | } | |
2046 | ||
2047 | debuglog("Exiting do_unlock...\n"); | |
2048 | ||
2049 | return retval; | |
2050 | } | |
2051 | ||
2052 | /* | |
2053 | * do_clear | |
2054 | * | |
2055 | * This routine is non-existent because it doesn't have a return code. | |
2056 | * It is here for completeness in case someone *does* need to do return | |
2057 | * codes later. A decent compiler should optimize this away. | |
2058 | */ | |
2059 | ||
2060 | void | |
2061 | do_clear(const char *hostname) | |
2062 | { | |
2063 | ||
2064 | clear_partialfilelock(hostname); | |
2065 | } | |
2066 | ||
2067 | /* | |
2068 | * The following routines are all called from the code which the | |
2069 | * RPC layer invokes | |
2070 | */ | |
2071 | ||
2072 | /* | |
2073 | * testlock(): inform the caller if the requested lock would be granted | |
2074 | * | |
2075 | * returns NULL if lock would granted | |
2076 | * returns pointer to a conflicting nlm4_holder if not | |
2077 | */ | |
2078 | ||
2079 | struct nlm4_holder * | |
2080 | testlock(struct nlm4_lock *lock, bool_t exclusive, int flags __unused) | |
2081 | { | |
2082 | struct file_lock test_fl, *conflicting_fl; | |
2083 | ||
2b484d24 A |
2084 | if (lock->fh.n_len > NFS_MAX_FH_SIZE) { |
2085 | debuglog("received fhandle size %d, max size %d", | |
2086 | lock->fh.n_len, NFS_MAX_FH_SIZE); | |
07f47057 A |
2087 | return NULL; |
2088 | } | |
2089 | ||
ac2f15b3 A |
2090 | bzero(&test_fl, sizeof(test_fl)); |
2091 | ||
2092 | test_fl.filehandle.n_len = lock->fh.n_len; | |
2093 | test_fl.filehandle.n_bytes = lock->fh.n_bytes; | |
2094 | copy_nlm4_lock_to_nlm4_holder(lock, exclusive, &test_fl.client); | |
2095 | ||
2096 | siglock(); | |
2097 | do_test(&test_fl, &conflicting_fl); | |
2098 | ||
2099 | if (conflicting_fl == NULL) { | |
2100 | debuglog("No conflicting lock found\n"); | |
2101 | sigunlock(); | |
2102 | return NULL; | |
2103 | } else { | |
2104 | debuglog("Found conflicting lock\n"); | |
2105 | dump_filelock(conflicting_fl); | |
2106 | sigunlock(); | |
2107 | return (&conflicting_fl->client); | |
2108 | } | |
2109 | } | |
2110 | ||
2111 | /* | |
2112 | * getlock: try to aquire the lock. | |
2113 | * If file is already locked and we can sleep, put the lock in the list with | |
2114 | * status LKST_WAITING; it'll be processed later. | |
2115 | * Otherwise try to lock. If we're allowed to block, fork a child which | |
2116 | * will do the blocking lock. | |
2117 | */ | |
2118 | ||
2b484d24 | 2119 | enum nlm4_stats |
ac2f15b3 A |
2120 | getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags) |
2121 | { | |
2122 | struct file_lock *newfl; | |
2b484d24 | 2123 | enum nlm4_stats retval; |
ac2f15b3 A |
2124 | |
2125 | debuglog("Entering getlock...\n"); | |
2126 | ||
2127 | if (grace_expired == 0 && lckarg->reclaim == 0) | |
2128 | return (flags & LOCK_V4) ? | |
2129 | nlm4_denied_grace_period : nlm_denied_grace_period; | |
2130 | ||
2b484d24 A |
2131 | if (lckarg->alock.fh.n_len > NFS_MAX_FH_SIZE) { |
2132 | debuglog("received fhandle size %d, max size %d", | |
2133 | lckarg->alock.fh.n_len, NFS_MAX_FH_SIZE); | |
07f47057 A |
2134 | return (flags & LOCK_V4) ? nlm4_failed : nlm_denied; |
2135 | } | |
2136 | ||
ac2f15b3 | 2137 | /* allocate new file_lock for this request */ |
ccaf7288 A |
2138 | newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->alock.fh, |
2139 | (struct sockaddr *)svc_getcaller(rqstp->rq_xprt), | |
2140 | lckarg->alock.caller_name); | |
ac2f15b3 A |
2141 | if (newfl == NULL) { |
2142 | syslog(LOG_NOTICE, "lock allocate failed: %s", strerror(errno)); | |
2143 | /* failed */ | |
2144 | return (flags & LOCK_V4) ? | |
2145 | nlm4_denied_nolocks : nlm_denied_nolocks; | |
2146 | } | |
2147 | ||
ac2f15b3 | 2148 | fill_file_lock(newfl, |
ac2f15b3 A |
2149 | lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset, |
2150 | lckarg->alock.l_len, | |
ccaf7288 | 2151 | lckarg->state, 0, flags, lckarg->block); |
ac2f15b3 A |
2152 | |
2153 | /* | |
2154 | * newfl is now fully constructed and deallocate_file_lock | |
2155 | * can now be used to delete it | |
2156 | */ | |
2157 | ||
2158 | siglock(); | |
2159 | debuglog("Pointer to new lock is %p\n",newfl); | |
2160 | ||
2161 | retval = do_lock(newfl); | |
2162 | ||
2163 | debuglog("Pointer to new lock is %p\n",newfl); | |
2164 | sigunlock(); | |
2165 | ||
2166 | switch (retval) | |
2167 | { | |
2168 | case nlm4_granted: | |
2169 | /* case nlm_granted: is the same as nlm4_granted */ | |
2170 | /* do_mon(lckarg->alock.caller_name); */ | |
2171 | break; | |
2172 | case nlm4_blocked: | |
2173 | /* case nlm_blocked: is the same as nlm4_blocked */ | |
2174 | /* do_mon(lckarg->alock.caller_name); */ | |
2175 | break; | |
2176 | default: | |
2177 | deallocate_file_lock(newfl); | |
2178 | break; | |
2179 | } | |
2180 | ||
2181 | debuglog("Exiting getlock...\n"); | |
2182 | ||
2183 | return retval; | |
2184 | } | |
2185 | ||
2186 | ||
2187 | /* unlock a filehandle */ | |
2b484d24 | 2188 | enum nlm4_stats |
07f47057 | 2189 | unlock(nlm4_lock *lock, const int flags) |
ac2f15b3 A |
2190 | { |
2191 | struct file_lock fl; | |
2b484d24 | 2192 | enum nlm4_stats err; |
ac2f15b3 | 2193 | |
ac2f15b3 | 2194 | debuglog("Entering unlock...\n"); |
07f47057 | 2195 | |
2b484d24 A |
2196 | if (lock->fh.n_len > NFS_MAX_FH_SIZE) { |
2197 | debuglog("received fhandle size %d, max size %d", | |
2198 | lock->fh.n_len, NFS_MAX_FH_SIZE); | |
07f47057 A |
2199 | return (flags & LOCK_V4) ? nlm4_failed : nlm_denied; |
2200 | } | |
2201 | ||
2202 | siglock(); | |
ac2f15b3 A |
2203 | |
2204 | bzero(&fl,sizeof(struct file_lock)); | |
2205 | fl.filehandle.n_len = lock->fh.n_len; | |
2206 | fl.filehandle.n_bytes = lock->fh.n_bytes; | |
2207 | ||
2208 | copy_nlm4_lock_to_nlm4_holder(lock, 0, &fl.client); | |
2209 | ||
2210 | err = do_unlock(&fl); | |
2211 | ||
2212 | sigunlock(); | |
2213 | ||
2214 | debuglog("Exiting unlock...\n"); | |
2215 | ||
2216 | return err; | |
2217 | } | |
2218 | ||
7902cf7e | 2219 | /* cancel a blocked lock request */ |
2b484d24 | 2220 | enum nlm4_stats |
07f47057 | 2221 | cancellock(nlm4_cancargs *args, const int flags) |
7902cf7e A |
2222 | { |
2223 | struct file_lock *ifl, *nfl; | |
2b484d24 | 2224 | enum nlm4_stats err; |
7902cf7e | 2225 | |
7902cf7e A |
2226 | debuglog("Entering cancellock...\n"); |
2227 | ||
2b484d24 A |
2228 | if (args->alock.fh.n_len > NFS_MAX_FH_SIZE) { |
2229 | debuglog("received fhandle size %d, max size %d", | |
2230 | args->alock.fh.n_len, NFS_MAX_FH_SIZE); | |
07f47057 A |
2231 | return (flags & LOCK_V4) ? nlm4_failed : nlm_denied; |
2232 | } | |
2233 | ||
2234 | siglock(); | |
2235 | ||
2b484d24 | 2236 | err = (flags & LOCK_V4) ? nlm4_denied : nlm_denied; |
7902cf7e A |
2237 | |
2238 | /* | |
2239 | * scan blocked lock list for matching request and remove/destroy | |
2240 | */ | |
2241 | ifl = LIST_FIRST(&blockedlocklist_head); | |
2242 | for ( ; ifl != NULL; ifl = nfl) { | |
2243 | nfl = LIST_NEXT(ifl, nfslocklist); | |
2244 | ||
2245 | /* compare lock fh - filehandle */ | |
2246 | if (!same_netobj(&args->alock.fh, &ifl->filehandle)) | |
2247 | continue; | |
2248 | ||
2249 | /* compare lock caller_name - client_name */ | |
2250 | if (strncmp(args->alock.caller_name, ifl->client_name, SM_MAXSTRLEN)) | |
2251 | continue; | |
2252 | ||
2253 | /* Note: done't compare cookie - client_cookie */ | |
2254 | /* The cookie may be specific to the cancel request */ | |
2255 | /* and not be the same as the one in the original lock request. */ | |
2256 | ||
2257 | /* compare lock oh - client.oh */ | |
2258 | if (!same_netobj(&args->alock.oh, &ifl->client.oh)) | |
2259 | continue; | |
2260 | ||
2261 | /* compare lock svid - client.svid */ | |
2262 | if (args->alock.svid != ifl->client.svid) | |
2263 | continue; | |
2264 | ||
2265 | /* compare lock l_offset - client.l_offset */ | |
2266 | if (args->alock.l_offset != ifl->client.l_offset) | |
2267 | continue; | |
2268 | ||
2269 | /* compare lock l_len - client.l_len */ | |
2270 | if (args->alock.l_len != ifl->client.l_len) | |
2271 | continue; | |
2272 | ||
2273 | /* compare exclusive - client.exclusive */ | |
2274 | if (args->exclusive != ifl->client.exclusive) | |
2275 | continue; | |
2276 | ||
2277 | /* got it */ | |
2278 | remove_blockingfilelock(ifl); | |
2279 | deallocate_file_lock(ifl); | |
2b484d24 | 2280 | err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
7902cf7e A |
2281 | break; |
2282 | } | |
2283 | ||
2284 | sigunlock(); | |
2285 | ||
2286 | debuglog("Exiting cancellock...\n"); | |
2287 | ||
2288 | return err; | |
2289 | } | |
2290 | ||
2291 | ||
ac2f15b3 A |
2292 | /* |
2293 | * XXX: The following monitor/unmonitor routines | |
2294 | * have not been extensively tested (ie. no regression | |
ccaf7288 | 2295 | * script exists like for the locking sections) |
ac2f15b3 A |
2296 | */ |
2297 | ||
7902cf7e A |
2298 | /* |
2299 | * Find a lock host on a queue. If found: | |
2300 | * bump the ref, | |
2301 | * bump the access time, | |
2302 | * dequeue it from the queue it was found on, | |
2303 | * enqueue it at the front of the "in use" queue. | |
2304 | */ | |
2305 | struct host * | |
ccaf7288 | 2306 | get_lock_host(struct hostlst_head *hd, const char *hostname, const struct sockaddr *saddr) |
7902cf7e A |
2307 | { |
2308 | struct host *ihp; | |
2309 | ||
ccaf7288 A |
2310 | if (!hostname && !saddr) |
2311 | return (NULL); | |
2312 | ||
2313 | debuglog("get_lock_host %s\n", hostname ? hostname : "addr"); | |
7902cf7e | 2314 | TAILQ_FOREACH(ihp, hd, hostlst) { |
ccaf7288 A |
2315 | if (hostname && (strncmp(hostname, ihp->name, SM_MAXSTRLEN) != 0)) |
2316 | continue; | |
2317 | if (saddr && addrcmp(saddr, &ihp->addr)) | |
2318 | continue; | |
2319 | TAILQ_REMOVE(hd, ihp, hostlst); | |
2320 | /* | |
2321 | * Host is already monitored, so just bump the | |
2322 | * reference count. But don't bump the reference | |
2323 | * count if we're adding additional client-side | |
2324 | * references. Client-side monitors are done by | |
2325 | * address, are never unmonitored, and should only | |
2326 | * take one refcount. Otherwise, repeated calls | |
2327 | * could cause the refcount to wrap. | |
2328 | */ | |
2329 | if (!saddr || !ihp->addr.sa_len) | |
7902cf7e | 2330 | ++ihp->refcnt; |
ccaf7288 A |
2331 | ihp->lastuse = currsec; |
2332 | /* Host should only be in the monitor list once */ | |
2333 | TAILQ_INSERT_HEAD(&hostlst_head, ihp, hostlst); | |
2334 | break; | |
7902cf7e A |
2335 | } |
2336 | debuglog("get_lock_host %s %s\n", | |
ccaf7288 | 2337 | ihp == NULL ? "did not find" : "found", hostname ? hostname : "addr"); |
7902cf7e A |
2338 | return (ihp); |
2339 | } | |
2340 | ||
ac2f15b3 A |
2341 | /* |
2342 | * monitor_lock_host: monitor lock hosts locally with a ref count and | |
2343 | * inform statd | |
2344 | */ | |
2345 | void | |
6720d03d | 2346 | monitor_lock_host_by_name(const char *hostname, const struct sockaddr *saddr) |
ac2f15b3 | 2347 | { |
ccaf7288 | 2348 | struct host *ihp; |
ac2f15b3 | 2349 | |
7902cf7e | 2350 | debuglog("monitor_lock_host: %s\n", hostname); |
ccaf7288 | 2351 | ihp = get_lock_host(&hostlst_head, hostname, NULL); |
7902cf7e | 2352 | if (ihp == NULL) |
ccaf7288 | 2353 | ihp = get_lock_host(&hostlst_unref, hostname, NULL); |
7902cf7e | 2354 | if (ihp != NULL) { |
6720d03d A |
2355 | if (ihp->revname) |
2356 | debuglog("Monitor_lock_host: %s - %s (cached)\n", | |
2357 | ihp->name, ihp->revname); | |
2358 | else | |
2359 | debuglog("Monitor_lock_host: %s (cached)\n", ihp->name); | |
7902cf7e | 2360 | return; |
ac2f15b3 A |
2361 | } |
2362 | ||
6720d03d | 2363 | monitor_lock_host(hostname, saddr); |
ccaf7288 A |
2364 | } |
2365 | ||
2366 | void | |
2367 | monitor_lock_host_by_addr(const struct sockaddr *saddr) | |
2368 | { | |
2369 | struct host *ihp; | |
2370 | struct hostent *hp; | |
2371 | char hostaddr[SM_MAXSTRLEN]; | |
2372 | struct sockaddr_in *sin = (struct sockaddr_in *)saddr; | |
7902cf7e | 2373 | |
ccaf7288 A |
2374 | if (getnameinfo(saddr, saddr->sa_len, hostaddr, sizeof(hostaddr), |
2375 | NULL, 0, NI_NUMERICHOST)) { | |
2376 | debuglog("monitor_lock_host: bad address\n"); | |
2377 | return; | |
2378 | } | |
2379 | debuglog("monitor_lock_host: %s\n", hostaddr); | |
2380 | ihp = get_lock_host(&hostlst_head, NULL, saddr); | |
2381 | if (ihp == NULL) | |
2382 | ihp = get_lock_host(&hostlst_unref, NULL, saddr); | |
2383 | if (ihp != NULL) { | |
2384 | debuglog("Monitor_lock_host: %s (cached)\n", ihp->name); | |
2385 | return; | |
2386 | } | |
2387 | ||
2388 | hp = gethostbyaddr((char*)&sin->sin_addr, sizeof(sin->sin_addr), AF_INET); | |
2389 | if (hp) { | |
2390 | monitor_lock_host(hp->h_name, saddr); | |
2391 | } else { | |
2392 | // herror(hostaddr); | |
2393 | monitor_lock_host(hostaddr, saddr); | |
2394 | } | |
2395 | } | |
2396 | ||
2397 | static void | |
2398 | monitor_lock_host(const char *hostname, const struct sockaddr *saddr) | |
2399 | { | |
2400 | struct host *nhp; | |
6720d03d | 2401 | struct hostent *hp = NULL; |
ccaf7288 A |
2402 | struct mon smon; |
2403 | struct sm_stat_res sres; | |
6720d03d A |
2404 | int rpcret; |
2405 | int retrying = 0; | |
ccaf7288 | 2406 | size_t n; |
6720d03d | 2407 | struct sockaddr_in *sin = (struct sockaddr_in *) saddr; |
ccaf7288 A |
2408 | |
2409 | rpcret = 0; | |
ccaf7288 A |
2410 | |
2411 | /* Host is not yet monitored, add it */ | |
2412 | debuglog("Monitor_lock_host: %s (creating)\n", hostname); | |
2413 | n = strnlen(hostname, SM_MAXSTRLEN); | |
2414 | if (n == SM_MAXSTRLEN) { | |
2415 | debuglog("monitor_lock_host: hostname too long\n"); | |
2416 | return; | |
2417 | } | |
6720d03d | 2418 | nhp = (struct host *) malloc(sizeof(struct host)); |
ac2f15b3 A |
2419 | if (nhp == NULL) { |
2420 | debuglog("Unable to allocate entry for statd mon\n"); | |
2421 | return; | |
2422 | } | |
2423 | ||
2424 | /* Allocated new host entry, now fill the fields */ | |
6720d03d A |
2425 | nhp->name = strdup(hostname); |
2426 | if (nhp->name == NULL) { | |
2427 | debuglog("Unable to allocate entry name for statd mon\n"); | |
2428 | free(nhp); | |
2429 | return; | |
2430 | } | |
2431 | nhp->revname = NULL; | |
ac2f15b3 | 2432 | nhp->refcnt = 1; |
7902cf7e | 2433 | nhp->lastuse = currsec; |
ccaf7288 A |
2434 | if (saddr) { |
2435 | bcopy(saddr, &nhp->addr, saddr->sa_len); | |
2436 | } else { | |
2437 | nhp->addr.sa_len = 0; | |
2438 | } | |
2439 | debuglog("Locally Monitoring host '%s'\n", hostname); | |
7902cf7e | 2440 | |
ac2f15b3 | 2441 | debuglog("Attempting to tell statd\n"); |
7902cf7e | 2442 | |
ac2f15b3 | 2443 | bzero(&smon,sizeof(smon)); |
7902cf7e | 2444 | |
ac2f15b3 A |
2445 | smon.mon_id.mon_name = nhp->name; |
2446 | smon.mon_id.my_id.my_name = "localhost\0"; | |
2447 | ||
2448 | smon.mon_id.my_id.my_prog = NLM_PROG; | |
2449 | smon.mon_id.my_id.my_vers = NLM_SM; | |
2450 | smon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; | |
7902cf7e | 2451 | |
6720d03d | 2452 | retry: |
ac2f15b3 A |
2453 | rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon, |
2454 | &smon, xdr_sm_stat_res, &sres); | |
7902cf7e | 2455 | |
ac2f15b3 | 2456 | if (rpcret == 0) { |
6720d03d | 2457 | if (sres.res_stat == stat_fail && !retrying) { |
ac2f15b3 | 2458 | debuglog("Statd call failed\n"); |
6720d03d A |
2459 | /* |
2460 | * It's possible that the hostname provided | |
2461 | * by the client isn't valid. Retry with a | |
2462 | * a hostname reverse-mapped from the client's | |
2463 | * address (if provided) and stash this name | |
2464 | * in the host entry so that we can identify it | |
2465 | * with this name later when we ask statd to | |
2466 | * unmonitor it. | |
2467 | */ | |
2468 | if (saddr) | |
2469 | hp = gethostbyaddr((char *) &sin->sin_addr, | |
2470 | sizeof(sin->sin_addr), AF_INET); | |
2471 | if (hp != NULL && strcmp(nhp->name, hp->h_name) != 0) { | |
2472 | debuglog("Statd retry with '%s'\n", hp->h_name); | |
2473 | smon.mon_id.mon_name = hp->h_name; | |
2474 | nhp->revname = strdup(hp->h_name); | |
2475 | if (nhp->revname == NULL) { | |
2476 | debuglog("No memory for revname\n"); | |
2477 | free(nhp->name); | |
2478 | free(nhp); | |
2479 | return; | |
2480 | } | |
2481 | retrying = 1; | |
2482 | goto retry; | |
2483 | } | |
ac2f15b3 A |
2484 | } |
2485 | } else { | |
2486 | debuglog("Rpc call to statd failed with return value: %d\n", | |
2487 | rpcret); | |
ac2f15b3 | 2488 | } |
7902cf7e | 2489 | |
6720d03d A |
2490 | /* |
2491 | * Insert host in the monitor list, even if statd | |
2492 | * doesn't like it. If the name is unacceptable | |
2493 | * to statd, at least we'll avoid subsequent rejection | |
2494 | * on every lock request. | |
2495 | */ | |
2496 | TAILQ_INSERT_HEAD(&hostlst_head, nhp, hostlst); | |
ac2f15b3 A |
2497 | } |
2498 | ||
2499 | /* | |
2500 | * unmonitor_lock_host: clear monitor ref counts and inform statd when gone | |
2501 | */ | |
2502 | void | |
2503 | unmonitor_lock_host(const char *hostname) | |
2504 | { | |
2505 | struct host *ihp; | |
ac2f15b3 | 2506 | |
7902cf7e | 2507 | TAILQ_FOREACH(ihp, &hostlst_head, hostlst) { |
ac2f15b3 | 2508 | if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) { |
ccaf7288 | 2509 | /* Host is unmonitored, drop refcount */ |
ac2f15b3 A |
2510 | --ihp->refcnt; |
2511 | /* Host should only be in the monitor list once */ | |
2512 | break; | |
2513 | } | |
2514 | } | |
2515 | ||
2516 | if (ihp == NULL) { | |
2517 | debuglog("Could not find host %16s in mon list\n", hostname); | |
2518 | return; | |
2519 | } | |
2520 | ||
2521 | if (ihp->refcnt > 0) | |
2522 | return; | |
2523 | ||
2524 | if (ihp->refcnt < 0) { | |
7902cf7e | 2525 | debuglog("Negative refcount!: %d\n", ihp->refcnt); |
ac2f15b3 A |
2526 | } |
2527 | ||
7902cf7e A |
2528 | TAILQ_REMOVE(&hostlst_head, ihp, hostlst); |
2529 | TAILQ_INSERT_HEAD(&hostlst_unref, ihp, hostlst); | |
2530 | if (host_expire <= 0) | |
2531 | destroy_lock_host(ihp); | |
2532 | } | |
2533 | ||
2534 | void | |
2535 | destroy_lock_host(struct host *ihp) | |
2536 | { | |
2537 | struct mon_id smon_id; | |
2538 | struct sm_stat smstat; | |
2539 | int rpcret; | |
6720d03d | 2540 | char *name; |
7902cf7e | 2541 | |
6720d03d A |
2542 | /* |
2543 | * If the client was monitored with a hostname obtained from | |
2544 | * its address, then use that instead of the possibly invalid | |
2545 | * hostname provided in caller_name. | |
2546 | */ | |
2547 | name = ihp->revname ? ihp->revname : ihp->name; | |
2548 | debuglog("Attempting to unmonitor host %16s\n", name); | |
ac2f15b3 A |
2549 | |
2550 | bzero(&smon_id,sizeof(smon_id)); | |
6720d03d | 2551 | smon_id.mon_name = name; |
ac2f15b3 A |
2552 | smon_id.my_id.my_name = "localhost"; |
2553 | smon_id.my_id.my_prog = NLM_PROG; | |
2554 | smon_id.my_id.my_vers = NLM_SM; | |
2555 | smon_id.my_id.my_proc = NLM_SM_NOTIFY; | |
2556 | ||
7902cf7e A |
2557 | rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON, xdr_mon_id, |
2558 | &smon_id, xdr_sm_stat, &smstat); | |
ac2f15b3 A |
2559 | |
2560 | if (rpcret != 0) { | |
2561 | debuglog("Rpc call to unmonitor statd failed with " | |
7902cf7e A |
2562 | " return value: %d: %s", rpcret, clnt_sperrno(rpcret)); |
2563 | } else { | |
2564 | debuglog("Succeeded unmonitoring %16s\n", ihp->name); | |
ac2f15b3 A |
2565 | } |
2566 | ||
7902cf7e | 2567 | TAILQ_REMOVE(&hostlst_unref, ihp, hostlst); |
6720d03d A |
2568 | if (ihp->name) |
2569 | free(ihp->name); | |
2570 | if (ihp->revname) | |
2571 | free(ihp->revname); | |
ac2f15b3 A |
2572 | free(ihp); |
2573 | } | |
2574 | ||
7902cf7e A |
2575 | /* |
2576 | * returns 1 if there are hosts to expire or 0 if there are none. | |
2577 | */ | |
2578 | int | |
2579 | expire_lock_hosts(void) | |
2580 | { | |
2581 | struct host *ihp; | |
2582 | ||
2583 | debuglog("expire_lock_hosts: called\n"); | |
2584 | for ( ;; ) { | |
2585 | ihp = TAILQ_LAST(&hostlst_unref, hostlst_head); | |
2586 | if (ihp == NULL) | |
2587 | break; | |
2588 | if (host_expire > 0 && ihp->lastuse >= currsec - host_expire) | |
2589 | break; | |
2590 | debuglog("expire_lock_hosts: expiring %s %d %d %d\n", | |
2591 | ihp->name, (int)ihp->lastuse, | |
2592 | (int)currsec, (int)currsec - host_expire); | |
2593 | destroy_lock_host(ihp); | |
2594 | } | |
2595 | return (TAILQ_LAST(&hostlst_unref, hostlst_head) != NULL); | |
2596 | } | |
2597 | ||
ac2f15b3 A |
2598 | /* |
2599 | * notify: Clear all locks from a host if statd complains | |
2600 | * | |
2601 | * XXX: This routine has not been thoroughly tested. However, neither | |
2602 | * had the old one been. It used to compare the statd crash state counter | |
2603 | * to the current lock state. The upshot of this was that it basically | |
2604 | * cleared all locks from the specified host 99% of the time (with the | |
2605 | * other 1% being a bug). Consequently, the assumption is that clearing | |
2606 | * all locks from a host when notified by statd is acceptable. | |
2607 | * | |
2608 | * Please note that this routine skips the usual level of redirection | |
2609 | * through a do_* type routine. This introduces a possible level of | |
2610 | * error and might better be written as do_notify and take this one out. | |
2611 | ||
2612 | */ | |
2613 | ||
2614 | void | |
2615 | notify(const char *hostname, const int state) | |
2616 | { | |
2617 | debuglog("notify from %s, new state %d", hostname, state); | |
2618 | ||
2619 | siglock(); | |
2620 | do_clear(hostname); | |
2621 | sigunlock(); | |
2622 | ||
2623 | debuglog("Leaving notify\n"); | |
2624 | } | |
2625 | ||
7902cf7e | 2626 | int |
ac2f15b3 A |
2627 | send_granted(fl, opcode) |
2628 | struct file_lock *fl; | |
2629 | int opcode __unused; | |
2630 | { | |
2631 | CLIENT *cli; | |
2632 | static char dummy; | |
2633 | struct timeval timeo; | |
7902cf7e | 2634 | enum clnt_stat rv; |
ac2f15b3 A |
2635 | static struct nlm_res retval; |
2636 | static struct nlm4_res retval4; | |
2637 | ||
2638 | debuglog("About to send granted on blocked lock\n"); | |
ac2f15b3 A |
2639 | |
2640 | cli = get_client(fl->addr, | |
2641 | (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS); | |
2642 | if (cli == NULL) { | |
2643 | syslog(LOG_NOTICE, "failed to get CLIENT for %s", | |
2644 | fl->client_name); | |
2645 | /* | |
2646 | * We fail to notify remote that the lock has been granted. | |
2647 | * The client will timeout and retry, the lock will be | |
2648 | * granted at this time. | |
2649 | */ | |
7902cf7e | 2650 | return -1; |
ac2f15b3 A |
2651 | } |
2652 | timeo.tv_sec = 0; | |
2653 | timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */ | |
2654 | ||
7902cf7e A |
2655 | fl->granted_cookie = ++send_granted_cookie; |
2656 | if (!send_granted_cookie) | |
2657 | send_granted_cookie++; | |
2658 | ||
ac2f15b3 A |
2659 | if (fl->flags & LOCK_V4) { |
2660 | static nlm4_testargs res; | |
7902cf7e A |
2661 | res.cookie.n_len = sizeof(fl->granted_cookie); |
2662 | res.cookie.n_bytes = (char*)&fl->granted_cookie; | |
ac2f15b3 A |
2663 | res.exclusive = fl->client.exclusive; |
2664 | res.alock.caller_name = fl->client_name; | |
2665 | res.alock.fh.n_len = fl->filehandle.n_len; | |
2666 | res.alock.fh.n_bytes = fl->filehandle.n_bytes; | |
2667 | res.alock.oh = fl->client.oh; | |
2668 | res.alock.svid = fl->client.svid; | |
2669 | res.alock.l_offset = fl->client.l_offset; | |
2670 | res.alock.l_len = fl->client.l_len; | |
2671 | debuglog("sending v4 reply%s", | |
2672 | (fl->flags & LOCK_ASYNC) ? " (async)":""); | |
2673 | if (fl->flags & LOCK_ASYNC) { | |
7902cf7e | 2674 | rv = clnt_call(cli, NLM4_GRANTED_MSG, |
ac2f15b3 A |
2675 | xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo); |
2676 | } else { | |
7902cf7e | 2677 | rv = clnt_call(cli, NLM4_GRANTED, |
ac2f15b3 A |
2678 | xdr_nlm4_testargs, &res, xdr_nlm4_res, |
2679 | &retval4, timeo); | |
2680 | } | |
2681 | } else { | |
2682 | static nlm_testargs res; | |
2683 | ||
7902cf7e A |
2684 | res.cookie.n_len = sizeof(fl->granted_cookie); |
2685 | res.cookie.n_bytes = (char*)&fl->granted_cookie; | |
ac2f15b3 A |
2686 | res.exclusive = fl->client.exclusive; |
2687 | res.alock.caller_name = fl->client_name; | |
2688 | res.alock.fh.n_len = fl->filehandle.n_len; | |
2689 | res.alock.fh.n_bytes = fl->filehandle.n_bytes; | |
2690 | res.alock.oh = fl->client.oh; | |
2691 | res.alock.svid = fl->client.svid; | |
2692 | res.alock.l_offset = fl->client.l_offset; | |
2693 | res.alock.l_len = fl->client.l_len; | |
2694 | debuglog("sending v1 reply%s", | |
2695 | (fl->flags & LOCK_ASYNC) ? " (async)":""); | |
2696 | if (fl->flags & LOCK_ASYNC) { | |
7902cf7e | 2697 | rv = clnt_call(cli, NLM_GRANTED_MSG, |
ac2f15b3 A |
2698 | xdr_nlm_testargs, &res, xdr_void, &dummy, timeo); |
2699 | } else { | |
7902cf7e | 2700 | rv = clnt_call(cli, NLM_GRANTED, |
ac2f15b3 A |
2701 | xdr_nlm_testargs, &res, xdr_nlm_res, |
2702 | &retval, timeo); | |
2703 | } | |
2704 | } | |
2705 | if (debug_level > 2) | |
2706 | debuglog("clnt_call returns %d(%s) for granted", | |
7902cf7e A |
2707 | rv, clnt_sperrno(rv)); |
2708 | ||
2709 | if ((rv != RPC_SUCCESS) && | |
2710 | !((fl->flags & LOCK_ASYNC) && (rv == RPC_TIMEDOUT))) | |
2711 | return -1; | |
2712 | return 0; | |
2713 | } | |
2714 | ||
2715 | /* | |
2716 | * granted_failed: remove a granted lock that wasn't successfully | |
2717 | * accepted by the client | |
2718 | */ | |
2719 | void | |
2720 | granted_failed(nlm4_res *arg) | |
2721 | { | |
2722 | u_int64_t cookie; | |
2723 | struct file_lock *ifl; | |
2724 | ||
2725 | debuglog("Entering granted_failed, status %d\n", arg->stat.stat); | |
2726 | ||
2727 | if (arg->cookie.n_len != sizeof(cookie)) { | |
2728 | debuglog("Exiting granted_failed: bogus cookie size %d\n", | |
2729 | arg->cookie.n_len); | |
2730 | return; | |
2731 | } | |
2732 | bcopy(arg->cookie.n_bytes, &cookie, sizeof(cookie)); | |
2733 | debuglog("granted_failed, cookie 0x%llx\n", cookie); | |
2734 | ||
2735 | LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) { | |
2736 | debuglog("Pointer to file lock: %p\n",ifl); | |
2737 | ||
2738 | debuglog("****Dump of ifl****\n"); | |
2739 | dump_filelock(ifl); | |
2740 | ||
2741 | if (ifl->granted_cookie != cookie) | |
2742 | continue; | |
2743 | ||
2744 | debuglog("granted_failed: cookie found\n"); | |
2745 | break; | |
2746 | } | |
2747 | ||
2748 | if (ifl) { | |
2749 | do_unlock(ifl); | |
2750 | /* ifl is NO LONGER VALID AT THIS POINT */ | |
2751 | } else { | |
2752 | debuglog("granted_failed: cookie NOT FOUND\n"); | |
2753 | } | |
ac2f15b3 | 2754 | |
7902cf7e | 2755 | debuglog("Exiting granted_failed\n"); |
ac2f15b3 A |
2756 | } |
2757 | ||
2758 | /* | |
2759 | * getshare: try to acquire a share reservation | |
2760 | */ | |
2b484d24 | 2761 | enum nlm4_stats |
ac2f15b3 A |
2762 | getshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags) |
2763 | { | |
2764 | struct sharefile *shrfile; | |
2765 | struct file_share *sh; | |
ccaf7288 | 2766 | size_t n; |
ac2f15b3 A |
2767 | |
2768 | debuglog("Entering getshare...\n"); | |
2769 | ||
2770 | if (grace_expired == 0 && shrarg->reclaim == 0) { | |
2771 | debuglog("getshare denied - grace period\n"); | |
2772 | return (flags & LOCK_V4) ? | |
2773 | nlm4_denied_grace_period : | |
2774 | nlm_denied_grace_period; | |
2775 | } | |
2776 | ||
2b484d24 A |
2777 | if (shrarg->share.fh.n_len > NFS_MAX_FH_SIZE) { |
2778 | debuglog("received fhandle size %d, max size %d", | |
2779 | shrarg->share.fh.n_len, NFS_MAX_FH_SIZE); | |
07f47057 A |
2780 | return (flags & LOCK_V4) ? nlm4_failed : nlm_denied; |
2781 | } | |
2782 | ||
ac2f15b3 A |
2783 | /* find file in list of share files */ |
2784 | LIST_FOREACH(shrfile, &nfssharefilelist_head, sharefilelist) { | |
2785 | if ((shrarg->share.fh.n_len == shrfile->filehandle.n_len) && | |
2786 | (bcmp(shrarg->share.fh.n_bytes, shrfile->filehandle.n_bytes, | |
2787 | shrarg->share.fh.n_len) == 0)) { | |
2788 | /* shrfile is the correct file */ | |
2789 | break; | |
2790 | } | |
2791 | } | |
2792 | ||
2793 | /* if share file not found, create a new share file */ | |
2794 | if (!shrfile) { | |
2b484d24 | 2795 | fhandle_t fh; |
ac2f15b3 | 2796 | int fd; |
2b484d24 A |
2797 | fh.fh_len = shrarg->share.fh.n_len; |
2798 | bcopy(shrarg->share.fh.n_bytes, fh.fh_data, fh.fh_len); | |
2799 | fd = fhopen(&fh, O_RDONLY); | |
ac2f15b3 A |
2800 | if (fd < 0) { |
2801 | debuglog("fhopen failed (from %16s): %32s\n", | |
2802 | shrarg->share.caller_name, strerror(errno)); | |
2803 | if ((flags & LOCK_V4) == 0) | |
2804 | return nlm_denied; | |
2805 | switch (errno) { | |
2806 | case ESTALE: | |
2807 | return nlm4_stale_fh; | |
2808 | default: | |
2809 | return nlm4_failed; | |
2810 | } | |
2811 | } | |
2812 | shrfile = malloc(sizeof(struct sharefile)); | |
2813 | if (!shrfile) { | |
2814 | debuglog("getshare failed: can't allocate sharefile\n"); | |
2815 | close(fd); | |
2b484d24 | 2816 | return (flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; |
ac2f15b3 A |
2817 | } |
2818 | shrfile->filehandle.n_len = shrarg->share.fh.n_len; | |
2819 | shrfile->filehandle.n_bytes = malloc(shrarg->share.fh.n_len); | |
2820 | if (!shrfile->filehandle.n_bytes) { | |
2821 | debuglog("getshare failed: can't allocate sharefile filehandle\n"); | |
2822 | free(shrfile); | |
2823 | close(fd); | |
2b484d24 | 2824 | return (flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; |
ac2f15b3 A |
2825 | } |
2826 | bcopy(shrarg->share.fh.n_bytes, shrfile->filehandle.n_bytes, | |
2827 | shrarg->share.fh.n_len); | |
2828 | shrfile->fd = fd; | |
2829 | shrfile->refcount = 0; | |
2830 | shrfile->sharelist_head.lh_first = NULL; | |
2831 | LIST_INSERT_HEAD(&nfssharefilelist_head, shrfile, sharefilelist); | |
2832 | } | |
2833 | ||
2834 | /* compare request mode/access to current shares */ | |
2835 | LIST_FOREACH(sh, &shrfile->sharelist_head, nfssharelist) { | |
2836 | /* if request host/owner matches a current share... */ | |
2837 | if ((strncmp(shrarg->share.caller_name, sh->client_name, SM_MAXSTRLEN) == 0) && | |
2838 | same_netobj(&shrarg->share.oh, &sh->oh)) { | |
2839 | /* ...then just update share mode/access */ | |
2840 | sh->mode = shrarg->share.mode; | |
2841 | sh->access = shrarg->share.access; | |
2842 | debuglog("getshare: updated existing share\n"); | |
2b484d24 | 2843 | return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
ac2f15b3 A |
2844 | } |
2845 | if (((shrarg->share.mode & sh->access) != 0) || | |
2846 | ((shrarg->share.access & sh->mode) != 0)) { | |
2847 | /* share request conflicts with existing share */ | |
2848 | debuglog("getshare: conflicts with existing share\n"); | |
2b484d24 | 2849 | return (flags & LOCK_V4) ? nlm4_denied : nlm_denied; |
ac2f15b3 A |
2850 | } |
2851 | } | |
2852 | ||
2853 | /* create/init new share */ | |
ccaf7288 A |
2854 | n = strnlen(shrarg->share.caller_name, SM_MAXSTRLEN); |
2855 | if (n < SM_MAXSTRLEN) { | |
2856 | sh = malloc(sizeof(*sh) - sizeof(sh->client_name) + n + 1); | |
2857 | } else { | |
2858 | debuglog("getshare failed: hostname too long\n"); | |
2859 | sh = NULL; | |
2860 | } | |
ac2f15b3 A |
2861 | if (!sh) { |
2862 | debuglog("getshare failed: can't allocate share\n"); | |
2863 | if (!shrfile->refcount) { | |
2864 | LIST_REMOVE(shrfile, sharefilelist); | |
2865 | close(shrfile->fd); | |
2866 | free(shrfile->filehandle.n_bytes); | |
2867 | free(shrfile); | |
2868 | } | |
2b484d24 | 2869 | return (flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; |
ac2f15b3 | 2870 | } |
ccaf7288 | 2871 | bzero(sh, sizeof(*sh) - sizeof(sh->client_name)); |
ac2f15b3 A |
2872 | sh->oh.n_len = shrarg->share.oh.n_len; |
2873 | sh->oh.n_bytes = malloc(sh->oh.n_len); | |
2874 | if (!sh->oh.n_bytes) { | |
2875 | debuglog("getshare failed: can't allocate share owner handle\n"); | |
2876 | free(sh); | |
2877 | if (!shrfile->refcount) { | |
2878 | LIST_REMOVE(shrfile, sharefilelist); | |
2879 | close(shrfile->fd); | |
2880 | free(shrfile->filehandle.n_bytes); | |
2881 | free(shrfile); | |
2882 | } | |
2b484d24 | 2883 | return (flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks; |
ac2f15b3 | 2884 | } |
ccaf7288 A |
2885 | memcpy(sh->client_name, shrarg->share.caller_name, n); |
2886 | sh->client_name[n] = 0; | |
ac2f15b3 A |
2887 | sh->mode = shrarg->share.mode; |
2888 | sh->access = shrarg->share.access; | |
2889 | ||
2890 | /* insert new share into file's share list */ | |
2891 | LIST_INSERT_HEAD(&shrfile->sharelist_head, sh, nfssharelist); | |
2892 | shrfile->refcount++; | |
2893 | ||
2894 | debuglog("Exiting getshare...\n"); | |
2895 | ||
2b484d24 | 2896 | return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
ac2f15b3 A |
2897 | } |
2898 | ||
2899 | ||
2900 | /* remove a share reservation */ | |
2b484d24 | 2901 | enum nlm4_stats |
07f47057 | 2902 | unshare(nlm_shareargs *shrarg, struct svc_req *rqstp, const int flags) |
ac2f15b3 A |
2903 | { |
2904 | struct sharefile *shrfile; | |
2905 | struct file_share *sh; | |
2906 | ||
2907 | debuglog("Entering unshare...\n"); | |
2908 | ||
2b484d24 A |
2909 | if (shrarg->share.fh.n_len > NFS_MAX_FH_SIZE) { |
2910 | debuglog("received fhandle size %d, max size %d", | |
2911 | shrarg->share.fh.n_len, NFS_MAX_FH_SIZE); | |
07f47057 A |
2912 | return (flags & LOCK_V4) ? nlm4_failed : nlm_denied; |
2913 | } | |
2914 | ||
ac2f15b3 A |
2915 | /* find file in list of share files */ |
2916 | LIST_FOREACH(shrfile, &nfssharefilelist_head, sharefilelist) { | |
2917 | if ((shrarg->share.fh.n_len == shrfile->filehandle.n_len) && | |
2918 | (bcmp(shrarg->share.fh.n_bytes, shrfile->filehandle.n_bytes, | |
2919 | shrarg->share.fh.n_len) == 0)) { | |
2920 | /* shrfile is the correct file */ | |
2921 | break; | |
2922 | } | |
2923 | } | |
2924 | ||
2925 | /* if share file not found, return success (per spec) */ | |
2926 | if (!shrfile) { | |
2927 | debuglog("unshare: no such share file\n"); | |
2b484d24 | 2928 | return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
ac2f15b3 A |
2929 | } |
2930 | ||
2931 | /* find share */ | |
2932 | LIST_FOREACH(sh, &shrfile->sharelist_head, nfssharelist) { | |
2933 | /* if request host/owner matches a current share... */ | |
2934 | if ((strncmp(shrarg->share.caller_name, sh->client_name, SM_MAXSTRLEN) == 0) && | |
2935 | same_netobj(&shrarg->share.oh, &sh->oh)) | |
2936 | break; | |
2937 | } | |
2938 | ||
2939 | /* if share not found, return success (per spec) */ | |
2940 | if (!sh) { | |
2941 | debuglog("unshare: no such share\n"); | |
2b484d24 | 2942 | return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
ac2f15b3 A |
2943 | } |
2944 | ||
2945 | /* remove share from file and deallocate */ | |
2946 | shrfile->refcount--; | |
2947 | LIST_REMOVE(sh, nfssharelist); | |
2948 | free(sh->oh.n_bytes); | |
2949 | free(sh); | |
2950 | ||
2951 | /* if file has no more shares, deallocate share file */ | |
2952 | if (!shrfile->refcount) { | |
2953 | debuglog("unshare: file has no more shares\n"); | |
2954 | LIST_REMOVE(shrfile, sharefilelist); | |
2955 | close(shrfile->fd); | |
2956 | free(shrfile->filehandle.n_bytes); | |
2957 | free(shrfile); | |
2958 | } | |
2959 | ||
2960 | debuglog("Exiting unshare...\n"); | |
2961 | ||
2b484d24 | 2962 | return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
ac2f15b3 A |
2963 | } |
2964 | ||
2965 | /* | |
2966 | * do_free_all | |
2967 | * | |
2968 | * Wipe out all non-monitored locks and shares held by a host. | |
2969 | */ | |
2970 | ||
2971 | void | |
2972 | do_free_all(const char *hostname) | |
2973 | { | |
2974 | struct file_lock *ifl, *nfl; | |
2975 | struct sharefile *shrfile, *nshrfile; | |
2976 | struct file_share *ifs, *nfs; | |
2b484d24 | 2977 | enum partialfilelock_status pfsret; |
ac2f15b3 A |
2978 | |
2979 | /* clear non-monitored blocking file locks */ | |
2980 | ifl = LIST_FIRST(&blockedlocklist_head); | |
2981 | while (ifl != NULL) { | |
2982 | nfl = LIST_NEXT(ifl, nfslocklist); | |
2983 | ||
2984 | if (((ifl->flags & LOCK_MON) == 0) && | |
2985 | (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0)) { | |
2986 | remove_blockingfilelock(ifl); | |
2987 | deallocate_file_lock(ifl); | |
2988 | } | |
2989 | ||
2990 | ifl = nfl; | |
2991 | } | |
2992 | ||
2993 | /* clear non-monitored file locks */ | |
2b484d24 | 2994 | restart: |
ac2f15b3 A |
2995 | ifl = LIST_FIRST(&nfslocklist_head); |
2996 | while (ifl != NULL) { | |
2997 | nfl = LIST_NEXT(ifl, nfslocklist); | |
2998 | ||
2999 | if (((ifl->flags & LOCK_MON) == 0) && | |
3000 | (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0)) { | |
3001 | /* Unlock destroys ifl out from underneath */ | |
2b484d24 A |
3002 | pfsret = unlock_partialfilelock(ifl); |
3003 | if (pfsret != PFL_GRANTED) { | |
3004 | /* Uh oh... there was some sort of problem. */ | |
3005 | /* If we restart the loop, we may get */ | |
3006 | /* stuck here forever getting errors. */ | |
3007 | /* So, let's just abort the whole scan. */ | |
3008 | syslog(LOG_WARNING, "unmonitored lock clearing for %s failed: %d", | |
3009 | hostname, pfsret); | |
3010 | break; | |
3011 | } | |
ac2f15b3 | 3012 | /* ifl is NO LONGER VALID AT THIS POINT */ |
2b484d24 A |
3013 | /* Note: the unlock may deallocate several existing locks. */ |
3014 | /* Therefore, we need to restart the scanning of the list, */ | |
3015 | /* because nfl could be pointing to a freed lock. */ | |
3016 | goto restart; | |
ac2f15b3 A |
3017 | } |
3018 | ||
3019 | ifl = nfl; | |
3020 | } | |
3021 | ||
3022 | /* clear shares */ | |
3023 | shrfile = LIST_FIRST(&nfssharefilelist_head); | |
3024 | while (shrfile != NULL) { | |
3025 | nshrfile = LIST_NEXT(shrfile, sharefilelist); | |
3026 | ||
3027 | ifs = LIST_FIRST(&shrfile->sharelist_head); | |
3028 | while (ifs != NULL) { | |
3029 | nfs = LIST_NEXT(ifs, nfssharelist); | |
3030 | ||
3031 | if (strncmp(hostname, ifs->client_name, SM_MAXSTRLEN) == 0) { | |
3032 | shrfile->refcount--; | |
3033 | LIST_REMOVE(ifs, nfssharelist); | |
3034 | free(ifs->oh.n_bytes); | |
3035 | free(ifs); | |
3036 | } | |
3037 | ||
3038 | ifs = nfs; | |
3039 | } | |
3040 | ||
3041 | if (!shrfile->refcount) { | |
3042 | LIST_REMOVE(shrfile, sharefilelist); | |
3043 | close(shrfile->fd); | |
3044 | free(shrfile->filehandle.n_bytes); | |
3045 | free(shrfile); | |
3046 | } | |
3047 | ||
3048 | shrfile = nshrfile; | |
3049 | } | |
3050 | ||
3051 | } | |
3052 | ||
3053 | ||
3054 | ||
3055 | /* | |
3056 | * Routines below here have not been modified in the overhaul | |
3057 | */ | |
3058 | ||
3059 | /* | |
3060 | * Are these two routines still required since lockd is not spawning off | |
3061 | * children to service locks anymore? Presumably they were originally | |
3062 | * put in place to prevent a one child from changing the lock list out | |
3063 | * from under another one. | |
3064 | */ | |
3065 | ||
3066 | void | |
3067 | siglock(void) | |
3068 | { | |
3069 | sigset_t block; | |
3070 | ||
3071 | sigemptyset(&block); | |
3072 | sigaddset(&block, SIGCHLD); | |
3073 | ||
3074 | if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { | |
3075 | syslog(LOG_WARNING, "siglock failed: %s", strerror(errno)); | |
3076 | } | |
3077 | } | |
3078 | ||
3079 | void | |
3080 | sigunlock(void) | |
3081 | { | |
3082 | sigset_t block; | |
3083 | ||
3084 | sigemptyset(&block); | |
3085 | sigaddset(&block, SIGCHLD); | |
3086 | ||
3087 | if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { | |
3088 | syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno)); | |
3089 | } | |
3090 | } | |
3091 | ||
3092 |