source: trunk/athena/lib/locker/mount.c @ 13066

Revision 13066, 6.2 KB checked in by danw, 26 years ago (diff)
when umount fails, check if the filesystem was already detached
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 routines to
17 * mount and unmount system-recognized filesystems.
18 */
19
20#include <sys/types.h>
21#include <sys/param.h>
22#include <sys/mount.h>
23#include <sys/stat.h>
24
25#include <sys/wait.h>
26#include <errno.h>
27#include <signal.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#include "locker.h"
33#include "locker_private.h"
34
35static const char rcsid[] = "$Id: mount.c,v 1.3 1999-05-19 14:19:49 danw Exp $";
36
37static int domount(locker_context context, int mount,
38                   locker_attachent *at, char **argv);
39static void cancel_mount(int sig);
40static volatile int cancel;
41
42
43int locker__mount(locker_context context, locker_attachent *at,
44                  char *mountoptions)
45{
46  char *argv[6];
47
48  argv[0] = MOUNT_CMD;
49  argv[1] = "-o";
50  argv[2] = mountoptions;
51  argv[3] = at->hostdir;
52  argv[4] = at->mountpoint;
53  argv[5] = NULL;
54  return domount(context, 1, at, argv);
55}
56
57int locker__unmount(locker_context context, locker_attachent *at)
58{
59  char *argv[3];
60  int status;
61
62  argv[0] = UMOUNT_CMD;
63  argv[1] = at->mountpoint;
64  argv[2] = NULL;
65  status = domount(context, 0, at, argv);
66
67  if (status == LOCKER_EDETACH)
68    {
69      /* Check if the detach failed because the filesystem was already
70       * unmounted. (By seeing if the mountpoint directory isn't
71       * there, or if its st_dev matches its parent's st_dev.)
72       */
73      struct stat st1, st2;
74      char *parent, *p;
75
76      if (stat(at->mountpoint, &st1) == -1 && errno == ENOENT)
77        return LOCKER_SUCCESS;
78      parent = strdup(at->mountpoint);
79      if (!parent)
80        return LOCKER_EDETACH;
81
82      p =  strrchr(parent, '/');
83      if (!p)
84        {
85          free(parent);
86          return LOCKER_EDETACH;
87        }
88      if (p == parent)
89        p++;
90      *p = '\0';
91      status = stat(parent, &st2);
92      free(parent);
93      if (status == -1)
94        return LOCKER_EDETACH;
95
96      if (st1.st_dev == st2.st_dev)
97        return LOCKER_SUCCESS;
98      else
99        return LOCKER_EDETACH;
100    }
101  else
102    return status;
103}
104
105static int domount(locker_context context, int mount,
106                   locker_attachent *at, char **argv)
107{
108  pid_t pid;
109  int status, pstat, pipefds[2], bufsiz, nread, new, oldalarm;
110  char *buf = NULL;
111  sigset_t omask, mask;
112  struct sigaction sa, sa_chld, sa_int, sa_alrm;
113
114  if (pipe(pipefds) == -1)
115    {
116      locker__error(context, "%s: Could not open pipe for mount: %s.\n",
117                    at->name, strerror(errno));
118      return mount ? LOCKER_EATTACH : LOCKER_EDETACH;
119    }
120
121  /* If the liblocker caller has set SIGCHLD's handler to SIG_IGN,
122   * waitpid won't work. So we set its handler to SIG_DFL after
123   * blocking it. This makes waitpid work but still prevents the
124   * caller from seeing SIGCHLDs.
125   */
126  sigemptyset(&mask);
127  sigaddset(&mask, SIGCHLD);
128  sigprocmask(SIG_BLOCK, &mask, &omask);
129
130  sigemptyset(&sa.sa_mask);
131  sa.sa_flags = 0;
132  sa.sa_handler = SIG_DFL;
133  sigaction(SIGCHLD, &sa, &sa_chld);
134
135  /* Set SIGINT and SIGALRM to interrupt us nicely and then unblock
136   * them.
137   */
138  cancel = 0;
139  sa.sa_handler = cancel_mount;
140  sigaction(SIGINT, &sa, &sa_int);
141  sigaction(SIGALRM, &sa, &sa_alrm);
142  oldalarm = alarm(LOCKER_MOUNT_TIMEOUT);
143
144  sigdelset(&mask, SIGALRM);
145  sigdelset(&mask, SIGINT);
146  sigprocmask(SIG_SETMASK, &mask, NULL);
147
148  switch (pid = fork())
149    {
150    case -1:
151      close(pipefds[0]);
152      close(pipefds[1]);
153      if (errno == ENOMEM)
154        {
155          locker__error(context, "Not enough memory to fork.\n");
156          status = LOCKER_ENOMEM;
157          goto cleanup;
158        }
159      else
160        {
161          locker__error(context, "%s: Could not fork to exec %s: %s.\n",
162                        at->name, argv[0], strerror(errno));
163          status = mount ? LOCKER_EATTACH : LOCKER_EDETACH;
164          goto cleanup;
165        }
166
167    case 0:
168      setuid(0);
169      close(pipefds[0]);
170      close(STDOUT_FILENO);
171      close(STDERR_FILENO);
172      dup2(pipefds[1], STDOUT_FILENO);
173      dup2(pipefds[1], STDERR_FILENO);
174
175      execv(argv[0], argv);
176      exit(1);
177
178    default:
179      close(pipefds[1]);
180    }
181
182  /* Need to read the data out of the pipe first, in case mount blocks
183   * writing to it before it can exit.
184   */
185  bufsiz = nread = 0;
186
187  new = -1;
188  while (new != 0 && !cancel)
189    {
190      char *nbuf;
191
192      bufsiz += 1024;
193      nbuf = realloc(buf, bufsiz);
194      if (nbuf)
195        {
196          buf = nbuf;
197          new = read(pipefds[0], buf + nread, 1023);
198        }
199
200      if (!nbuf || new == -1)
201        {
202          /* We can't finish reading the buffer, but we need to wait for
203           * the child to exit.
204           */
205          free(buf);
206          buf = NULL;
207          break;
208        }
209      else
210        nread += new;
211    }
212  close(pipefds[0]);
213  if (buf)
214    buf[nread] = '\0';
215
216  do
217    {
218      if (cancel)
219        kill(pid, SIGKILL);
220    }
221  while (waitpid(pid, &pstat, 0) < 0 && errno == EINTR);
222
223  if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
224    status = LOCKER_SUCCESS;
225  else if (cancel == SIGALRM)
226    {
227      locker__error(context, "%s: %s timed out.\n", at->name,
228                    mount ? "Mount" : "Unmount");
229      status = mount ? LOCKER_EATTACH : LOCKER_EDETACH;
230    }
231  else if (cancel == SIGINT)
232    {
233      locker__error(context, "%s: %s interrupted by user.\n", at->name,
234                    mount ? "Mount" : "Unmount");
235      status = mount ? LOCKER_EATTACH : LOCKER_EDETACH;
236    }
237  else
238    {
239      locker__error(context, "%s: %s failed:\n%s", at->name,
240                    mount ? "Mount" : "Unmount",
241                    buf ? buf : "Unknown error.\n");
242      status = mount ? LOCKER_EATTACH : LOCKER_EDETACH;
243    }
244
245cleanup:
246  free(buf);
247  sigaction(SIGCHLD, &sa_chld, NULL);
248  sigaction(SIGINT, &sa_int, NULL);
249  alarm(0);
250  sigaction(SIGALRM, &sa_alrm, NULL);
251  sigprocmask(SIG_SETMASK, &omask, NULL);
252  alarm(oldalarm);
253
254  return status;
255}
256
257static void cancel_mount(int sig)
258{
259  cancel = sig;
260}
Note: See TracBrowser for help on using the repository browser.