Commit e8674181 e86741813eb537dc19491a9f69f8ebc12815ac0e by Sergey Poznyakoff

Rewrite

1 parent 525ec621
1 /* GNU Mailutils -- a suite of utilities for electronic mail 1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2005 Free Software Foundation, Inc. 2 Copyright (C) 1999, 2000, 2001, 2005, 2007 Free Software Foundation, Inc.
3 3
4 This library is free software; you can redistribute it and/or 4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public 5 modify it under the terms of the GNU Lesser General Public
...@@ -47,34 +47,53 @@ enum mu_locker_set_mode ...@@ -47,34 +47,53 @@ enum mu_locker_set_mode
47 47
48 /* mu_locker_create() flags */ 48 /* mu_locker_create() flags */
49 49
50 #define MU_LOCKER_SIMPLE 0x00 50 /* Locker types */
51
52 #define MU_LOCKER_TYPE_DOTLOCK 0
53 #define MU_LOCKER_TYPE_EXTERNAL 1
54 /* Use an external program to lock the file. This is necessary
55 for programs having permission to access a file, but do not
56 have write permission on the directory that contains that file. */
57 #define MU_LOCKER_TYPE_KERNEL 2
58 /* Use kernel locking (flock, lockf or ioctl) */
59 #define MU_LOCKER_TYPE_NULL 3
60 /* Special locker type: means no lock. This is to be used with
61 temporary mailboxes stored in memory. */
62
63 #define MU_LOCKER_TYPE_TO_FLAG(t) ((t) << 8)
64 #define MU_LOCKER_FLAG_TO_TYPE(f) ((f) >> 8)
65 #define MU_LOCKER_IS_TYPE(f,t) (MU_LOCKER_FLAG_TO_TYPE(f) == (t))
66 #define MU_LOCKER_SET_TYPE(f,t) ((f) = MU_LOCKER_TYPE_TO_FLAG(t) | MU_LOCKER_OPTIONS(f))
67 #define MU_LOCKER_TYPE_MASK 0xff00
68 #define MU_LOCKER_OPTION_MASK 0x00ff
69 #define MU_LOCKER_OPTIONS(f) ((f) & MU_LOCKER_OPTION_MASK)
70
71 #define MU_LOCKER_NULL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_NULL)
72 #define MU_LOCKER_DOTLOCK MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_DOTLOCK)
73 #define MU_LOCKER_EXTERNAL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_EXTERNAL)
74 #define MU_LOCKER_KERNEL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_KERNEL)
75
76 /* Options */
77
78 #define MU_LOCKER_SIMPLE 0x0000
51 /* Just try and dotlock the file, not the default because its usually 79 /* Just try and dotlock the file, not the default because its usually
52 better to retry. */ 80 better to retry. */
53 #define MU_LOCKER_RETRY 0x01 81 #define MU_LOCKER_RETRY 0x0001
54 /* This requests that we loop retries times, sleeping retry_sleep 82 /* This requests that we loop retries times, sleeping retry_sleep
55 seconds in between trying to obtain the lock before failing with 83 seconds in between trying to obtain the lock before failing with
56 MU_LOCK_CONFLICT. */ 84 MU_LOCK_CONFLICT. */
57 #define MU_LOCKER_TIME 0x02 85 #define MU_LOCKER_TIME 0x0002
58 /* This mode checks the last update time of the lock, then removes 86 /* This mode checks the last update time of the lock, then removes
59 it if older than MU_LOCKER_EXPIRE_TIME. If a client uses this, 87 it if older than MU_LOCKER_EXPIRE_TIME. If a client uses this,
60 then the servers better periodically update the lock on the 88 then the servers better periodically update the lock on the
61 file... do they? */ 89 file... do they? */
62 #define MU_LOCKER_PID 0x04 90 #define MU_LOCKER_PID 0x0004
63 /* PID locking is only useful for programs that aren't using 91 /* PID locking is only useful for programs that aren't using
64 an external dotlocker, non-setgid programs will use a dotlocker, 92 an external dotlocker, non-setgid programs will use a dotlocker,
65 which locks and exits imediately. This is a protection against 93 which locks and exits imediately. This is a protection against
66 a server crashing, it's not generally useful. */ 94 a server crashing, it's not generally useful. */
67 #define MU_LOCKER_EXTERNAL 0x08
68 /* Use an external program to lock the file. This is necessary
69 for programs having permission to access a file, but do not
70 have write permission on the directory that contains that file. */
71 #define MU_LOCKER_NULL 0x10
72 /* Special locker type: means no lock. This is to be used with
73 temporary mailboxes stored in memory. */
74 #define MU_LOCKER_KERNEL 0x20
75 /* Use kernel locking (flock, lockf or ioctl) */
76 95
77 #define MU_LOCKER_DEFAULT (MU_LOCKER_RETRY) 96 #define MU_LOCKER_DEFAULT (MU_LOCKER_DOTLOCK | MU_LOCKER_RETRY)
78 97
79 /* Use these flags for as the default locker flags (the default defaults 98 /* Use these flags for as the default locker flags (the default defaults
80 * to MU_LOCKER_DEFAULT). A flags of 0 resets the flags back to the 99 * to MU_LOCKER_DEFAULT). A flags of 0 resets the flags back to the
...@@ -104,6 +123,14 @@ extern int mu_locker_get_retries (mu_locker_t, int*); ...@@ -104,6 +123,14 @@ extern int mu_locker_get_retries (mu_locker_t, int*);
104 extern int mu_locker_get_retry_sleep (mu_locker_t, int*); 123 extern int mu_locker_get_retry_sleep (mu_locker_t, int*);
105 extern int mu_locker_get_external (mu_locker_t, char**); 124 extern int mu_locker_get_external (mu_locker_t, char**);
106 125
126 enum mu_locker_mode
127 {
128 mu_lck_shr, /* Shared (advisory) lock */
129 mu_lck_exc, /* Exclusive lock */
130 mu_lck_opt /* Optional lock = shared, if the locker supports it, no
131 locking otherwise */
132 };
133
107 extern int mu_locker_lock (mu_locker_t); 134 extern int mu_locker_lock (mu_locker_t);
108 extern int mu_locker_touchlock (mu_locker_t); 135 extern int mu_locker_touchlock (mu_locker_t);
109 extern int mu_locker_unlock (mu_locker_t); 136 extern int mu_locker_unlock (mu_locker_t);
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail 1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2005, 2006 Free Software Foundation, Inc. 2 Copyright (C) 1999, 2000, 2001, 2005, 2006,
3 2007 Free Software Foundation, Inc.
3 4
4 This library is free software; you can redistribute it and/or 5 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public 6 modify it under the terms of the GNU Lesser General Public
...@@ -49,7 +50,8 @@ ...@@ -49,7 +50,8 @@
49 50
50 struct _mu_locker 51 struct _mu_locker
51 { 52 {
52 int refcnt; 53 unsigned refcnt; /* Number of times mu_locker_lock was called */
54 enum mu_locker_mode mode; /* Current locking mode (if refcnt > 0) */
53 55
54 char *file; 56 char *file;
55 int flags; 57 int flags;
...@@ -73,20 +75,121 @@ struct _mu_locker ...@@ -73,20 +75,121 @@ struct _mu_locker
73 } data; 75 } data;
74 }; 76 };
75 77
76 /* Assert that we're managing the refcnt and fd correctly, either 78 #define MU_LOCKER_TYPE(l) MU_LOCKER_FLAG_TO_TYPE((l)->flags)
77 * we have a lock, and the fd is valid, or refcnt is 0 and fd is -1. 79
78 * And refcnt can never be less than 0. 80 struct locker_tab
79 */ 81 {
80 #define INVARIANT(l) assert((l)->refcnt >= 0); 82 int (*init) (mu_locker_t);
81 83 void (*destroy) (mu_locker_t);
82 static void expire_stale_lock (mu_locker_t lock); 84 int (*prelock) (mu_locker_t);
83 static int stat_check (const char *file, int fd, int links); 85 int (*lock) (mu_locker_t, enum mu_locker_mode);
84 static int check_file_permissions (const char *file); 86 int (*unlock) (mu_locker_t);
85 static int lock_external (mu_locker_t l, int lock); 87 };
86 static int _locker_lock_dotlock (mu_locker_t lock); 88
87 static int _locker_unlock_dotlock (mu_locker_t lock); 89 static int init_dotlock (mu_locker_t);
88 static int _locker_lock_kernel (mu_locker_t lock); 90 static void destroy_dotlock (mu_locker_t);
89 static int _locker_unlock_kernel (mu_locker_t lock); 91 static int lock_dotlock (mu_locker_t, enum mu_locker_mode);
92 static int unlock_dotlock (mu_locker_t);
93
94 static int init_external (mu_locker_t);
95 static void destroy_external (mu_locker_t);
96 static int lock_external (mu_locker_t, enum mu_locker_mode);
97 static int unlock_external (mu_locker_t);
98
99 static int init_kernel (mu_locker_t);
100 static int lock_kernel (mu_locker_t, enum mu_locker_mode);
101 static int unlock_kernel (mu_locker_t);
102
103 static int prelock_common (mu_locker_t);
104
105 static struct locker_tab locker_tab[] = {
106 { init_dotlock, destroy_dotlock, prelock_common,
107 lock_dotlock, unlock_dotlock },
108 { init_external, destroy_external, prelock_common,
109 lock_external, unlock_external },
110 { init_kernel, NULL, NULL, lock_kernel, unlock_kernel },
111 { NULL, NULL, NULL, NULL, NULL }
112 };
113
114 #define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))
115
116
117 static int
118 stat_check (const char *file, int fd, int links)
119 {
120 struct stat fn_stat;
121 struct stat fd_stat;
122 int err = 0;
123 int localfd = -1;
124
125 if (fd == -1)
126 {
127 localfd = open (file, O_RDONLY);
128
129 if (localfd == -1)
130 return errno;
131 fd = localfd;
132 }
133
134 /* We should always be able to stat a valid fd, so this
135 is an error condition. */
136 if (lstat (file, &fn_stat) || fstat (fd, &fd_stat))
137 err = errno;
138 else
139 {
140 /* If the link and stat don't report the same info, or the
141 file is a symlink, fail the locking. */
142 #define CHK(X) if(X) err = EINVAL
143
144 CHK (!S_ISREG (fn_stat.st_mode));
145 CHK (!S_ISREG (fd_stat.st_mode));
146 CHK (fn_stat.st_nlink != links);
147 CHK (fn_stat.st_dev != fd_stat.st_dev);
148 CHK (fn_stat.st_ino != fd_stat.st_ino);
149 CHK (fn_stat.st_mode != fd_stat.st_mode);
150 CHK (fn_stat.st_nlink != fd_stat.st_nlink);
151 CHK (fn_stat.st_uid != fd_stat.st_uid);
152 CHK (fn_stat.st_gid != fd_stat.st_gid);
153 CHK (fn_stat.st_rdev != fd_stat.st_rdev);
154
155 #undef CHK
156 }
157 if (localfd != -1)
158 close (localfd);
159
160 return err;
161 }
162
163 static int
164 check_file_permissions (const char *file)
165 {
166 int fd = -1;
167 int err = 0;
168
169 if ((fd = open (file, O_RDONLY)) == -1)
170 return errno == ENOENT ? 0 : errno;
171
172 err = stat_check (file, fd, 1);
173 close (fd);
174 fd = -1;
175 if (err)
176 {
177 if (err == EINVAL)
178 err = MU_ERR_LOCK_BAD_FILE;
179 return err;
180 }
181
182 return 0;
183 }
184
185 static int
186 prelock_common (mu_locker_t locker)
187 {
188 /* Check if we are trying to lock a regular file, with a link count
189 of 1, that we have permission to read, etc., or don't lock it. */
190 return check_file_permissions (locker->file);
191 }
192
90 193
91 static int mu_locker_default_flags = MU_LOCKER_DEFAULT; 194 static int mu_locker_default_flags = MU_LOCKER_DEFAULT;
92 static time_t mu_locker_retry_timeout = MU_LOCKER_RETRY_SLEEP; 195 static time_t mu_locker_retry_timeout = MU_LOCKER_RETRY_SLEEP;
...@@ -143,115 +246,36 @@ mu_locker_set_default_external_program (char *path) ...@@ -143,115 +246,36 @@ mu_locker_set_default_external_program (char *path)
143 } 246 }
144 247
145 int 248 int
146 mu_locker_create (mu_locker_t *plocker, const char *filename_, int flags) 249 mu_locker_set_flags (mu_locker_t locker, int flags)
147 { 250 {
148 mu_locker_t l; 251 unsigned otype, ntype;
149 char filename[_POSIX_PATH_MAX];
150 int err = 0;
151 252
152 if (plocker == NULL) 253 if (!locker)
153 return MU_ERR_OUT_PTR_NULL; 254 return MU_ERR_LOCKER_NULL;
154 255
155 if (filename_ == NULL) 256 otype = MU_LOCKER_TYPE (locker);
257 if (otype >= MU_LOCKER_NTYPES)
258 return EINVAL;
259 ntype = MU_LOCKER_FLAG_TO_TYPE (flags);
260 if (ntype >= MU_LOCKER_NTYPES)
156 return EINVAL; 261 return EINVAL;
157 262
158 if((err = mu_unroll_symlink(filename, sizeof(filename), filename_))) 263 if (ntype != otype)
159 return err;
160
161 l = calloc (1, sizeof (*l));
162
163 if (l == NULL)
164 return ENOMEM;
165
166 l->file = strdup(filename);
167
168 if (l->file == NULL)
169 {
170 free (l);
171 return ENOMEM;
172 }
173
174 if (strcmp (filename, "/dev/null") == 0)
175 l->flags = MU_LOCKER_NULL;
176 else if (flags)
177 l->flags = flags;
178 else
179 l->flags = mu_locker_default_flags;
180
181 l->expire_time = mu_locker_expire_timeout;
182 l->retries = mu_locker_retry_count;
183 l->retry_sleep = mu_locker_retry_timeout;
184
185 /* Initialize locker-type-specific data */
186 if (l->flags & MU_LOCKER_EXTERNAL)
187 {
188 if (!(l->data.external.name = strdup (mu_locker_external_program ?
189 mu_locker_external_program :
190 MU_LOCKER_EXTERNAL_PROGRAM)))
191 { 264 {
192 mu_locker_destroy (&l); 265 int rc;
193 return ENOMEM;
194 }
195 }
196 else if (!(l->flags & MU_LOCKER_KERNEL))
197 {
198 l->data.dot.dotlock = malloc (strlen (l->file)
199 + 5 /*strlen(".lock")*/ + 1);
200 266
201 if (!l->data.dot.dotlock) 267 if (locker_tab[otype].destroy)
268 locker_tab[otype].destroy (locker);
269 locker->flags = flags;
270 if (locker_tab[otype].init)
202 { 271 {
203 free (l->file); 272 rc = locker_tab[otype].init (locker);
204 free (l); 273 if (rc)
205 return ENOMEM; 274 locker->flags = MU_LOCKER_NULL;
275 return rc;
206 } 276 }
207
208 sprintf (l->data.dot.dotlock, "%s.lock", l->file);
209 } 277 }
210
211 INVARIANT(l);
212
213 *plocker = l;
214
215 return 0;
216 }
217
218 void
219 _locker_destroy_private (mu_locker_t locker)
220 {
221 if (locker)
222 {
223 if (locker->flags & MU_LOCKER_EXTERNAL)
224 free (locker->data.external.name);
225 else if (locker->flags & MU_LOCKER_KERNEL)
226 /* nothing */;
227 else 278 else
228 {
229 free (locker->data.dot.dotlock);
230 locker->data.dot.dotlock = NULL;
231 free (locker->data.dot.nfslock);
232 locker->data.dot.nfslock = NULL;
233 }
234 }
235 }
236
237 void
238 mu_locker_destroy (mu_locker_t *plocker)
239 {
240 if (plocker && *plocker)
241 {
242 _locker_destroy_private (*plocker);
243 free ((*plocker)->file);
244 free (*plocker);
245 *plocker = NULL;
246 }
247 }
248
249 int
250 mu_locker_set_flags (mu_locker_t locker, int flags)
251 {
252 if (!locker)
253 return MU_ERR_LOCKER_NULL;
254
255 locker->flags = flags; 279 locker->flags = flags;
256 280
257 return 0; 281 return 0;
...@@ -306,7 +330,7 @@ mu_locker_set_external (mu_locker_t locker, const char* program) ...@@ -306,7 +330,7 @@ mu_locker_set_external (mu_locker_t locker, const char* program)
306 330
307 if (!locker) 331 if (!locker)
308 return MU_ERR_LOCKER_NULL; 332 return MU_ERR_LOCKER_NULL;
309 if (!(locker->flags & MU_LOCKER_EXTERNAL)) 333 if (MU_LOCKER_TYPE (locker) != MU_LOCKER_TYPE_EXTERNAL)
310 return EINVAL; 334 return EINVAL;
311 335
312 /* program can be NULL */ 336 /* program can be NULL */
...@@ -329,7 +353,7 @@ mu_locker_get_flags (mu_locker_t locker, int *flags) ...@@ -329,7 +353,7 @@ mu_locker_get_flags (mu_locker_t locker, int *flags)
329 if (!locker) 353 if (!locker)
330 return MU_ERR_LOCKER_NULL; 354 return MU_ERR_LOCKER_NULL;
331 355
332 if(!flags) 356 if (!flags)
333 return EINVAL; 357 return EINVAL;
334 358
335 *flags = locker->flags; 359 *flags = locker->flags;
...@@ -380,144 +404,162 @@ mu_locker_get_retry_sleep (mu_locker_t locker, int *retry_sleep) ...@@ -380,144 +404,162 @@ mu_locker_get_retry_sleep (mu_locker_t locker, int *retry_sleep)
380 } 404 }
381 405
382 int 406 int
383 mu_locker_lock (mu_locker_t lock) 407 mu_locker_create (mu_locker_t *plocker, const char *fname, int flags)
384 { 408 {
385 int rc; 409 unsigned type;
386 int retries = 1; 410 mu_locker_t l;
411 char filename[_POSIX_PATH_MAX];
412 int err = 0;
387 413
388 if (lock == NULL) 414 if (plocker == NULL)
415 return MU_ERR_OUT_PTR_NULL;
416
417 if (fname == NULL)
389 return EINVAL; 418 return EINVAL;
390 419
391 if (lock->flags == MU_LOCKER_NULL) 420 if ((err = mu_unroll_symlink (filename, sizeof (filename), fname)))
392 return 0; 421 return err;
393 422
394 INVARIANT (lock) 423 l = calloc (1, sizeof (*l));
395 /* Is the lock already applied? */ 424
396 if (lock->refcnt > 0) 425 if (l == NULL)
426 return ENOMEM;
427
428 l->file = strdup (filename);
429
430 if (l->file == NULL)
397 { 431 {
398 lock->refcnt++; 432 free (l);
399 return 0; 433 return ENOMEM;
400 } 434 }
401 435
402 if (access (lock->file, W_OK)) 436 if (strcmp (filename, "/dev/null") == 0)
437 l->flags = MU_LOCKER_NULL;
438 else if (flags)
439 l->flags = flags;
440 else
441 l->flags = mu_locker_default_flags;
442
443 l->expire_time = mu_locker_expire_timeout;
444 l->retries = mu_locker_retry_count;
445 l->retry_sleep = mu_locker_retry_timeout;
446
447 type = MU_LOCKER_TYPE (l);
448
449 if (type >= MU_LOCKER_NTYPES)
403 { 450 {
404 /* There is no use trying to lock the file if we are not 451 free (l->file);
405 allowed to write to it */ 452 return EINVAL;
406 _locker_destroy_private (lock);
407 lock->flags |= MU_LOCKER_NULL;
408 return 0;
409 } 453 }
410 454
411 /* Check we are trying to lock a regular file, with a link count 455 /* Initialize locker-type-specific data */
412 of 1, that we have permission to read, etc., or don't lock it. */ 456 err = locker_tab[type].init ? locker_tab[type].init (l) : 0;
413 if ((rc = check_file_permissions(lock->file))) 457 if (err)
414 return rc;
415
416 /* Do the lock with an external program, if requested. */
417 if (lock->flags & MU_LOCKER_EXTERNAL)
418 return lock_external (lock, 1);
419 else if (!(lock->flags & MU_LOCKER_KERNEL))
420 { 458 {
421 char *tmp, *p; 459 mu_locker_destroy (&l);
460 return err;
461 }
422 462
423 tmp = strdup (lock->file); 463 *plocker = l;
424 if (!tmp)
425 return ENOMEM;
426 464
427 strcpy (tmp, lock->file); 465 return 0;
428 p = strrchr (tmp, '/'); 466 }
429 if (!p) 467
468 void
469 mu_locker_destroy (mu_locker_t *plocker)
470 {
471 if (plocker && *plocker)
430 { 472 {
431 free (tmp); 473 unsigned type = MU_LOCKER_TYPE (*plocker);
432 tmp = strdup ("."); 474 if (type < MU_LOCKER_NTYPES)
433 if (!tmp) 475 {
434 return ENOMEM; 476 if (locker_tab[type].destroy)
477 locker_tab[type].destroy (*plocker);
478 free ((*plocker)->file);
479 free (*plocker);
480 *plocker = NULL;
435 } 481 }
436 else 482 }
437 *p = 0; 483 }
438 484
439 if (access (tmp, W_OK)) 485 int
486 _mu_locker_lock (mu_locker_t lock, enum mu_locker_mode mode)
487 {
488 int rc;
489 unsigned type;
490 unsigned retries = 1;
491
492 if (lock == NULL || (type = MU_LOCKER_TYPE (lock)) >= MU_LOCKER_NTYPES)
493 return EINVAL;
494
495 if (locker_tab[type].prelock && (rc = locker_tab[type].prelock (lock)))
496 return rc;
497
498 /* Is the lock already applied? */
499 if (lock->refcnt > 0)
440 { 500 {
441 /* Fallback to kernel locking */ 501 lock->refcnt++;
442 _locker_destroy_private (lock); 502 if (mode == lock->mode)
443 lock->flags |= MU_LOCKER_KERNEL; 503 return 0;
444 }
445 free (tmp);
446 } 504 }
447 505
506 lock->mode = mode;
507
448 if (lock->flags & MU_LOCKER_RETRY) 508 if (lock->flags & MU_LOCKER_RETRY)
449 {
450 retries = lock->retries; 509 retries = lock->retries;
451 }
452 510
511 if (locker_tab[type].lock)
512 {
453 while (retries--) 513 while (retries--)
454 { 514 {
455 if (lock->flags & MU_LOCKER_KERNEL) 515 rc = locker_tab[type].lock (lock, mode);
456 rc = _locker_lock_kernel (lock);
457 else
458 rc = _locker_lock_dotlock (lock);
459 if (rc == EAGAIN && retries) 516 if (rc == EAGAIN && retries)
517 {
460 sleep (lock->retry_sleep); 518 sleep (lock->retry_sleep);
461 else if (rc) 519 continue;
462 return rc;
463 else /* rc == 0 */
464 break;
465 } 520 }
466 521
467 lock->refcnt = 1; 522 if (rc == 0)
523 lock->refcnt++;
468 524
469 return 0; 525 break;
526 }
527 }
528 else
529 rc = 0;
530
531 return rc;
470 } 532 }
471 533
472 int 534 int
473 mu_locker_touchlock (mu_locker_t lock) 535 mu_locker_lock (mu_locker_t lock)
474 { 536 {
475 if (!lock) 537 return _mu_locker_lock (lock, mu_lck_exc);
476 return MU_ERR_LOCKER_NULL;
477
478 if (lock->flags == MU_LOCKER_NULL)
479 return 0;
480
481 if (lock->refcnt > 0)
482 return utime (lock->data.dot.dotlock, NULL);
483
484 return MU_ERR_LOCK_NOT_HELD;
485 } 538 }
486 539
487 int 540 int
488 mu_locker_unlock (mu_locker_t lock) 541 mu_locker_unlock (mu_locker_t lock)
489 { 542 {
490 int rc = 0; 543 int rc = 0;
544 unsigned type;
491 545
492 if (!lock) 546 if (!lock)
493 return MU_ERR_LOCKER_NULL; 547 return MU_ERR_LOCKER_NULL;
494 548
495 if (lock->flags == MU_LOCKER_NULL)
496 return 0;
497
498 if (lock->refcnt == 0) 549 if (lock->refcnt == 0)
499 return MU_ERR_LOCK_NOT_HELD; 550 return MU_ERR_LOCK_NOT_HELD;
500 551
501 /* Do the lock with an external program, if requested. */
502 if (lock->flags & MU_LOCKER_EXTERNAL)
503 return lock_external (lock, 0);
504
505 if (lock->refcnt > 1)
506 {
507 lock->refcnt--;
508 return 0;
509 }
510
511 if ((rc = check_file_permissions (lock->file))) 552 if ((rc = check_file_permissions (lock->file)))
512 return rc; 553 return rc;
513 554
514 if (lock->flags & MU_LOCKER_KERNEL) 555 if (--lock->refcnt > 0)
515 rc = _locker_unlock_kernel (lock); 556 return 0;
516 else
517 rc = _locker_unlock_dotlock (lock);
518 557
519 if (rc == 0) 558 type = MU_LOCKER_TYPE (lock);
520 lock->refcnt = 0; 559 if (locker_tab[type].unlock)
560 rc = locker_tab[type].unlock (lock);
561 else
562 rc = 0;
521 563
522 return rc; 564 return rc;
523 } 565 }
...@@ -525,21 +567,17 @@ mu_locker_unlock (mu_locker_t lock) ...@@ -525,21 +567,17 @@ mu_locker_unlock (mu_locker_t lock)
525 int 567 int
526 mu_locker_remove_lock (mu_locker_t lock) 568 mu_locker_remove_lock (mu_locker_t lock)
527 { 569 {
528 int err;
529
530 if (!lock) 570 if (!lock)
531 return MU_ERR_LOCKER_NULL; 571 return MU_ERR_LOCKER_NULL;
532 572
533 if (lock->flags == MU_LOCKER_NULL)
534 return 0;
535
536 /* Force the reference count to 1 to unlock the file. */ 573 /* Force the reference count to 1 to unlock the file. */
537 lock->refcnt = 1; 574 lock->refcnt = 1;
538 err = mu_locker_unlock (lock); 575 return mu_locker_unlock (lock);
539
540 return err;
541 } 576 }
542 577
578
579 #define DOTLOCK_SUFFIX ".lock"
580
543 /* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */ 581 /* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */
544 static void 582 static void
545 expire_stale_lock (mu_locker_t lock) 583 expire_stale_lock (mu_locker_t lock)
...@@ -587,80 +625,60 @@ expire_stale_lock (mu_locker_t lock) ...@@ -587,80 +625,60 @@ expire_stale_lock (mu_locker_t lock)
587 } 625 }
588 626
589 static int 627 static int
590 stat_check (const char *file, int fd, int links) 628 init_dotlock (mu_locker_t locker)
591 { 629 {
592 struct stat fn_stat; 630 char *tmp, *p;
593 struct stat fd_stat;
594 int err = 0;
595 int localfd = -1;
596 631
597 if (fd == -1) 632 /* Make sure the spool directory is writable */
598 { 633 tmp = strdup (locker->file);
599 localfd = open(file, O_RDONLY); 634 if (!tmp)
635 return ENOMEM;
600 636
601 if (localfd == -1) 637 strcpy (tmp, locker->file);
602 return errno; 638 p = strrchr (tmp, '/');
603 fd = localfd; 639 if (!p)
640 {
641 free (tmp);
642 tmp = strdup (".");
643 if (!tmp)
644 return ENOMEM;
604 } 645 }
605
606 /* We should always be able to stat a valid fd, so this
607 is an error condition. */
608 if (lstat (file, &fn_stat) || fstat (fd, &fd_stat))
609 err = errno;
610 else 646 else
611 { 647 *p = 0;
612 /* If the link and stat don't report the same info, or the
613 file is a symlink, fail the locking. */
614 #define CHK(X) if(X) err = EINVAL
615
616 CHK (!S_ISREG (fn_stat.st_mode));
617 CHK (!S_ISREG (fd_stat.st_mode));
618 CHK (fn_stat.st_nlink != links);
619 CHK (fn_stat.st_dev != fd_stat.st_dev);
620 CHK (fn_stat.st_ino != fd_stat.st_ino);
621 CHK (fn_stat.st_mode != fd_stat.st_mode);
622 CHK (fn_stat.st_nlink != fd_stat.st_nlink);
623 CHK (fn_stat.st_uid != fd_stat.st_uid);
624 CHK (fn_stat.st_gid != fd_stat.st_gid);
625 CHK (fn_stat.st_rdev != fd_stat.st_rdev);
626 648
627 #undef CHK 649 if (access (tmp, W_OK))
650 {
651 /* Fallback to kernel locking */
652 free (tmp);
653 return mu_locker_set_flags (locker,
654 MU_LOCKER_KERNEL|MU_LOCKER_OPTIONS(locker->flags));
628 } 655 }
629 if (localfd != -1)
630 close (localfd);
631 656
632 return err; 657 free (tmp);
633 }
634
635 static int
636 check_file_permissions (const char *file)
637 {
638 int fd = -1;
639 int err = 0;
640 658
641 if ((fd = open (file, O_RDONLY)) == -1) 659 locker->data.dot.dotlock = malloc (strlen (locker->file)
642 return errno == ENOENT ? 0 : errno; 660 + sizeof (DOTLOCK_SUFFIX));
643 661
644 err = stat_check (file, fd, 1); 662 if (!locker->data.dot.dotlock)
645 close (fd); 663 return ENOMEM;
646 fd = -1; 664 strcpy (locker->data.dot.dotlock, locker->file);
647 if (err) 665 strcat (locker->data.dot.dotlock, DOTLOCK_SUFFIX);
648 {
649 if (err == EINVAL)
650 err = MU_ERR_LOCK_BAD_FILE;
651 return err;
652 }
653 666
654 return 0; 667 return 0;
655 } 668 }
656 669
670 static void
671 destroy_dotlock (mu_locker_t locker)
672 {
673 free (locker->data.dot.dotlock);
674 }
675
657 #ifndef MAXHOSTNAMELEN 676 #ifndef MAXHOSTNAMELEN
658 # define MAXHOSTNAMELEN 256 677 # define MAXHOSTNAMELEN 256
659 #endif 678 #endif
660 679
661 /* Locker-specific lock/unlock functions */ 680 static int
662 int 681 lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
663 _locker_lock_dotlock (mu_locker_t lock)
664 { 682 {
665 char host[MAXHOSTNAMELEN + 1] = "localhost"; 683 char host[MAXHOSTNAMELEN + 1] = "localhost";
666 char pid[11]; /* 10 is strlen(2^32 = 4294967296) */ 684 char pid[11]; /* 10 is strlen(2^32 = 4294967296) */
...@@ -669,14 +687,14 @@ _locker_lock_dotlock (mu_locker_t lock) ...@@ -669,14 +687,14 @@ _locker_lock_dotlock (mu_locker_t lock)
669 int err = 0; 687 int err = 0;
670 int fd; 688 int fd;
671 689
672 if (lock->data.dot.nfslock) 690 if (locker->data.dot.nfslock)
673 { 691 {
674 unlink (lock->data.dot.nfslock); 692 unlink (locker->data.dot.nfslock);
675 free (lock->data.dot.nfslock); 693 free (locker->data.dot.nfslock);
676 lock->data.dot.nfslock = 0; 694 locker->data.dot.nfslock = 0;
677 } 695 }
678 696
679 expire_stale_lock (lock); 697 expire_stale_lock (locker);
680 698
681 /* build the NFS hitching-post to the lock file */ 699 /* build the NFS hitching-post to the lock file */
682 700
...@@ -689,20 +707,20 @@ _locker_lock_dotlock (mu_locker_t lock) ...@@ -689,20 +707,20 @@ _locker_lock_dotlock (mu_locker_t lock)
689 snprintf (pid, sizeof (pid), "%lu", (unsigned long) getpid ()); 707 snprintf (pid, sizeof (pid), "%lu", (unsigned long) getpid ());
690 pid[sizeof (pid) - 1] = 0; 708 pid[sizeof (pid) - 1] = 0;
691 709
692 sz = strlen (lock->file) + 1 /* "." */ 710 sz = strlen (locker->file) + 1 /* "." */
693 + strlen (pid) + 1 /* "." */ 711 + strlen (pid) + 1 /* "." */
694 + strlen (now) + 1 /* "." */ 712 + strlen (now) + 1 /* "." */
695 + strlen (host) + 1; 713 + strlen (host) + 1;
696 714
697 lock->data.dot.nfslock = malloc (sz); 715 locker->data.dot.nfslock = malloc (sz);
698 716
699 if (!lock->data.dot.nfslock) 717 if (!locker->data.dot.nfslock)
700 return ENOMEM; 718 return ENOMEM;
701 719
702 snprintf (lock->data.dot.nfslock, sz, "%s.%s.%s.%s", lock->file, pid, now, 720 snprintf (locker->data.dot.nfslock, sz, "%s.%s.%s.%s",
703 host); 721 locker->file, pid, now, host);
704 722
705 fd = open (lock->data.dot.nfslock, 723 fd = open (locker->data.dot.nfslock,
706 O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR); 724 O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR);
707 if (fd == -1) 725 if (fd == -1)
708 { 726 {
...@@ -714,53 +732,53 @@ _locker_lock_dotlock (mu_locker_t lock) ...@@ -714,53 +732,53 @@ _locker_lock_dotlock (mu_locker_t lock)
714 close (fd); 732 close (fd);
715 733
716 /* Try to link to the lockfile. */ 734 /* Try to link to the lockfile. */
717 if (link (lock->data.dot.nfslock, lock->data.dot.dotlock) == -1) 735 if (link (locker->data.dot.nfslock, locker->data.dot.dotlock) == -1)
718 { 736 {
719 unlink (lock->data.dot.nfslock); 737 unlink (locker->data.dot.nfslock);
720 if (errno == EEXIST) 738 if (errno == EEXIST)
721 return MU_ERR_LOCK_CONFLICT; 739 return MU_ERR_LOCK_CONFLICT;
722 return errno; 740 return errno;
723 } 741 }
724 742
725 if ((fd = open (lock->data.dot.dotlock, O_RDWR)) == -1) 743 if ((fd = open (locker->data.dot.dotlock, O_RDWR)) == -1)
726 { 744 {
727 unlink (lock->data.dot.nfslock); 745 unlink (locker->data.dot.nfslock);
728 return errno; 746 return errno;
729 } 747 }
730 748
731 err = stat_check (lock->data.dot.nfslock, fd, 2); 749 err = stat_check (locker->data.dot.nfslock, fd, 2);
732 if (err) 750 if (err)
733 { 751 {
734 unlink (lock->data.dot.nfslock); 752 unlink (locker->data.dot.nfslock);
735 if (err == EINVAL) 753 if (err == EINVAL)
736 return MU_ERR_LOCK_BAD_LOCK; 754 return MU_ERR_LOCK_BAD_LOCK;
737 return errno; 755 return errno;
738 } 756 }
739 757
740 unlink (lock->data.dot.nfslock); 758 unlink (locker->data.dot.nfslock);
741 759
742 /* If no errors, we have the lock. */ 760 /* FIXME: If no errors, we have the lock. */
743 assert (lock->refcnt == 0); 761 assert (locker->refcnt == 0);
744 762
745 if (lock->flags & MU_LOCKER_PID) 763 if (locker->flags & MU_LOCKER_PID)
746 { 764 {
747 char buf[16]; 765 char buf[16];
748 sprintf (buf, "%ld", (long) getpid ()); 766 sprintf (buf, "%ld", (long) getpid ());
749 write (fd, buf, strlen (buf)); 767 write (fd, buf, strlen (buf));
750 } 768 }
751 close(fd); 769 close (fd);
752 return 0; 770 return 0;
753 } 771 }
754 772
755 int 773 static int
756 _locker_unlock_dotlock (mu_locker_t lock) 774 unlock_dotlock (mu_locker_t locker)
757 { 775 {
758 if (unlink (lock->data.dot.dotlock) == -1) 776 if (unlink (locker->data.dot.dotlock) == -1)
759 { 777 {
760 int err = errno; 778 int err = errno;
761 if (err == ENOENT) 779 if (err == ENOENT)
762 { 780 {
763 lock->refcnt = 0; 781 locker->refcnt = 0; /*FIXME?*/
764 err = MU_ERR_LOCK_NOT_HELD; 782 err = MU_ERR_LOCK_NOT_HELD;
765 return err; 783 return err;
766 } 784 }
...@@ -770,16 +788,56 @@ _locker_unlock_dotlock (mu_locker_t lock) ...@@ -770,16 +788,56 @@ _locker_unlock_dotlock (mu_locker_t lock)
770 } 788 }
771 789
772 int 790 int
773 _locker_lock_kernel (mu_locker_t lock) 791 mu_locker_touchlock (mu_locker_t lock)
792 {
793 if (!lock)
794 return MU_ERR_LOCKER_NULL;
795
796 if (MU_LOCKER_TYPE (lock) != MU_LOCKER_TYPE_DOTLOCK)
797 return 0;
798
799 if (lock->refcnt > 0)
800 return utime (lock->data.dot.dotlock, NULL);
801
802 return MU_ERR_LOCK_NOT_HELD;
803 }
804
805
806 /* Kernel locking */
807 static int
808 init_kernel (mu_locker_t locker)
809 {
810 return 0;
811 }
812
813 static int
814 lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
774 { 815 {
775 int fd; 816 int fd;
776 struct flock fl; 817 struct flock fl;
777 818
778 fd = open (lock->file, O_RDWR); 819 switch (mode)
820 {
821 case mu_lck_shr:
822 case mu_lck_opt:
823 mode = O_RDONLY;
824 fl.l_type = F_RDLCK;
825 break;
826
827 case mu_lck_exc:
828 mode = O_RDWR;
829 fl.l_type = F_WRLCK;
830 break;
831
832 default:
833 return EINVAL;
834 }
835
836 fd = open (locker->file, O_RDWR);
779 if (fd == -1) 837 if (fd == -1)
780 return errno; 838 return errno;
781 lock->data.kernel.fd = fd; 839 locker->data.kernel.fd = fd;
782 fl.l_type = F_WRLCK; 840
783 fl.l_whence = SEEK_SET; 841 fl.l_whence = SEEK_SET;
784 fl.l_start = 0; 842 fl.l_start = 0;
785 fl.l_len = 0; /* Lock entire file */ 843 fl.l_len = 0; /* Lock entire file */
...@@ -796,8 +854,8 @@ _locker_lock_kernel (mu_locker_t lock) ...@@ -796,8 +854,8 @@ _locker_lock_kernel (mu_locker_t lock)
796 return 0; 854 return 0;
797 } 855 }
798 856
799 int 857 static int
800 _locker_unlock_kernel (mu_locker_t lock) 858 unlock_kernel (mu_locker_t locker)
801 { 859 {
802 struct flock fl; 860 struct flock fl;
803 861
...@@ -805,7 +863,7 @@ _locker_unlock_kernel (mu_locker_t lock) ...@@ -805,7 +863,7 @@ _locker_unlock_kernel (mu_locker_t lock)
805 fl.l_whence = SEEK_SET; 863 fl.l_whence = SEEK_SET;
806 fl.l_start = 0; 864 fl.l_start = 0;
807 fl.l_len = 0; /* Unlock entire file */ 865 fl.l_len = 0; /* Unlock entire file */
808 if (fcntl (lock->data.kernel.fd, F_SETLK, &fl)) 866 if (fcntl (locker->data.kernel.fd, F_SETLK, &fl))
809 { 867 {
810 #ifdef EACCESS 868 #ifdef EACCESS
811 if (errno == EACCESS) 869 if (errno == EACCESS)
...@@ -815,17 +873,33 @@ _locker_unlock_kernel (mu_locker_t lock) ...@@ -815,17 +873,33 @@ _locker_unlock_kernel (mu_locker_t lock)
815 return EAGAIN; 873 return EAGAIN;
816 return errno; 874 return errno;
817 } 875 }
818 close (lock->data.kernel.fd); 876 close (locker->data.kernel.fd);
819 return 0; 877 return 0;
820 } 878 }
821 879
880 static int
881 init_external (mu_locker_t locker)
882 {
883 if (!(locker->data.external.name = strdup (mu_locker_external_program ?
884 mu_locker_external_program :
885 MU_LOCKER_EXTERNAL_PROGRAM)))
886 return ENOMEM;
887 return 0;
888 }
889
890 static void
891 destroy_external (mu_locker_t locker)
892 {
893 free (locker->data.external.name);
894 }
895
822 /* 896 /*
823 Estimate 1 decimal digit per 3 bits, + 1 for round off. 897 Estimate 1 decimal digit per 3 bits, + 1 for round off.
824 */ 898 */
825 #define DEC_DIGS_PER_INT (sizeof(int) * 8 / 3 + 1) 899 #define DEC_DIGS_PER_INT (sizeof(int) * 8 / 3 + 1)
826 900
827 static int 901 static int
828 lock_external (mu_locker_t l, int lock) 902 external_locker (mu_locker_t l, int lock)
829 { 903 {
830 int err = 0; 904 int err = 0;
831 char *av[6]; 905 char *av[6];
...@@ -836,6 +910,7 @@ lock_external (mu_locker_t l, int lock) ...@@ -836,6 +910,7 @@ lock_external (mu_locker_t l, int lock)
836 910
837 assert (l); 911 assert (l);
838 assert (l->flags & MU_LOCKER_EXTERNAL); 912 assert (l->flags & MU_LOCKER_EXTERNAL);
913 /* FIXME */
839 assert (lock == !l->refcnt); 914 assert (lock == !l->refcnt);
840 /* lock is true, refcnt is 0 or lock is false and refcnt is 1 */ 915 /* lock is true, refcnt is 0 or lock is false and refcnt is 1 */
841 916
...@@ -856,10 +931,8 @@ lock_external (mu_locker_t l, int lock) ...@@ -856,10 +931,8 @@ lock_external (mu_locker_t l, int lock)
856 av[ac++] = aretry; 931 av[ac++] = aretry;
857 } 932 }
858 933
859 if (lock == 0) 934 if (!lock)
860 {
861 av[ac++] = "-u"; 935 av[ac++] = "-u";
862 }
863 936
864 av[ac++] = l->file; 937 av[ac++] = l->file;
865 938
...@@ -879,19 +952,24 @@ lock_external (mu_locker_t l, int lock) ...@@ -879,19 +952,24 @@ lock_external (mu_locker_t l, int lock)
879 case 127: 952 case 127:
880 err = MU_ERR_LOCK_EXT_FAIL; 953 err = MU_ERR_LOCK_EXT_FAIL;
881 break; 954 break;
955
882 case MU_DL_EX_OK: 956 case MU_DL_EX_OK:
883 err = 0; 957 err = 0;
884 l->refcnt = lock; 958 l->refcnt = lock;
885 break; 959 break;
960
886 case MU_DL_EX_NEXIST: 961 case MU_DL_EX_NEXIST:
887 err = MU_ERR_LOCK_NOT_HELD; 962 err = MU_ERR_LOCK_NOT_HELD;
888 break; 963 break;
964
889 case MU_DL_EX_EXIST: 965 case MU_DL_EX_EXIST:
890 err = MU_ERR_LOCK_CONFLICT; 966 err = MU_ERR_LOCK_CONFLICT;
891 break; 967 break;
968
892 case MU_DL_EX_PERM: 969 case MU_DL_EX_PERM:
893 err = EPERM; 970 err = EPERM;
894 break; 971 break;
972
895 default: 973 default:
896 case MU_DL_EX_ERROR: 974 case MU_DL_EX_ERROR:
897 err = MU_ERR_LOCK_EXT_ERR; 975 err = MU_ERR_LOCK_EXT_ERR;
...@@ -902,3 +980,15 @@ lock_external (mu_locker_t l, int lock) ...@@ -902,3 +980,15 @@ lock_external (mu_locker_t l, int lock)
902 return err; 980 return err;
903 } 981 }
904 982
983 static int
984 lock_external (mu_locker_t locker, enum mu_locker_mode mode)
985 {
986 return external_locker (locker, 1);
987 }
988
989 static int
990 unlock_external (mu_locker_t locker)
991 {
992 return external_locker (locker, 0);
993 }
994
......