source: trunk/athena/lib/locker/attachtab.c @ 13563

Revision 13563, 30.4 KB checked in by danw, 25 years ago (diff)
block SIGTTOU and SIGTTIN too
Line 
1/* Copyright 1998 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16/* This file is part of liblocker. It implements the attachtab directory,
17 * which contains files corresponding to attached (or formerly attached)
18 * lockers.
19 */
20
21static const char rcsid[] = "$Id: attachtab.c,v 1.8 1999-09-10 17:17:04 danw Exp $";
22
23#include "locker.h"
24#include "locker_private.h"
25
26#include <sys/stat.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29
30#include <dirent.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <signal.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38/* Used internally by locker_iterate_attachtab */
39struct locker__dirent {
40  char *name;
41  time_t ctime;
42};
43
44/* Used by lock_attachtab */
45struct locker__lock_data {
46  int fd;
47  sigset_t omask;
48};
49
50static int delete_attachent(locker_context context, locker_attachent *at);
51static int get_attachent(locker_context context, char *name,
52                         char *mountpoint, int create,
53                         locker_attachent **atp);
54static int read_attachent(locker_context context, int kind,
55                          char *name, locker_attachent **atp);
56static int lock_attachtab(locker_context context,
57                          struct locker__lock_data *lock);
58static void unlock_attachtab(locker_context context,
59                             struct locker__lock_data *lock);
60static int compare_locker__dirents(const void *a, const void *b);
61
62/* Public interface to locker__lookup_attachent. */
63int locker_read_attachent(locker_context context, char *name,
64                          locker_attachent **atp)
65{
66  return locker__lookup_attachent(context, name, NULL, 0, atp);
67}
68
69/* Look up a locker by filesys name and return an attachent for it. If
70 * "which" is 0, only return an already-attached locker. Otherwise,
71 * "which" is the index of the Hesiod filesys entry for "name" to look
72 * up. (The caller can keep calling with successively higher values of
73 * "which" until it succeeds in attaching one, or
74 * locker__lookup_attachent returns a LOCKER_LOOKUP_FAILURE() status.
75 *
76 * name can be NULL iff mountpoint is non-NULL and which is 0.
77 */
78int locker__lookup_attachent(locker_context context, char *name,
79                             char *mountpoint, int which,
80                             locker_attachent **atp)
81{
82  int status, i;
83  struct locker__lock_data lock;
84  locker_attachent *at;
85  char **descs, *desc, *p;
86  void *cleanup;
87  struct locker_ops *fs;
88
89  status = lock_attachtab(context, &lock);
90  if (status != LOCKER_SUCCESS)
91    return status;
92
93  if (which < 2)
94    {
95      /* Look for a pre-attached locker. */
96      at = NULL;
97      status = get_attachent(context, name, mountpoint, 0, &at);
98      if (status != LOCKER_ENOTATTACHED)
99        {
100          unlock_attachtab(context, &lock);
101          if (status == LOCKER_SUCCESS)
102            *atp = at;
103          return status;
104        }
105      else if (!which)
106        {
107          unlock_attachtab(context, &lock);
108          locker__error(context, "%s: Not attached.\n",
109                        name ? name : mountpoint);
110          return status;
111        }
112    }
113
114  /* It's not attached. Look up the locker description. */
115  status = locker_lookup_filsys(context, name, &descs, &cleanup);
116  if (status)
117    {
118      unlock_attachtab(context, &lock);
119      return status;
120    }
121
122  if (!descs[1])
123    {
124      /* If only one description, it won't have an order field. */
125      if (which == 1)
126        desc = descs[0];
127      else
128        desc = NULL;
129    }
130  else
131    {
132      /* Check each filesystem description until we find the one we want. */
133      for (i = 0, desc = NULL; descs[i]; i++)
134        {
135          p = strrchr(descs[i], ' ');
136          if (!p)
137            {
138              unlock_attachtab(context, &lock);
139              locker_free_filesys(context, descs, cleanup);
140              locker__error(context, "%s: Could not parse locker "
141                            "description \"%s\".\n", name, descs[i]);
142              return LOCKER_EPARSE;
143            }
144
145          if (atoi(p + 1) == which)
146            {
147              desc = descs[i];
148              break;
149            }
150        }
151    }
152
153  if (!desc)
154    {
155      unlock_attachtab(context, &lock);
156      locker_free_filesys(context, descs, cleanup);
157      return LOCKER_ENOENT;
158    }
159
160  fs = locker__get_fstype(context, desc);
161  if (!fs)
162    {
163      unlock_attachtab(context, &lock);
164      locker__error(context, "%s: Unknown locker type in description "
165                    "\"%s\".\n", name, desc);
166      locker_free_filesys(context, descs, cleanup);
167      return LOCKER_EPARSE;
168    }
169
170  status = fs->parse(context, name, desc, mountpoint, &at);
171  locker_free_filesys(context, descs, cleanup);
172  if (status)
173    {
174      unlock_attachtab(context, &lock);
175      return status;
176    }
177
178  if (mountpoint)
179    status = get_attachent(context, NULL, at->mountpoint, which != 0, &at);
180  else
181    status = get_attachent(context, name, at->mountpoint, which != 0, &at);
182  unlock_attachtab(context, &lock);
183  if (status == LOCKER_SUCCESS)
184    *atp = at;
185  else
186    locker_free_attachent(context, at);
187
188  return status;
189}
190
191/* Look up a locker by explicit description and return an attachent
192 * for it. If "create" is 0, only return an already-attached locker.
193 */
194int locker__lookup_attachent_explicit(locker_context context, char *type,
195                                      char *desc, char *mountpoint,
196                                      int create, locker_attachent **atp)
197{
198  struct locker_ops *fs;
199  locker_attachent *at;
200  int status;
201  struct locker__lock_data lock;
202
203  fs = locker__get_fstype(context, type);
204  if (!fs)
205    {
206      locker__error(context, "%s: Unknown locker type \"%s\".\n", desc, type);
207      return LOCKER_EPARSE;
208    }
209
210  status = fs->parse(context, NULL, desc, mountpoint, &at);
211  if (status)
212    return status;
213
214  status = lock_attachtab(context, &lock);
215  if (status != LOCKER_SUCCESS)
216    {
217      locker_free_attachent(context, at);
218      return status;
219    }
220  status = get_attachent(context, NULL, at->mountpoint, create, &at);
221  unlock_attachtab(context, &lock);
222  if (status == LOCKER_SUCCESS)
223    *atp = at;
224  else
225    locker_free_attachent(context, at);
226
227  return status;
228}
229
230/* Delete an attachent file and its corresponding symlink, if any. */
231static int delete_attachent(locker_context context, locker_attachent *at)
232{
233  int status;
234  struct locker__lock_data lock;
235  char *path;
236
237  status = lock_attachtab(context, &lock);
238  if (status != LOCKER_SUCCESS)
239    return status;
240
241  if (at->flags & LOCKER_FLAG_NAMEFILE)
242    {
243      path = locker__attachtab_pathname(context, LOCKER_NAME, at->name);
244      if (!path)
245        {
246          locker__error(context, "Out of memory deleting attachent.\n");
247          status = LOCKER_ENOMEM;
248          goto cleanup;
249        }
250      if (unlink(path) == -1 && errno != ENOENT)
251        {
252          locker__error(context, "Could not delete file %s:\n%s.\n",
253                        path, strerror(errno));
254          status = LOCKER_EATTACHTAB;
255          goto cleanup;
256        }
257      free(path);
258    }
259
260  path = locker__attachtab_pathname(context, LOCKER_MOUNTPOINT,
261                                    at->mountpoint);
262  if (!path)
263    {
264      locker__error(context, "Out of memory deleting attachent.\n");
265      status = LOCKER_ENOMEM;
266      goto cleanup;
267    }
268  if (unlink(path) == -1)
269    {
270      locker__error(context, "Could not delete file %s:\n%s.\n",
271                    path, strerror(errno));
272      status = LOCKER_EATTACHTAB;
273      goto cleanup;
274    }
275  free(path);
276
277  unlock_attachtab(context, &lock);
278  return LOCKER_SUCCESS;
279
280cleanup:
281  free(path);
282  unlock_attachtab(context, &lock);
283  return status;
284}
285
286/* Free a locker_attachent and unlock any locks associated with it. */
287void locker_free_attachent(locker_context context, locker_attachent *at)
288{
289  if (at->mountpoint_file)
290    {
291      fclose(at->mountpoint_file);
292      if (!at->attached && at->mountpoint)
293        delete_attachent(context, at);
294    }
295  if (at->dirlockfd)
296    close(at->dirlockfd);
297  free(at->name);
298  free(at->mountpoint);
299  free(at->buildfrom);
300  free(at->hostdir);
301  free(at->owners);
302
303  if (at->next)
304    locker_free_attachent(context, at->next);
305
306  free(at);
307}
308
309
310/* Iterate through all attachent entries, calling the "test" function
311 * on each one, and then calling the "act" function on all entries for
312 * which the "test" function returns true. Holds an exclusive lock on
313 * the attachtab lock file through the duration of the call. Only
314 * returns an error if it was unable to begin iterating through the
315 * entries.
316 */
317int locker_iterate_attachtab(locker_context context,
318                             locker_callback test, void *testarg,
319                             locker_callback act, void *actarg)
320{
321  int status;
322  struct locker__lock_data lock;
323  char *path;
324  DIR *dir;
325  struct dirent *entry;
326  locker_attachent *at;
327  struct locker__dirent *files = NULL;
328  int nfiles = 0, filessize = 0, i;
329  struct stat st;
330
331  status = lock_attachtab(context, &lock);
332  if (status)
333    return status;
334
335  path = locker__attachtab_pathname(context, LOCKER_MOUNTPOINT, "");
336  if (!path)
337    {
338      unlock_attachtab(context, &lock);
339      locker__error(context, "Out of memory reading attachtab.\n");
340      return LOCKER_ENOMEM;
341    }
342
343  dir = opendir(path);
344  if (!dir)
345    {
346      unlock_attachtab(context, &lock);
347      locker__error(context, "Could not read attachtab:\n%s opening "
348                    "directory %s\n", strerror(errno), path);
349      free(path);
350      return LOCKER_EATTACHTAB;
351    }
352
353  while ((entry = readdir(dir)))
354    {
355      if (entry->d_name[0] == '.')
356        continue;
357
358      if (nfiles >= filessize)
359        {
360          struct locker__dirent *newfiles;
361          int newfilessize;
362
363          newfilessize = filessize ? 2 * filessize : 10;
364          newfiles = realloc(files, (newfilessize) * sizeof(*files));
365          if (!newfiles)
366            {
367              status = LOCKER_ENOMEM;
368              break;
369            }
370          files = newfiles;
371          filessize = newfilessize;
372        }
373
374      files[nfiles].name = malloc(strlen(path) + strlen(entry->d_name) + 1);
375      if (!files[nfiles].name)
376        {
377          status = LOCKER_ENOMEM;
378          break;
379        }
380      sprintf(files[nfiles].name, "%s%s", path, entry->d_name);
381     
382      if (stat(files[nfiles].name, &st) != 0)
383        {
384          locker__error(context, "%s while reading attachtab entry\n\"%s\".\n",
385                        strerror(errno), files[nfiles].name);
386          status = LOCKER_EATTACHTAB;
387          break;
388        }
389
390      files[nfiles++].ctime = st.st_ctime;
391    }
392  closedir(dir);
393  free(path);
394
395  if (status != LOCKER_SUCCESS)
396    {
397      if (status == LOCKER_ENOMEM)
398        locker__error(context, "Out of memory reading attachtab.\n");
399
400      goto cleanup;
401    }
402
403  /* Sort the entries. */
404  qsort(files, nfiles, sizeof(*files), compare_locker__dirents);
405
406  /* Now run through the callbacks. */
407  for (i = 0; i < nfiles; i++)
408    {
409      status = read_attachent(context, LOCKER_FULL_PATH, files[i].name, &at);
410      if (status == LOCKER_SUCCESS)
411        {
412          if (!test || test(context, at, testarg))
413            act(context, at, actarg);
414          locker_free_attachent(context, at);
415        }
416    }
417  status = LOCKER_SUCCESS;
418
419cleanup:
420  for (i = 0; i < nfiles; i++)
421    free(files[i].name);
422  free(files);
423
424  unlock_attachtab(context, &lock);
425  return status;
426}
427
428static int compare_locker__dirents(const void *a, const void *b)
429{
430  const struct locker__dirent *da = a, *db = b;
431
432  /* MUL lockers (which have spaces in their "mountpoint" names) are
433   * sorted before all other lockers. Other than that, lockers are
434   * sorted by attachent creation time.
435   */
436
437  if (strchr(da->name, ' '))
438    {
439      if (!strchr(db->name, ' '))
440        return -1;
441    }
442  else if (strchr(db->name, ' '))
443    return 1;
444
445  return (da->ctime < db->ctime) ? -1 : (da->ctime > db->ctime) ? 1 : 0;
446}
447
448
449/* Fetch the desired attachtab entry into a struct attachtab. name is
450 * the locker name and mountpoint is its mountpoint. If create is non-0
451 * an empty attachtab entry will be created if none exists.
452 *
453 * name may be NULL. mountpoint may be NULL if name is not. *atp should
454 * be a partially-filled in attachent if create is true. Otherwise it
455 * can be partially-filled in, or NULL.
456 */
457static int get_attachent(locker_context context, char *name,
458                         char *mountpoint, int create,
459                         locker_attachent **atp)
460{
461  locker_attachent *at = NULL;
462  int fd = 0;
463  FILE *fp = NULL;
464  int status;
465  char *path = NULL;
466  struct flock fl;
467  mode_t omask;
468
469  /* Try opening an existing file */
470  if (mountpoint)
471    status = read_attachent(context, LOCKER_MOUNTPOINT, mountpoint, &at);
472  else
473    status = read_attachent(context, LOCKER_NAME, name, &at);
474
475  if (status == LOCKER_SUCCESS)
476    {
477      /* If caller passed in both mountpoint and name, make sure they
478       * both matched.
479       */
480      if (mountpoint && name && strcmp(name, at->name))
481        {
482          locker__error(context, "%s: %s is already attached on %s.\n",
483                        name, at->name, mountpoint);
484          locker_free_attachent(context, at);
485          return LOCKER_EMOUNTPOINTBUSY;
486        }
487
488      /* Otherwise, if no attachent was passed in, we've won. */
489      if (!*atp)
490        {
491          *atp = at;
492          return status;
493        }
494
495      /* Make sure the found attachent matches the passed-in attachent. */
496      if (strcmp(at->name, (*atp)->name) ||
497          strcmp(at->mountpoint, (*atp)->mountpoint) ||
498          strcmp(at->hostdir, (*atp)->hostdir) ||
499          at->fs != (*atp)->fs)
500        {
501          locker__error(context, "%s: %s is already attached on %s.\n",
502                        (*atp)->name, at->name, at->mountpoint);
503          locker_free_attachent(context, at);
504          return LOCKER_EMOUNTPOINTBUSY;
505        }
506
507      /* Free the passed-in attachent and return the found one. */
508      locker_free_attachent(context, *atp);
509      *atp = at;
510      return LOCKER_SUCCESS;
511    }
512  else if (status != LOCKER_ENOTATTACHED)
513    return status;
514
515  /* If we were looking for something already attached, we lose. (But
516   * don't print an error: this might not be fatal.)
517   */
518  if (!create)
519    return LOCKER_ENOTATTACHED;
520
521  path = locker__attachtab_pathname(context, LOCKER_MOUNTPOINT, mountpoint);
522  if (!path)
523    return LOCKER_ENOMEM;
524
525  omask = umask(0);
526  fd = open(path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
527  umask(omask);
528  if (fd == -1)
529    {
530      locker__error(context, "Could not create attachtab file %s:\n%s.\n",
531                    path, strerror(errno));
532      status = LOCKER_EATTACHTAB;
533      goto cleanup;
534    }
535
536  fp = fdopen(fd, "r+");
537  if (!fp)
538    {
539      locker__error(context, "Could not create attachtab file %s:\n%s.\n",
540                    path, strerror(errno));
541      status = LOCKER_EATTACHTAB;
542      goto cleanup;
543    }
544
545  fl.l_type = F_WRLCK;
546  fl.l_whence = SEEK_SET;
547  fl.l_start = fl.l_len = 0;
548  status = fcntl(fileno(fp), F_SETLKW, &fl);
549  if (status < 0)
550    {
551      locker__error(context, "Could not lock attachtab file %s:\n%s.\n",
552                    path, strerror(errno));
553      status = LOCKER_EATTACHTAB;
554      goto cleanup;
555    }
556
557  if (name)
558    {
559      char *npath;
560      npath = locker__attachtab_pathname(context, LOCKER_NAME, name);
561      if (!npath)
562        {
563          status = LOCKER_ENOMEM;
564          goto cleanup;
565        }
566      unlink(npath);
567      if (symlink(path, npath) < 0)
568        {
569          locker__error(context, "Could not symlink %s\n to %s: %s.\n",
570                        path, npath, strerror(errno));
571          free(npath);
572          status = LOCKER_EATTACHTAB;
573          goto cleanup;
574        }
575      free(npath);
576      (*atp)->flags |= LOCKER_FLAG_NAMEFILE;
577    }
578
579  free(path);
580
581  (*atp)->mountpoint_file = fp;
582  return LOCKER_SUCCESS;
583
584cleanup:
585  if (fp || fd > 0)
586    {
587      unlink(path);
588      if (fp)
589        fclose(fp);
590      else
591        close(fd);
592    }
593  if (path)
594    free(path);
595  if (at)
596    free(at);
597  return status;
598}
599
600/* The format of a mountpoint file is:
601 *
602 * version (currently 1)
603 * hesiod or explicit name
604 * mountpoint
605 * fstype
606 * IP addr of host (or 0.0.0.0)
607 * path to locker
608 * auth mode
609 * flags
610 * # of owners:owner uid:owner uid:...
611 *
612 */
613
614/* Copy data from a locker_attachent into the corresponding file. */
615void locker__update_attachent(locker_context context, locker_attachent *at)
616{
617  int i;
618
619  if (at->attached)
620    {
621      rewind(at->mountpoint_file);
622      fprintf(at->mountpoint_file, "1\n%s\n%s\n%s\n%s\n%s\n%c\n%d\n",
623              at->name, at->mountpoint, at->fs->name, inet_ntoa(at->hostaddr),
624              at->hostdir, at->mode, at->flags);
625
626      fprintf(at->mountpoint_file, "%d", at->nowners);
627      for (i = 0; i < at->nowners; i++)
628        fprintf(at->mountpoint_file, ":%ld", (long)at->owners[i]);
629      fprintf(at->mountpoint_file, "\n");
630      fflush(at->mountpoint_file);
631    }
632  else
633    {
634      ftruncate(fileno(at->mountpoint_file), 0);
635      rewind(at->mountpoint_file);
636    }
637}
638
639/* Read the named attachtab file into a locker_attachent. */
640static int read_attachent(locker_context context, int kind, char *name,
641                          locker_attachent **atp)
642{
643  FILE *fp;
644  char *path, *pmem = NULL, *buf = NULL, *p, *q;
645  int bufsize, status, i;
646  struct flock fl;
647  locker_attachent *at;
648  struct stat st1, st2;
649
650  if (kind != LOCKER_FULL_PATH)
651    {
652      path = pmem = locker__attachtab_pathname(context, kind, name);
653      if (!path)
654        return LOCKER_ENOMEM;
655    }
656  else
657    path = name;
658
659  /* Need to open it read/write so we can get an F_WRLCK. */
660  fp = fopen(path, "r+");
661  if (!fp)
662    {
663      if (errno == ENOENT)
664        {
665          free(pmem);
666          return LOCKER_ENOTATTACHED;
667        }
668
669      locker__error(context, "Could not open attachtab file %s:\n%s.\n",
670                    path, strerror(errno));
671      free(pmem);
672      return LOCKER_EATTACHTAB;
673    }
674
675  fl.l_type = F_WRLCK;
676  fl.l_whence = SEEK_SET;
677  fl.l_start = fl.l_len = 0;
678  status = fcntl(fileno(fp), F_SETLKW, &fl);
679  if (status < 0)
680    {
681      fclose(fp);
682      locker__error(context, "Could not lock attachtab file %s:\n%s.\n",
683                    path, strerror(errno));
684      free(pmem);
685      return LOCKER_EATTACHTAB;
686    }
687
688  /* The file might have been deleted while we were waiting on the lock.
689   * Worse yet, someone else might have created a new one after the one
690   * we're waiting for was deleted, but before the deleter gave up its
691   * lock. So check first that it's there, and second that it's still
692   * the right file.
693   */
694  if (stat(path, &st1) == -1)
695    {
696      fclose(fp);
697      if (errno == ENOENT)
698        {
699          free(pmem);
700          return LOCKER_ENOTATTACHED;
701        }
702      else
703        {
704          locker__error(context, "Could not stat attachent file %s:\n%s.\n",
705                        path, strerror(errno));
706          free(pmem);
707          return LOCKER_EATTACHTAB;
708        }
709    }
710  if (fstat(fileno(fp), &st2) == -1)
711    {
712      fclose(fp);
713      locker__error(context, "Could not fstat attachent file %s:\n%s.\n",
714                    path, strerror(errno));
715      free(pmem);
716      return LOCKER_EATTACHTAB;
717    }
718  if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
719    {
720      fclose(fp);
721      free(pmem);
722      return LOCKER_ENOTATTACHED;
723    }
724  if (st1.st_size == 0)
725    {
726      /* "This can't happen." (Ha ha) */
727      unlink(path);
728      fclose(fp);
729      free(pmem);
730      return LOCKER_ENOTATTACHED;
731    }
732  free(pmem);
733
734  /* Start building the attachent. */
735  at = locker__new_attachent(context, NULL);
736  if (!at)
737    {
738      fclose(fp);
739      return LOCKER_ENOMEM;
740    }
741
742  at->mountpoint_file = fp;
743  at->attached = 1;
744
745  /* Read version number. */
746  status = locker__read_line(fp, &buf, &bufsize);
747  if (status != LOCKER_SUCCESS)
748    goto cleanup;
749  if (atoi(buf) != 1)
750    {
751      locker__error(context, "Could not parse attachtab file %s:\n"
752                    "unknown version number \"%s\".\n", name, buf);
753      status = LOCKER_EATTACHTAB;
754      goto cleanup2;
755    }
756
757  /* Read locker name. */
758  status = locker__read_line(fp, &buf, &bufsize);
759  if (status != LOCKER_SUCCESS)
760    goto cleanup;
761  at->name = strdup(buf);
762  if (!at->name)
763    {
764      status = LOCKER_ENOMEM;
765      goto cleanup;
766    }
767
768  /* Read mountpoint. */
769  status = locker__read_line(fp, &buf, &bufsize);
770  if (status != LOCKER_SUCCESS)
771    goto cleanup;
772  at->mountpoint = strdup(buf);
773  if (!at->mountpoint)
774    {
775      status = LOCKER_ENOMEM;
776      goto cleanup;
777    }
778
779  /* Read fstype. */
780  status = locker__read_line(fp, &buf, &bufsize);
781  if (status != LOCKER_SUCCESS)
782    goto cleanup;
783  at->fs = locker__get_fstype(context, buf);
784  if (!at->fs)
785    {
786      status = LOCKER_ENOMEM;
787      goto cleanup;
788    }
789
790  /* Read hostaddr. */
791  status = locker__read_line(fp, &buf, &bufsize);
792  if (status != LOCKER_SUCCESS)
793    goto cleanup;
794  at->hostaddr.s_addr = inet_addr(buf);
795
796  /* Read hostdir. */
797  status = locker__read_line(fp, &buf, &bufsize);
798  if (status != LOCKER_SUCCESS)
799    goto cleanup;
800  at->hostdir = strdup(buf);
801  if (!at->hostdir)
802    {
803      status = LOCKER_ENOMEM;
804      goto cleanup;
805    }
806
807  /* Read mode */
808  status = locker__read_line(fp, &buf, &bufsize);
809  if (status != LOCKER_SUCCESS)
810    goto cleanup;
811  at->mode = buf[0];
812
813  /* Read flags */
814  status = locker__read_line(fp, &buf, &bufsize);
815  if (status != LOCKER_SUCCESS)
816    goto cleanup;
817  at->flags = atoi(buf);
818
819  /* Read list of owners. */
820  status = locker__read_line(fp, &buf, &bufsize);
821  if (status != LOCKER_SUCCESS)
822    goto cleanup;
823  at->nowners = strtol(buf, &p, 10);
824  if (p == buf || (*p && *p != ':'))
825    {
826      status = LOCKER_EATTACHTAB;
827      goto cleanup;
828    }
829  at->owners = malloc(at->nowners * sizeof(uid_t));
830  if (!at->owners)
831    {
832      status = LOCKER_ENOMEM;
833      goto cleanup;
834    }
835
836  for (i = 0, p++; i < at->nowners && p; i++, p = q + 1)
837    {
838      at->owners[i] = strtol(p, &q, 10);
839      if (p == q || (*q && *q != ':'))
840        {
841          status = LOCKER_EATTACHTAB;
842          goto cleanup;
843        }
844    }
845  if (i < at->nowners)
846    {
847      status = LOCKER_EATTACHTAB;
848      goto cleanup;
849    }
850
851  free(buf);
852
853  /* If this is a MUL locker, use mul_parse to read in the other
854   * attachents.
855   */
856  if (!strcmp(at->fs->name, "MUL"))
857    {
858      locker_attachent *atm;
859
860      status = at->fs->parse(context, at->name, at->mountpoint, NULL, &atm);
861      if (status)
862        goto cleanup;
863
864      /* Swap the parsed attachent's MUL chain into the one we read off
865       * disk, and then dispose of the parsed one.
866       */
867      at->next = atm->next;
868      atm->next = NULL;
869      locker_free_attachent(context, atm);
870    }
871
872  *atp = at;
873  return LOCKER_SUCCESS;
874
875cleanup:
876  if (status == LOCKER_EATTACHTAB)
877    {
878      locker__error(context, "Invalid line in attachtab file %s:%s\n",
879                    name, buf);
880    }
881  else if (status == LOCKER_EMOUNTPOINTBUSY)
882    locker__error(context, "%s: Mountpoint busy.\n", at->mountpoint);
883  else if (status == LOCKER_ENOMEM)
884    locker__error(context, "Out of memory reading attachtab file.\n");
885  else
886    {
887      locker__error(context, "Unexpected %s while reading attachtab "
888                    "file %s.\n", status == LOCKER_EOF ? "end of file" :
889                    "file error", name);
890      status = LOCKER_EATTACHTAB;
891    }
892cleanup2:
893  free(buf);
894  locker_free_attachent(context, at);
895  return status;
896}
897
898/* Lock the attachtab directory. This must be done in order to:
899 *  - create an attachent
900 *  - delete an attachent
901 *  - create a symlink from mountpoints/ to lockers/
902 *  - prevent other processes from doing the above, to guarantee
903 *    a consistent view of the attachtab.
904 */
905static int lock_attachtab(locker_context context,
906                          struct locker__lock_data *lock)
907{
908  int lockfd, status;
909  char *path;
910  struct flock fl;
911  sigset_t mask;
912
913  /* If the attachtab is already locked (because we're reading a
914   * subcomponent of a MUL locker, for instance), just record an
915   * additional lock and return success.
916   */
917  if (context->locks)
918    {
919      context->locks++;
920      return LOCKER_SUCCESS;
921    }
922
923  path = locker__attachtab_pathname(context, LOCKER_LOCK, ".lock");
924  if (!path)
925    {
926      locker__error(context, "Out of memory in lock_attachtab.\n");
927      return LOCKER_ENOMEM;
928    }
929
930  lockfd = open(path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
931  free (path);
932  if (lockfd < 0)
933    {
934      locker__error(context, "Could not open attachtab lock file: %s.\n",
935                    strerror(errno));
936      return LOCKER_EATTACHTAB;
937    }
938
939  /* Protect from ^Z while holding the attachtab lock. */
940  sigemptyset(&mask);
941  sigaddset(&mask, SIGTSTP);
942  sigaddset(&mask, SIGTTOU);
943  sigaddset(&mask, SIGTTIN);
944  sigprocmask(SIG_BLOCK, &mask, &lock->omask);
945
946  fl.l_type = F_WRLCK;
947  fl.l_whence = SEEK_SET;
948  fl.l_start = fl.l_len = 0;
949  status = fcntl(lockfd, F_SETLKW, &fl);
950  if (status < 0)
951    {
952      close(lockfd);
953      sigprocmask(SIG_SETMASK, &lock->omask, NULL);
954      locker__error(context, "Could not lock attachtab: %s.\n",
955                    strerror(errno));
956      return LOCKER_EATTACHTAB;
957    }
958
959  context->locks++;
960  lock->fd = lockfd;
961  return LOCKER_SUCCESS;
962}
963
964static void unlock_attachtab(locker_context context,
965                             struct locker__lock_data *lock)
966{
967  if (--context->locks == 0)
968    {
969      close(lock->fd);
970      sigprocmask(SIG_SETMASK, &lock->omask, NULL);
971    }
972}
973
974static char *kpath[] = {
975  ".", /* LOCK */
976  "locker", /* NAME */
977  "mountpoint", /* MOUNTPOINT */
978  "directory", /* DIRECTORY */
979};
980
981char *locker__attachtab_pathname(locker_context context,
982                                 int kind, char *name)
983{
984  int len;
985  char *path, *s, *d;
986
987  len = strlen(context->attachtab) + strlen(kpath[kind]) + strlen(name) + 3;
988  for (path = name; *path; path++)
989    {
990      if (*path == '/' || *path == '_')
991        len++;
992    }
993
994  path = malloc(len);
995  if (!path)
996    {
997      locker__error(context, "Out of memory reading attachent.\n");
998      return NULL;
999    }
1000
1001  sprintf(path, "%s/%s/", context->attachtab, kpath[kind]);
1002  d = path + strlen(path);
1003
1004  for (s = name; *s; s++)
1005    {
1006      switch (*s)
1007        {
1008        case '/':
1009          *d++ = '_';
1010          *d++ = '=';
1011          break;
1012
1013        case '_':
1014          *d++ = '_';
1015          /* fall through */
1016
1017        default:
1018          *d++ = *s;
1019        }
1020    }
1021  *d = '\0';
1022
1023  return path;
1024}
1025
1026locker_attachent *locker__new_attachent(locker_context context,
1027                                        struct locker_ops *type)
1028{
1029  locker_attachent *at;
1030
1031  at = malloc(sizeof(locker_attachent));
1032  if (!at)
1033    {
1034      locker__error(context, "Out of memory creating attachent.\n");
1035      return NULL;
1036    }
1037
1038  memset(at, 0, sizeof(locker_attachent));
1039  at->name = at->mountpoint = at->hostdir = at->buildfrom = NULL;
1040  at->owners = NULL;
1041  at->mountpoint_file = NULL;
1042  at->next = NULL;
1043
1044  at->fs = type;
1045  return at;
1046}
1047
1048
1049/* Convert an old attachtab file into a new attachtab directory */
1050int locker_convert_attachtab(locker_context context, char *oattachtab)
1051{
1052  FILE *fp;
1053  char *buf = NULL;
1054  int bufsize, status;
1055  char *p, *lasts = NULL, *host, *name;
1056  int rmdir;
1057  locker_attachent *at;
1058  struct locker_ops *fs;
1059
1060  fp = fopen(oattachtab, "r");
1061  if (!fp)
1062    {
1063      locker__error(context, "Could not open %s: %s.\n", oattachtab,
1064                    strerror(errno));
1065      return LOCKER_EATTACHTAB;
1066    }
1067
1068  while ((status = locker__read_line(fp, &buf, &bufsize)) == LOCKER_SUCCESS)
1069    {
1070      /* Check attachtab version. */
1071      if (strncmp(buf, "A1 ", 3))
1072        {
1073          locker__error(context, "Ignoring unrecognized attachtab line: %s\n",
1074                        buf);
1075          continue;
1076        }
1077
1078      /* Check if it's attached, attaching, or detaching. */
1079      if (buf[4] != '+')
1080        {
1081          locker__error(context, "Ignoring not-fully-attached locker: %s\n",
1082                        buf);
1083          continue;
1084        }
1085
1086      /* Get fs type. */
1087      p = strtok_r(buf + 5, " ", &lasts);
1088      fs = locker__get_fstype(context, p);
1089      if (!fs)
1090        {
1091          locker__error(context, "Ignoring unknown locker type \"%s\"\n", p);
1092          continue;
1093        }
1094      at = locker__new_attachent(context, fs);
1095      if (!at)
1096        {
1097          locker__error(context, "Out of memory.\n");
1098          status = LOCKER_ENOMEM;
1099          break;
1100        }
1101
1102      /* Now start parsing. First name. */
1103      p = strtok_r(NULL, " ", &lasts);
1104      if (p)
1105        at->name = strdup(p);
1106
1107      /* Host */
1108      host = strtok_r(NULL, " ", &lasts);
1109
1110      /* Directory */
1111      p = strtok_r(NULL, " ", &lasts);
1112      if (p)
1113        {
1114          if (!strcmp(at->fs->name, "MUL"))
1115            {
1116              /* Old attachtab stores MUL components as hostdir. We store
1117               * it as mountpoint.
1118               */
1119
1120              char *q;
1121              /* Convert commas to spaces */
1122              for (q = p; *q; q++)
1123                {
1124                  if (*q == ',')
1125                    *q = ' ';
1126                }
1127
1128              at->mountpoint = strdup(p);
1129              at->hostdir = strdup("");
1130            }
1131          else if (host && strcmp(host, "localhost"))
1132            {
1133              at->hostdir = malloc(strlen(host) + strlen(p) + 2);
1134              if (at->hostdir)
1135                sprintf(at->hostdir, "%s:%s", host, p);
1136            }
1137          else
1138            at->hostdir = strdup(p);
1139        }
1140
1141      /* IP addr */
1142      p = strtok_r(NULL, " ", &lasts);
1143      if (p)
1144        at->hostaddr.s_addr = inet_addr(p);
1145
1146      /* Number of directories to rm on detach.
1147       * Needs special conversion later.
1148       */
1149      p = strtok_r(NULL, " ", &lasts);
1150      if (p)
1151        rmdir = atoi(p);
1152
1153      /* Mountpoint */
1154      p = strtok_r(NULL, " ", &lasts);
1155      if (p && strcmp(at->fs->name, "MUL"))
1156        at->mountpoint = strdup(p);
1157
1158      /* Convert old flags to new flags */
1159      p = strtok_r(NULL, " ", &lasts);
1160      if (p)
1161        {
1162          int oflags = atoi(p);
1163          if (oflags & 0x1)
1164            at->flags |= LOCKER_FLAG_NOSUID;
1165          if (oflags & 0x2)
1166            at->flags |= LOCKER_FLAG_LOCKED;
1167          if (oflags & 0x8)
1168            at->flags |= LOCKER_FLAG_KEEP;
1169        }
1170
1171      /* Comma-separated list of owners */
1172      p = strtok_r(NULL, " ", &lasts);
1173      if (p)
1174        {
1175          char *q = p;
1176
1177          do
1178            {
1179              at->nowners++;
1180              q++;
1181            }
1182          while ((q = strchr(q, ',')));
1183
1184          at->owners = malloc(at->nowners * sizeof(uid_t));
1185          if (at->owners)
1186            {
1187              int i;
1188              for (i = 0; i < at->nowners; i++)
1189                {
1190                  at->owners[i] = strtoul(p, &p, 10);
1191                  p++;
1192                }
1193            }
1194        }
1195
1196      /* RVD drivenum -- Ignored */
1197      p = strtok_r(NULL, " ", &lasts);
1198
1199      /* And mount mode */
1200      p = strtok_r(NULL, " ", &lasts);
1201      if (p)
1202        at->mode = *p;
1203
1204      /* Make sure we got all that */
1205      if (!p || !at->name || !at->hostdir || !at->mountpoint || !at->owners)
1206        {
1207          locker__error(context, "Parse error or out of memory.\n");
1208          status = LOCKER_EATTACHTAB;
1209          break;
1210        }
1211
1212      at->attached = 1;
1213      if (buf[3] == '0')
1214        {
1215          at->flags |= LOCKER_FLAG_NAMEFILE;
1216          name = at->name;
1217        }
1218      else
1219        name = NULL;
1220
1221      status = get_attachent(context, name, at->mountpoint, 1, &at);
1222      if (status != LOCKER_SUCCESS)
1223        {
1224          locker__error(context, "Could not create attachent for %s.\n",
1225                        at->name);
1226          locker_free_attachent(context, at);
1227          continue;
1228        }
1229
1230      locker__update_attachent(context, at);
1231
1232      /* Due to a bug in the old attach, rmdir will be "1" instead of "0"
1233       * if you attach an AFS locker in an immediate subdir of "/".
1234       */
1235      if (rmdir == 1 && !strchr(at->mountpoint + 1, '/'))
1236        rmdir--;
1237
1238      /* Now record the directories to delete on detach. */
1239      while (rmdir--)
1240        {
1241          int fd;
1242          char *file = locker__attachtab_pathname(context, LOCKER_DIRECTORY,
1243                                                  at->mountpoint);
1244          if (!file)
1245            {
1246              locker__error(context, "Out of memory.\n");
1247              status = LOCKER_ENOMEM;
1248              goto cleanup;
1249            }
1250
1251          fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
1252          if (fd == -1 && errno != EEXIST)
1253            {
1254              locker__error(context, "Could not create directory data file "
1255                            "%s:\n%s.\n", file, strerror(errno));
1256              free(file);
1257              continue;
1258            }
1259          free(file);
1260          close(fd);
1261
1262          p = strrchr(at->mountpoint, '/');
1263          if (!p || p == at->mountpoint)
1264            break;
1265          *p = '\0';
1266        }
1267
1268      locker_free_attachent(context, at);
1269    }
1270
1271cleanup:
1272  fclose(fp);
1273  free(buf);
1274
1275  return status;
1276}
Note: See TracBrowser for help on using the repository browser.