File: | src/usr.sbin/rpc.lockd/lockd_lock.c |
Warning: | line 726, column 2 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: lockd_lock.c,v 1.12 2023/03/08 04:43:15 guenther Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2000 Manuel Bouyer. | |||
5 | * | |||
6 | * Redistribution and use in source and binary forms, with or without | |||
7 | * modification, are permitted provided that the following conditions | |||
8 | * are met: | |||
9 | * 1. Redistributions of source code must retain the above copyright | |||
10 | * notice, this list of conditions and the following disclaimer. | |||
11 | * 2. Redistributions in binary form must reproduce the above copyright | |||
12 | * notice, this list of conditions and the following disclaimer in the | |||
13 | * documentation and/or other materials provided with the distribution. | |||
14 | * | |||
15 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |||
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |||
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
25 | * SUCH DAMAGE. | |||
26 | * | |||
27 | */ | |||
28 | ||||
29 | #include <sys/socket.h> | |||
30 | #include <sys/stat.h> | |||
31 | #include <sys/mount.h> | |||
32 | #include <sys/wait.h> | |||
33 | #include <stdio.h> | |||
34 | #include <stdlib.h> | |||
35 | #include <unistd.h> | |||
36 | #include <fcntl.h> | |||
37 | #include <inttypes.h> | |||
38 | #include <syslog.h> | |||
39 | #include <errno(*__errno()).h> | |||
40 | #include <string.h> | |||
41 | #include <signal.h> | |||
42 | #include <rpc/rpc.h> | |||
43 | #include <rpcsvc/sm_inter.h> | |||
44 | #include <rpcsvc/nlm_prot.h> | |||
45 | #include "lockd_lock.h" | |||
46 | #include "lockd.h" | |||
47 | ||||
48 | /* A set of utilities for managing file locking */ | |||
49 | LIST_HEAD(lcklst_head, file_lock)struct lcklst_head { struct file_lock *lh_first; }; | |||
50 | struct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head){ ((void *)0) }; | |||
51 | ||||
52 | #define FHANDLE_SIZE_MAX1024 1024 /* arbitrary big enough value */ | |||
53 | typedef struct { | |||
54 | size_t fhsize; | |||
55 | char *fhdata; | |||
56 | } nfs_fhandle_t; | |||
57 | ||||
58 | static int | |||
59 | fhcmp(const nfs_fhandle_t *fh1, const nfs_fhandle_t *fh2) | |||
60 | { | |||
61 | return memcmp(fh1->fhdata, fh2->fhdata, sizeof(fhandle_t)); | |||
62 | } | |||
63 | ||||
64 | static int | |||
65 | fhconv(nfs_fhandle_t *fh, const netobj *rfh) | |||
66 | { | |||
67 | size_t sz; | |||
68 | ||||
69 | sz = rfh->n_len; | |||
70 | if (sz > FHANDLE_SIZE_MAX1024) { | |||
71 | syslog(LOG_DEBUG7, | |||
72 | "received fhandle size %zd, max supported size %d", | |||
73 | sz, FHANDLE_SIZE_MAX1024); | |||
74 | errno(*__errno()) = EINVAL22; | |||
75 | return -1; | |||
76 | } | |||
77 | fh->fhdata = malloc(sz); | |||
78 | if (fh->fhdata == NULL((void *)0)) { | |||
79 | return -1; | |||
80 | } | |||
81 | fh->fhsize = sz; | |||
82 | memcpy(fh->fhdata, rfh->n_bytes, sz); | |||
83 | return 0; | |||
84 | } | |||
85 | ||||
86 | static void | |||
87 | fhfree(nfs_fhandle_t *fh) | |||
88 | { | |||
89 | ||||
90 | free(fh->fhdata); | |||
91 | } | |||
92 | ||||
93 | /* struct describing a lock */ | |||
94 | struct file_lock { | |||
95 | LIST_ENTRY(file_lock)struct { struct file_lock *le_next; struct file_lock **le_prev ; } lcklst; | |||
96 | nfs_fhandle_t filehandle; /* NFS filehandle */ | |||
97 | struct sockaddr_in *addr; | |||
98 | struct nlm4_holder client; /* lock holder */ | |||
99 | netobj client_cookie; /* cookie sent by the client */ | |||
100 | char client_name[128]; | |||
101 | int nsm_status; /* status from the remote lock manager */ | |||
102 | int status; /* lock status, see below */ | |||
103 | int flags; /* lock flags, see lockd_lock.h */ | |||
104 | pid_t locker; /* pid of the child process trying to get the lock */ | |||
105 | int fd; /* file descriptor for this lock */ | |||
106 | }; | |||
107 | ||||
108 | /* lock status */ | |||
109 | #define LKST_LOCKED1 1 /* lock is locked */ | |||
110 | #define LKST_WAITING2 2 /* file is already locked by another host */ | |||
111 | #define LKST_PROCESSING3 3 /* child is trying to acquire the lock */ | |||
112 | #define LKST_DYING4 4 /* must die when we get news from the child */ | |||
113 | ||||
114 | static struct file_lock *lalloc(void); | |||
115 | void lfree(struct file_lock *); | |||
116 | enum nlm_stats do_lock(struct file_lock *, int); | |||
117 | enum nlm_stats do_unlock(struct file_lock *); | |||
118 | void send_granted(struct file_lock *, int); | |||
119 | void siglock(void); | |||
120 | void sigunlock(void); | |||
121 | ||||
122 | /* list of hosts we monitor */ | |||
123 | LIST_HEAD(hostlst_head, host)struct hostlst_head { struct host *lh_first; }; | |||
124 | struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head){ ((void *)0) }; | |||
125 | ||||
126 | /* struct describing a lock */ | |||
127 | struct host { | |||
128 | LIST_ENTRY(host)struct { struct host *le_next; struct host **le_prev; } hostlst; | |||
129 | char name[SM_MAXSTRLEN1024+1]; | |||
130 | int refcnt; | |||
131 | }; | |||
132 | ||||
133 | void do_mon(const char *); | |||
134 | ||||
135 | #define LL_FH0x01 0x01 | |||
136 | #define LL_NAME0x02 0x02 | |||
137 | #define LL_SVID0x04 0x04 | |||
138 | ||||
139 | static struct file_lock *lock_lookup(struct file_lock *, int); | |||
140 | ||||
141 | /* | |||
142 | * lock_lookup: lookup a matching lock. | |||
143 | * called with siglock held. | |||
144 | */ | |||
145 | static struct file_lock * | |||
146 | lock_lookup(struct file_lock *newfl, int flags) | |||
147 | { | |||
148 | struct file_lock *fl; | |||
149 | ||||
150 | LIST_FOREACH(fl, &lcklst_head, lcklst)for((fl) = ((&lcklst_head)->lh_first); (fl)!= ((void * )0); (fl) = ((fl)->lcklst.le_next)) { | |||
151 | if ((flags & LL_SVID0x04) != 0 && | |||
152 | newfl->client.svid != fl->client.svid) | |||
153 | continue; | |||
154 | if ((flags & LL_NAME0x02) != 0 && | |||
155 | strcmp(newfl->client_name, fl->client_name) != 0) | |||
156 | continue; | |||
157 | if ((flags & LL_FH0x01) != 0 && | |||
158 | fhcmp(&newfl->filehandle, &fl->filehandle) != 0) | |||
159 | continue; | |||
160 | /* found */ | |||
161 | break; | |||
162 | } | |||
163 | ||||
164 | return fl; | |||
165 | } | |||
166 | ||||
167 | /* | |||
168 | * testlock(): inform the caller if the requested lock would be granted or not | |||
169 | * returns NULL if lock would granted, or pointer to the current nlm4_holder | |||
170 | * otherwise. | |||
171 | */ | |||
172 | ||||
173 | struct nlm4_holder * | |||
174 | testlock(struct nlm4_lock *lock, int flags) | |||
175 | { | |||
176 | struct file_lock *fl; | |||
177 | nfs_fhandle_t filehandle; | |||
178 | ||||
179 | /* convert lock to a local filehandle */ | |||
180 | if (fhconv(&filehandle, &lock->fh)) { | |||
181 | syslog(LOG_NOTICE5, "fhconv failed (%m)"); | |||
182 | return NULL((void *)0); /* XXX */ | |||
183 | } | |||
184 | ||||
185 | siglock(); | |||
186 | /* search through the list for lock holder */ | |||
187 | LIST_FOREACH(fl, &lcklst_head, lcklst)for((fl) = ((&lcklst_head)->lh_first); (fl)!= ((void * )0); (fl) = ((fl)->lcklst.le_next)) { | |||
188 | if (fl->status != LKST_LOCKED1) | |||
189 | continue; | |||
190 | if (fhcmp(&fl->filehandle, &filehandle) != 0) | |||
191 | continue; | |||
192 | /* got it ! */ | |||
193 | syslog(LOG_DEBUG7, "test for %s: found lock held by %s", | |||
194 | lock->caller_name, fl->client_name); | |||
195 | sigunlock(); | |||
196 | fhfree(&filehandle); | |||
197 | return (&fl->client); | |||
198 | } | |||
199 | /* not found */ | |||
200 | sigunlock(); | |||
201 | fhfree(&filehandle); | |||
202 | syslog(LOG_DEBUG7, "test for %s: no lock found", lock->caller_name); | |||
203 | return NULL((void *)0); | |||
204 | } | |||
205 | ||||
206 | /* | |||
207 | * getlock: try to acquire the lock. | |||
208 | * If file is already locked and we can sleep, put the lock in the list with | |||
209 | * status LKST_WAITING; it'll be processed later. | |||
210 | * Otherwise try to lock. If we're allowed to block, fork a child which | |||
211 | * will do the blocking lock. | |||
212 | */ | |||
213 | enum nlm_stats | |||
214 | getlock(nlm4_lockargs * lckarg, struct svc_req *rqstp, int flags) | |||
215 | { | |||
216 | struct file_lock *fl, *newfl; | |||
217 | enum nlm_stats retval; | |||
218 | struct sockaddr_in *addr; | |||
219 | ||||
220 | if (grace_expired == 0 && lckarg->reclaim == 0) | |||
221 | return (flags & LOCK_V40x02) ? | |||
222 | nlm4_denied_grace_period : nlm_denied_grace_period; | |||
223 | ||||
224 | /* allocate new file_lock for this request */ | |||
225 | newfl = lalloc(); | |||
226 | if (newfl == NULL((void *)0)) { | |||
227 | syslog(LOG_NOTICE5, "malloc failed (%m)"); | |||
228 | /* failed */ | |||
229 | return (flags & LOCK_V40x02) ? | |||
230 | nlm4_denied_nolock : nlm_denied_nolocks; | |||
231 | } | |||
232 | if (fhconv(&newfl->filehandle, &lckarg->alock.fh)) { | |||
233 | syslog(LOG_NOTICE5, "fhconv failed (%m)"); | |||
234 | lfree(newfl); | |||
235 | /* failed */ | |||
236 | return (flags & LOCK_V40x02) ? | |||
237 | nlm4_denied_nolock : nlm_denied_nolocks; | |||
238 | } | |||
239 | addr = svc_getcaller(rqstp->rq_xprt)(&(rqstp->rq_xprt)->xp_raddr); | |||
240 | newfl->addr = malloc(addr->sin_len); | |||
241 | if (newfl->addr == NULL((void *)0)) { | |||
242 | syslog(LOG_NOTICE5, "malloc failed (%m)"); | |||
243 | lfree(newfl); | |||
244 | /* failed */ | |||
245 | return (flags & LOCK_V40x02) ? | |||
246 | nlm4_denied_nolock : nlm_denied_nolocks; | |||
247 | } | |||
248 | memcpy(newfl->addr, addr, addr->sin_len); | |||
249 | newfl->client.exclusive = lckarg->exclusive; | |||
250 | newfl->client.svid = lckarg->alock.svid; | |||
251 | newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len); | |||
252 | if (newfl->client.oh.n_bytes == NULL((void *)0)) { | |||
253 | syslog(LOG_NOTICE5, "malloc failed (%m)"); | |||
254 | lfree(newfl); | |||
255 | return (flags & LOCK_V40x02) ? | |||
256 | nlm4_denied_nolock : nlm_denied_nolocks; | |||
257 | } | |||
258 | newfl->client.oh.n_len = lckarg->alock.oh.n_len; | |||
259 | memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes, | |||
260 | lckarg->alock.oh.n_len); | |||
261 | newfl->client.l_offset = lckarg->alock.l_offset; | |||
262 | newfl->client.l_len = lckarg->alock.l_len; | |||
263 | newfl->client_cookie.n_len = lckarg->cookie.n_len; | |||
264 | newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len); | |||
265 | if (newfl->client_cookie.n_bytes == NULL((void *)0)) { | |||
266 | syslog(LOG_NOTICE5, "malloc failed (%m)"); | |||
267 | lfree(newfl); | |||
268 | return (flags & LOCK_V40x02) ? | |||
269 | nlm4_denied_nolock : nlm_denied_nolocks; | |||
270 | } | |||
271 | memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes, | |||
272 | lckarg->cookie.n_len); | |||
273 | strlcpy(newfl->client_name, lckarg->alock.caller_name, | |||
274 | sizeof(newfl->client_name)); | |||
275 | newfl->nsm_status = lckarg->state; | |||
276 | newfl->status = 0; | |||
277 | newfl->flags = flags; | |||
278 | siglock(); | |||
279 | /* look for a lock rq from this host for this fh */ | |||
280 | fl = lock_lookup(newfl, LL_FH0x01|LL_NAME0x02|LL_SVID0x04); | |||
281 | if (fl) { | |||
282 | /* already locked by this host ??? */ | |||
283 | sigunlock(); | |||
284 | syslog(LOG_NOTICE5, "duplicate lock from %s.%" | |||
285 | PRIu32"u", | |||
286 | newfl->client_name, newfl->client.svid); | |||
287 | lfree(newfl); | |||
288 | switch(fl->status) { | |||
289 | case LKST_LOCKED1: | |||
290 | return (flags & LOCK_V40x02) ? | |||
291 | nlm4_granted : nlm_granted; | |||
292 | case LKST_WAITING2: | |||
293 | case LKST_PROCESSING3: | |||
294 | return (flags & LOCK_V40x02) ? | |||
295 | nlm4_blocked : nlm_blocked; | |||
296 | case LKST_DYING4: | |||
297 | return (flags & LOCK_V40x02) ? | |||
298 | nlm4_denied : nlm_denied; | |||
299 | default: | |||
300 | syslog(LOG_NOTICE5, "bad status %d", | |||
301 | fl->status); | |||
302 | return (flags & LOCK_V40x02) ? | |||
303 | nlm4_failed : nlm_denied; | |||
304 | } | |||
305 | /* NOTREACHED */ | |||
306 | } | |||
307 | fl = lock_lookup(newfl, LL_FH0x01); | |||
308 | if (fl) { | |||
309 | /* | |||
310 | * We already have a lock for this file. | |||
311 | * Put this one in waiting state if allowed to block | |||
312 | */ | |||
313 | if (lckarg->block) { | |||
314 | syslog(LOG_DEBUG7, "lock from %s.%" PRIu32"u" ": " | |||
315 | "already locked, waiting", | |||
316 | lckarg->alock.caller_name, | |||
317 | lckarg->alock.svid); | |||
318 | newfl->status = LKST_WAITING2; | |||
319 | LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst)do { if (((newfl)->lcklst.le_next = (&lcklst_head)-> lh_first) != ((void *)0)) (&lcklst_head)->lh_first-> lcklst.le_prev = &(newfl)->lcklst.le_next; (&lcklst_head )->lh_first = (newfl); (newfl)->lcklst.le_prev = &( &lcklst_head)->lh_first; } while (0); | |||
320 | do_mon(lckarg->alock.caller_name); | |||
321 | sigunlock(); | |||
322 | return (flags & LOCK_V40x02) ? | |||
323 | nlm4_blocked : nlm_blocked; | |||
324 | } else { | |||
325 | sigunlock(); | |||
326 | syslog(LOG_DEBUG7, "lock from %s.%" PRIu32"u" ": " | |||
327 | "already locked, failed", | |||
328 | lckarg->alock.caller_name, | |||
329 | lckarg->alock.svid); | |||
330 | lfree(newfl); | |||
331 | return (flags & LOCK_V40x02) ? | |||
332 | nlm4_denied : nlm_denied; | |||
333 | } | |||
334 | /* NOTREACHED */ | |||
335 | } | |||
336 | ||||
337 | /* no entry for this file yet; add to list */ | |||
338 | LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst)do { if (((newfl)->lcklst.le_next = (&lcklst_head)-> lh_first) != ((void *)0)) (&lcklst_head)->lh_first-> lcklst.le_prev = &(newfl)->lcklst.le_next; (&lcklst_head )->lh_first = (newfl); (newfl)->lcklst.le_prev = &( &lcklst_head)->lh_first; } while (0); | |||
339 | /* do the lock */ | |||
340 | retval = do_lock(newfl, lckarg->block); | |||
341 | switch (retval) { | |||
342 | case nlm4_granted: | |||
343 | /* case nlm_granted: is the same as nlm4_granted */ | |||
344 | case nlm4_blocked: | |||
345 | /* case nlm_blocked: is the same as nlm4_blocked */ | |||
346 | do_mon(lckarg->alock.caller_name); | |||
347 | break; | |||
348 | default: | |||
349 | lfree(newfl); | |||
350 | break; | |||
351 | } | |||
352 | sigunlock(); | |||
353 | return retval; | |||
354 | } | |||
355 | ||||
356 | /* unlock a filehandle */ | |||
357 | enum nlm_stats | |||
358 | unlock(nlm4_lock *lck, int flags) | |||
359 | { | |||
360 | struct file_lock *fl; | |||
361 | nfs_fhandle_t filehandle; | |||
362 | int err = (flags & LOCK_V40x02) ? nlm4_granted : nlm_granted; | |||
363 | ||||
364 | if (fhconv(&filehandle, &lck->fh)) { | |||
365 | syslog(LOG_NOTICE5, "fhconv failed (%m)"); | |||
366 | return (flags & LOCK_V40x02) ? nlm4_denied : nlm_denied; | |||
367 | } | |||
368 | siglock(); | |||
369 | LIST_FOREACH(fl, &lcklst_head, lcklst)for((fl) = ((&lcklst_head)->lh_first); (fl)!= ((void * )0); (fl) = ((fl)->lcklst.le_next)) { | |||
370 | if (strcmp(fl->client_name, lck->caller_name) || | |||
371 | fhcmp(&filehandle, &fl->filehandle) != 0 || | |||
372 | fl->client.oh.n_len != lck->oh.n_len || | |||
373 | memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes, | |||
374 | fl->client.oh.n_len) != 0 || | |||
375 | fl->client.svid != lck->svid) | |||
376 | continue; | |||
377 | /* Got it, unlock and remove from the queue */ | |||
378 | syslog(LOG_DEBUG7, "unlock from %s.%" PRIu32"u" ": found struct, " | |||
379 | "status %d", lck->caller_name, lck->svid, fl->status); | |||
380 | switch (fl->status) { | |||
381 | case LKST_LOCKED1: | |||
382 | err = do_unlock(fl); | |||
383 | break; | |||
384 | case LKST_WAITING2: | |||
385 | /* remove from the list */ | |||
386 | LIST_REMOVE(fl, lcklst)do { if ((fl)->lcklst.le_next != ((void *)0)) (fl)->lcklst .le_next->lcklst.le_prev = (fl)->lcklst.le_prev; *(fl)-> lcklst.le_prev = (fl)->lcklst.le_next; ; ; } while (0); | |||
387 | lfree(fl); | |||
388 | break; | |||
389 | case LKST_PROCESSING3: | |||
390 | /* | |||
391 | * being handled by a child; will clean up | |||
392 | * when the child exits | |||
393 | */ | |||
394 | fl->status = LKST_DYING4; | |||
395 | break; | |||
396 | case LKST_DYING4: | |||
397 | /* nothing to do */ | |||
398 | break; | |||
399 | default: | |||
400 | syslog(LOG_NOTICE5, "unknown status %d for %s", | |||
401 | fl->status, fl->client_name); | |||
402 | } | |||
403 | sigunlock(); | |||
404 | fhfree(&filehandle); | |||
405 | return err; | |||
406 | } | |||
407 | sigunlock(); | |||
408 | /* didn't find a matching entry; log anyway */ | |||
409 | syslog(LOG_NOTICE5, "no matching entry for %s", | |||
410 | lck->caller_name); | |||
411 | fhfree(&filehandle); | |||
412 | return (flags & LOCK_V40x02) ? nlm4_granted : nlm_granted; | |||
413 | } | |||
414 | ||||
415 | static struct file_lock * | |||
416 | lalloc(void) | |||
417 | { | |||
418 | return calloc(1, sizeof(struct file_lock)); | |||
419 | } | |||
420 | ||||
421 | void | |||
422 | lfree(struct file_lock *fl) | |||
423 | { | |||
424 | free(fl->addr); | |||
425 | free(fl->client.oh.n_bytes); | |||
426 | free(fl->client_cookie.n_bytes); | |||
427 | fhfree(&fl->filehandle); | |||
428 | free(fl); | |||
429 | } | |||
430 | ||||
431 | void | |||
432 | sigchild_handler(int sig) | |||
433 | { | |||
434 | int sstatus; | |||
435 | pid_t pid; | |||
436 | struct file_lock *fl; | |||
437 | ||||
438 | for (;;) { | |||
439 | pid = wait4(-1, &sstatus, WNOHANG0x01, NULL((void *)0)); | |||
440 | if (pid == -1) { | |||
441 | if (errno(*__errno()) != ECHILD10) | |||
442 | syslog(LOG_NOTICE5, "wait failed (%m)"); | |||
443 | else | |||
444 | syslog(LOG_DEBUG7, "wait failed (%m)"); | |||
445 | return; | |||
446 | } | |||
447 | if (pid == 0) { | |||
448 | /* no more child to handle yet */ | |||
449 | return; | |||
450 | } | |||
451 | /* | |||
452 | * if we're here we have a child that exited | |||
453 | * Find the associated file_lock. | |||
454 | */ | |||
455 | LIST_FOREACH(fl, &lcklst_head, lcklst)for((fl) = ((&lcklst_head)->lh_first); (fl)!= ((void * )0); (fl) = ((fl)->lcklst.le_next)) { | |||
456 | if (pid == fl->locker) | |||
457 | break; | |||
458 | } | |||
459 | if (fl == NULL((void *)0)) { | |||
460 | syslog(LOG_NOTICE5, "unknown child %d", pid); | |||
461 | } else { | |||
462 | /* protect from pid reusing. */ | |||
463 | fl->locker = 0; | |||
464 | if (!WIFEXITED(sstatus)(((sstatus) & 0177) == 0) || WEXITSTATUS(sstatus)(int)(((unsigned)(sstatus) >> 8) & 0xff) != 0) { | |||
465 | syslog(LOG_NOTICE5, "child %d failed", pid); | |||
466 | /* | |||
467 | * can't do much here; we can't reply | |||
468 | * anything but OK for blocked locks | |||
469 | * Eventually the client will time out | |||
470 | * and retry. | |||
471 | */ | |||
472 | do_unlock(fl); | |||
473 | return; | |||
474 | } | |||
475 | ||||
476 | /* check lock status */ | |||
477 | syslog(LOG_DEBUG7, "processing child %d, status %d", | |||
478 | pid, fl->status); | |||
479 | switch(fl->status) { | |||
480 | case LKST_PROCESSING3: | |||
481 | fl->status = LKST_LOCKED1; | |||
482 | send_granted(fl, (fl->flags & LOCK_V40x02) ? | |||
483 | nlm4_granted : nlm_granted); | |||
484 | break; | |||
485 | case LKST_DYING4: | |||
486 | do_unlock(fl); | |||
487 | break; | |||
488 | default: | |||
489 | syslog(LOG_NOTICE5, "bad lock status (%d) for" | |||
490 | " child %d", fl->status, pid); | |||
491 | } | |||
492 | } | |||
493 | } | |||
494 | } | |||
495 | ||||
496 | /* | |||
497 | * | |||
498 | * try to acquire the lock described by fl. Eventually fork a child to do a | |||
499 | * blocking lock if allowed and required. | |||
500 | */ | |||
501 | ||||
502 | enum nlm_stats | |||
503 | do_lock(struct file_lock *fl, int block) | |||
504 | { | |||
505 | int lflags, error; | |||
506 | struct stat st; | |||
507 | ||||
508 | fl->fd = fhopen((fhandle_t *)fl->filehandle.fhdata, O_RDWR0x0002); | |||
509 | if (fl->fd == -1) { | |||
510 | switch (errno(*__errno())) { | |||
511 | case ESTALE70: | |||
512 | error = nlm4_stale_fh; | |||
513 | break; | |||
514 | case EROFS30: | |||
515 | error = nlm4_rofs; | |||
516 | break; | |||
517 | default: | |||
518 | error = nlm4_failed; | |||
519 | } | |||
520 | if ((fl->flags & LOCK_V40x02) == 0) | |||
521 | error = nlm_denied; | |||
522 | syslog(LOG_NOTICE5, "fhopen failed (from %s) (%m)", | |||
523 | fl->client_name); | |||
524 | LIST_REMOVE(fl, lcklst)do { if ((fl)->lcklst.le_next != ((void *)0)) (fl)->lcklst .le_next->lcklst.le_prev = (fl)->lcklst.le_prev; *(fl)-> lcklst.le_prev = (fl)->lcklst.le_next; ; ; } while (0); | |||
525 | return error; | |||
526 | } | |||
527 | if (fstat(fl->fd, &st) == -1) { | |||
528 | syslog(LOG_NOTICE5, "fstat failed (from %s) (%m)", | |||
529 | fl->client_name); | |||
530 | } | |||
531 | syslog(LOG_DEBUG7, "lock from %s.%" PRIu32"u" " for file%s%s: " | |||
532 | "dev %u ino %llu (uid %d), flags %d", | |||
533 | fl->client_name, fl->client.svid, | |||
534 | fl->client.exclusive ? " (exclusive)":"", block ? " (block)":"", | |||
535 | st.st_dev, (unsigned long long)st.st_ino, st.st_uid, fl->flags); | |||
536 | lflags = LOCK_NB0x04; | |||
537 | if (fl->client.exclusive == 0) | |||
538 | lflags |= LOCK_SH0x01; | |||
539 | else | |||
540 | lflags |= LOCK_EX0x02; | |||
541 | error = flock(fl->fd, lflags); | |||
542 | if (error != 0 && errno(*__errno()) == EAGAIN35 && block) { | |||
543 | switch (fl->locker = fork()) { | |||
544 | case -1: /* fork failed */ | |||
545 | syslog(LOG_NOTICE5, "fork failed (%m)"); | |||
546 | LIST_REMOVE(fl, lcklst)do { if ((fl)->lcklst.le_next != ((void *)0)) (fl)->lcklst .le_next->lcklst.le_prev = (fl)->lcklst.le_prev; *(fl)-> lcklst.le_prev = (fl)->lcklst.le_next; ; ; } while (0); | |||
547 | close(fl->fd); | |||
548 | return (fl->flags & LOCK_V40x02) ? | |||
549 | nlm4_denied_nolock : nlm_denied_nolocks; | |||
550 | case 0: | |||
551 | /* | |||
552 | * Attempt a blocking lock. Will have to call | |||
553 | * NLM_GRANTED later. | |||
554 | */ | |||
555 | setproctitle("%s.%" PRIu32"u", | |||
556 | fl->client_name, fl->client.svid); | |||
557 | lflags &= ~LOCK_NB0x04; | |||
558 | if(flock(fl->fd, lflags) != 0) { | |||
559 | syslog(LOG_NOTICE5, "flock failed (%m)"); | |||
560 | _exit(1); | |||
561 | } | |||
562 | /* lock granted */ | |||
563 | _exit(0); | |||
564 | /*NOTREACHED*/ | |||
565 | default: | |||
566 | syslog(LOG_DEBUG7, "lock request from %s.%" PRIu32"u" ": " | |||
567 | "forked %d", | |||
568 | fl->client_name, fl->client.svid, fl->locker); | |||
569 | fl->status = LKST_PROCESSING3; | |||
570 | return (fl->flags & LOCK_V40x02) ? | |||
571 | nlm4_blocked : nlm_blocked; | |||
572 | } | |||
573 | } | |||
574 | /* non block case */ | |||
575 | if (error != 0) { | |||
576 | switch (errno(*__errno())) { | |||
577 | case EAGAIN35: | |||
578 | error = nlm4_denied; | |||
579 | break; | |||
580 | case ESTALE70: | |||
581 | error = nlm4_stale_fh; | |||
582 | break; | |||
583 | case EROFS30: | |||
584 | error = nlm4_rofs; | |||
585 | break; | |||
586 | default: | |||
587 | error = nlm4_failed; | |||
588 | } | |||
589 | if ((fl->flags & LOCK_V40x02) == 0) | |||
590 | error = nlm_denied; | |||
591 | if (errno(*__errno()) != EAGAIN35) | |||
592 | syslog(LOG_NOTICE5, "flock for %s failed (%m)", | |||
593 | fl->client_name); | |||
594 | else syslog(LOG_DEBUG7, "flock for %s failed (%m)", | |||
595 | fl->client_name); | |||
596 | LIST_REMOVE(fl, lcklst)do { if ((fl)->lcklst.le_next != ((void *)0)) (fl)->lcklst .le_next->lcklst.le_prev = (fl)->lcklst.le_prev; *(fl)-> lcklst.le_prev = (fl)->lcklst.le_next; ; ; } while (0); | |||
597 | close(fl->fd); | |||
598 | return error; | |||
599 | } | |||
600 | fl->status = LKST_LOCKED1; | |||
601 | return (fl->flags & LOCK_V40x02) ? nlm4_granted : nlm_granted; | |||
602 | } | |||
603 | ||||
604 | void | |||
605 | send_granted(struct file_lock *fl, int opcode) | |||
606 | { | |||
607 | CLIENT *cli; | |||
608 | static char dummy; | |||
609 | struct timeval timeo; | |||
610 | int success; | |||
611 | static struct nlm_res retval; | |||
612 | static struct nlm4_res retval4; | |||
613 | ||||
614 | cli = get_client(fl->addr, | |||
615 | (fl->flags & LOCK_V40x02) ? NLM_VERS4((u_long)4) : NLM_VERS((u_long)1)); | |||
616 | if (cli == NULL((void *)0)) { | |||
617 | syslog(LOG_NOTICE5, "failed to get CLIENT for %s.%" PRIu32"u", | |||
618 | fl->client_name, fl->client.svid); | |||
619 | /* | |||
620 | * We fail to notify remote that the lock has been granted. | |||
621 | * The client will timeout and retry, the lock will be | |||
622 | * granted at this time. | |||
623 | */ | |||
624 | return; | |||
625 | } | |||
626 | timeo.tv_sec = 0; | |||
627 | timeo.tv_usec = (fl->flags & LOCK_ASYNC0x01) ? 0 : 500000; /* 0.5s */ | |||
628 | ||||
629 | if (fl->flags & LOCK_V40x02) { | |||
630 | static nlm4_testargs result; | |||
631 | result.cookie = fl->client_cookie; | |||
632 | result.exclusive = fl->client.exclusive; | |||
633 | result.alock.caller_name = fl->client_name; | |||
634 | result.alock.fh.n_len = fl->filehandle.fhsize; | |||
635 | result.alock.fh.n_bytes = fl->filehandle.fhdata; | |||
636 | result.alock.oh = fl->client.oh; | |||
637 | result.alock.svid = fl->client.svid; | |||
638 | result.alock.l_offset = fl->client.l_offset; | |||
639 | result.alock.l_len = fl->client.l_len; | |||
640 | syslog(LOG_DEBUG7, "sending v4 reply%s", | |||
641 | (fl->flags & LOCK_ASYNC0x01) ? " (async)":""); | |||
642 | if (fl->flags & LOCK_ASYNC0x01) { | |||
643 | success = clnt_call(cli, NLM4_GRANTED_MSG,((*(cli)->cl_ops->cl_call)(cli, ((u_long)10), xdr_nlm4_testargs , (caddr_t)&result, xdr_void, (caddr_t)&dummy, timeo) ) | |||
644 | xdr_nlm4_testargs, &result, xdr_void, &dummy, timeo)((*(cli)->cl_ops->cl_call)(cli, ((u_long)10), xdr_nlm4_testargs , (caddr_t)&result, xdr_void, (caddr_t)&dummy, timeo) ); | |||
645 | } else { | |||
646 | success = clnt_call(cli, NLM4_GRANTED,((*(cli)->cl_ops->cl_call)(cli, ((u_long)5), xdr_nlm4_testargs , (caddr_t)&result, xdr_nlm4_res, (caddr_t)&retval4, timeo )) | |||
647 | xdr_nlm4_testargs, &result, xdr_nlm4_res,((*(cli)->cl_ops->cl_call)(cli, ((u_long)5), xdr_nlm4_testargs , (caddr_t)&result, xdr_nlm4_res, (caddr_t)&retval4, timeo )) | |||
648 | &retval4, timeo)((*(cli)->cl_ops->cl_call)(cli, ((u_long)5), xdr_nlm4_testargs , (caddr_t)&result, xdr_nlm4_res, (caddr_t)&retval4, timeo )); | |||
649 | } | |||
650 | } else { | |||
651 | static nlm_testargs result; | |||
652 | ||||
653 | result.cookie = fl->client_cookie; | |||
654 | result.exclusive = fl->client.exclusive; | |||
655 | result.alock.caller_name = fl->client_name; | |||
656 | result.alock.fh.n_len = fl->filehandle.fhsize; | |||
657 | result.alock.fh.n_bytes = fl->filehandle.fhdata; | |||
658 | result.alock.oh = fl->client.oh; | |||
659 | result.alock.svid = fl->client.svid; | |||
660 | result.alock.l_offset = | |||
661 | (unsigned int)fl->client.l_offset; | |||
662 | result.alock.l_len = | |||
663 | (unsigned int)fl->client.l_len; | |||
664 | syslog(LOG_DEBUG7, "sending v1 reply%s", | |||
665 | (fl->flags & LOCK_ASYNC0x01) ? " (async)":""); | |||
666 | if (fl->flags & LOCK_ASYNC0x01) { | |||
667 | success = clnt_call(cli, NLM_GRANTED_MSG,((*(cli)->cl_ops->cl_call)(cli, ((u_long)10), xdr_nlm_testargs , (caddr_t)&result, xdr_void, (caddr_t)&dummy, timeo) ) | |||
668 | xdr_nlm_testargs, &result, xdr_void, &dummy, timeo)((*(cli)->cl_ops->cl_call)(cli, ((u_long)10), xdr_nlm_testargs , (caddr_t)&result, xdr_void, (caddr_t)&dummy, timeo) ); | |||
669 | } else { | |||
670 | success = clnt_call(cli, NLM_GRANTED,((*(cli)->cl_ops->cl_call)(cli, ((u_long)5), xdr_nlm_testargs , (caddr_t)&result, xdr_nlm_res, (caddr_t)&retval, timeo )) | |||
671 | xdr_nlm_testargs, &result, xdr_nlm_res,((*(cli)->cl_ops->cl_call)(cli, ((u_long)5), xdr_nlm_testargs , (caddr_t)&result, xdr_nlm_res, (caddr_t)&retval, timeo )) | |||
672 | &retval, timeo)((*(cli)->cl_ops->cl_call)(cli, ((u_long)5), xdr_nlm_testargs , (caddr_t)&result, xdr_nlm_res, (caddr_t)&retval, timeo )); | |||
673 | } | |||
674 | } | |||
675 | if (debug_level > 2) | |||
676 | syslog(LOG_DEBUG7, "clnt_call returns %d(%s) for granted", | |||
677 | success, clnt_sperrno(success)); | |||
678 | ||||
679 | } | |||
680 | ||||
681 | enum nlm_stats | |||
682 | do_unlock(struct file_lock *rfl) | |||
683 | { | |||
684 | struct file_lock *fl; | |||
685 | int error; | |||
686 | int lockst; | |||
687 | ||||
688 | /* unlock the file: closing is enough ! */ | |||
689 | if (close(rfl->fd) == -1) { | |||
690 | if (errno(*__errno()) == ESTALE70) | |||
691 | error = nlm4_stale_fh; | |||
692 | else | |||
693 | error = nlm4_failed; | |||
694 | if ((rfl->flags & LOCK_V40x02) == 0) | |||
695 | error = nlm_denied; | |||
696 | syslog(LOG_NOTICE5, "close failed (from %s) (%m)", | |||
697 | rfl->client_name); | |||
698 | } else { | |||
699 | error = (rfl->flags & LOCK_V40x02) ? | |||
700 | nlm4_granted : nlm_granted; | |||
701 | } | |||
702 | LIST_REMOVE(rfl, lcklst)do { if ((rfl)->lcklst.le_next != ((void *)0)) (rfl)->lcklst .le_next->lcklst.le_prev = (rfl)->lcklst.le_prev; *(rfl )->lcklst.le_prev = (rfl)->lcklst.le_next; ; ; } while ( 0); | |||
703 | ||||
704 | /* process the next LKST_WAITING lock request for this fh */ | |||
705 | LIST_FOREACH(fl, &lcklst_head, lcklst)for((fl) = ((&lcklst_head)->lh_first); (fl)!= ((void * )0); (fl) = ((fl)->lcklst.le_next)) { | |||
706 | if (fl->status != LKST_WAITING2 || | |||
707 | fhcmp(&rfl->filehandle, &fl->filehandle) != 0) | |||
708 | continue; | |||
709 | ||||
710 | lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */ | |||
711 | switch (lockst) { | |||
712 | case nlm4_granted: | |||
713 | /* case nlm_granted: same as nlm4_granted */ | |||
714 | send_granted(fl, (fl->flags & LOCK_V40x02) ? | |||
715 | nlm4_granted : nlm_granted); | |||
716 | break; | |||
717 | case nlm4_blocked: | |||
718 | /* case nlm_blocked: same as nlm4_blocked */ | |||
719 | break; | |||
720 | default: | |||
721 | lfree(fl); | |||
722 | break; | |||
723 | } | |||
724 | break; | |||
725 | } | |||
726 | lfree(rfl); | |||
| ||||
727 | return error; | |||
728 | } | |||
729 | ||||
730 | void | |||
731 | siglock(void) | |||
732 | { | |||
733 | sigset_t block; | |||
734 | ||||
735 | sigemptyset(&block); | |||
736 | sigaddset(&block, SIGCHLD20); | |||
737 | ||||
738 | if (sigprocmask(SIG_BLOCK1, &block, NULL((void *)0)) == -1) { | |||
739 | syslog(LOG_WARNING4, "siglock failed (%m)"); | |||
740 | } | |||
741 | } | |||
742 | ||||
743 | void | |||
744 | sigunlock(void) | |||
745 | { | |||
746 | sigset_t block; | |||
747 | ||||
748 | sigemptyset(&block); | |||
749 | sigaddset(&block, SIGCHLD20); | |||
750 | ||||
751 | if (sigprocmask(SIG_UNBLOCK2, &block, NULL((void *)0)) == -1) { | |||
752 | syslog(LOG_WARNING4, "sigunlock failed (%m)"); | |||
753 | } | |||
754 | } | |||
755 | ||||
756 | /* monitor a host through rpc.statd, and keep a ref count */ | |||
757 | void | |||
758 | do_mon(const char *hostname) | |||
759 | { | |||
760 | static char localhost[] = "localhost"; | |||
761 | struct host *hp; | |||
762 | struct mon my_mon; | |||
763 | struct sm_stat_res result; | |||
764 | int retval; | |||
765 | ||||
766 | LIST_FOREACH(hp, &hostlst_head, hostlst)for((hp) = ((&hostlst_head)->lh_first); (hp)!= ((void * )0); (hp) = ((hp)->hostlst.le_next)) { | |||
767 | if (strcmp(hostname, hp->name) == 0) { | |||
768 | /* already monitored, just bump refcnt */ | |||
769 | hp->refcnt++; | |||
770 | return; | |||
771 | } | |||
772 | } | |||
773 | /* not found, have to create an entry for it */ | |||
774 | hp = malloc(sizeof(struct host)); | |||
775 | if (hp == NULL((void *)0)) { | |||
776 | syslog(LOG_WARNING4, "can't monitor host %s (%m)", hostname); | |||
777 | return; | |||
778 | } | |||
779 | strlcpy(hp->name, hostname, sizeof(hp->name)); | |||
780 | hp->refcnt = 1; | |||
781 | syslog(LOG_DEBUG7, "monitoring host %s", hostname); | |||
782 | memset(&my_mon, 0, sizeof(my_mon)); | |||
783 | my_mon.mon_id.mon_name = hp->name; | |||
784 | my_mon.mon_id.my_id.my_name = localhost; | |||
785 | my_mon.mon_id.my_id.my_prog = NLM_PROG((u_long)100021); | |||
786 | my_mon.mon_id.my_id.my_vers = NLM_SM((u_long)0); | |||
787 | my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY((u_long)1); | |||
788 | if ((retval = callrpc(localhost, SM_PROG((u_long)100024), SM_VERS((u_long)1), SM_MON((u_long)2), xdr_mon, | |||
789 | (void *)&my_mon, xdr_sm_stat_res, (void *)&result)) != 0) { | |||
790 | syslog(LOG_WARNING4, "rpc to statd failed (%s)", | |||
791 | clnt_sperrno((enum clnt_stat)retval)); | |||
792 | free(hp); | |||
793 | return; | |||
794 | } | |||
795 | if (result.res_stat == stat_fail) { | |||
796 | syslog(LOG_WARNING4, "statd failed"); | |||
797 | free(hp); | |||
798 | return; | |||
799 | } | |||
800 | LIST_INSERT_HEAD(&hostlst_head, hp, hostlst)do { if (((hp)->hostlst.le_next = (&hostlst_head)-> lh_first) != ((void *)0)) (&hostlst_head)->lh_first-> hostlst.le_prev = &(hp)->hostlst.le_next; (&hostlst_head )->lh_first = (hp); (hp)->hostlst.le_prev = &(& hostlst_head)->lh_first; } while (0); | |||
801 | } | |||
802 | ||||
803 | void | |||
804 | notify(const char *hostname, int state) | |||
805 | { | |||
806 | struct file_lock *fl, *next_fl; | |||
807 | int err; | |||
808 | syslog(LOG_DEBUG7, "notify from %s, new state %d", hostname, state); | |||
809 | /* search all lock for this host; if status changed, release the lock */ | |||
810 | siglock(); | |||
811 | for (fl = LIST_FIRST(&lcklst_head)((&lcklst_head)->lh_first); fl != NULL((void *)0); fl = next_fl) { | |||
| ||||
812 | next_fl = LIST_NEXT(fl, lcklst)((fl)->lcklst.le_next); | |||
813 | if (strcmp(hostname, fl->client_name) == 0 && | |||
814 | fl->nsm_status != state) { | |||
815 | syslog(LOG_DEBUG7, "state %d, nsm_state %d, unlocking", | |||
816 | fl->status, fl->nsm_status); | |||
817 | switch(fl->status) { | |||
818 | case LKST_LOCKED1: | |||
819 | err = do_unlock(fl); | |||
820 | if (err != nlm_granted) | |||
821 | syslog(LOG_DEBUG7, | |||
822 | "notify: unlock failed for %s (%d)", | |||
823 | hostname, err); | |||
824 | break; | |||
825 | case LKST_WAITING2: | |||
826 | LIST_REMOVE(fl, lcklst)do { if ((fl)->lcklst.le_next != ((void *)0)) (fl)->lcklst .le_next->lcklst.le_prev = (fl)->lcklst.le_prev; *(fl)-> lcklst.le_prev = (fl)->lcklst.le_next; ; ; } while (0); | |||
827 | lfree(fl); | |||
828 | break; | |||
829 | case LKST_PROCESSING3: | |||
830 | fl->status = LKST_DYING4; | |||
831 | break; | |||
832 | case LKST_DYING4: | |||
833 | break; | |||
834 | default: | |||
835 | syslog(LOG_NOTICE5, "unknown status %d for %s", | |||
836 | fl->status, fl->client_name); | |||
837 | } | |||
838 | } | |||
839 | } | |||
840 | sigunlock(); | |||
841 | } |