1/*
2 * Copyright (c) 1997-2006 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgment:
21 *      This product includes software developed by the University of
22 *      California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *
40 * File: am-utils/amd/mntfs.c
41 *
42 */
43
44#ifdef HAVE_CONFIG_H
45# include <config.h>
46#endif /* HAVE_CONFIG_H */
47#include <am_defs.h>
48#include <amd.h>
49
50qelem mfhead = {&mfhead, &mfhead};
51
52int mntfs_allocated;
53
54
55mntfs *
56dup_mntfs(mntfs *mf)
57{
58  if (mf->mf_refc == 0) {
59    if (mf->mf_cid)
60      untimeout(mf->mf_cid);
61    mf->mf_cid = 0;
62  }
63  mf->mf_refc++;
64
65  return mf;
66}
67
68
69static void
70init_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
71{
72  mf->mf_ops = ops;
73  mf->mf_fsflags = ops->nfs_fs_flags;
74  mf->mf_fo = mo;
75  mf->mf_mount = strdup(mp);
76  mf->mf_info = strdup(info);
77  mf->mf_auto = strdup(auto_opts);
78  mf->mf_mopts = strdup(mopts);
79  mf->mf_remopts = strdup(remopts);
80  mf->mf_loopdev = NULL;
81  mf->mf_refc = 1;
82  mf->mf_flags = 0;
83  mf->mf_error = -1;
84  mf->mf_cid = 0;
85  mf->mf_private = 0;
86  mf->mf_prfree = 0;
87
88  if (ops->ffserver)
89    mf->mf_server = (*ops->ffserver) (mf);
90  else
91    mf->mf_server = 0;
92}
93
94
95static mntfs *
96alloc_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
97{
98  mntfs *mf = ALLOC(struct mntfs);
99
100  init_mntfs(mf, ops, mo, mp, info, auto_opts, mopts, remopts);
101  ins_que(&mf->mf_q, &mfhead);
102  mntfs_allocated++;
103
104  return mf;
105}
106
107
108/* find a matching mntfs in our list */
109mntfs *
110locate_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
111{
112  mntfs *mf;
113
114  dlog("Locating mntfs reference to (%s,%s)", mp, info);
115
116  ITER(mf, mntfs, &mfhead) {
117    /*
118     * For backwards compatibility purposes, we treat already-mounted
119     * filesystems differently and only require a match of their mount point,
120     * not of their server info. After all, there is little we can do if
121     * the user asks us to mount two different things onto the same mount: one
122     * will always cover the other one.
123     */
124    if (STREQ(mf->mf_mount, mp) &&
125	((mf->mf_flags & MFF_MOUNTED && !(mf->mf_fsflags & FS_DIRECT))
126	 || (STREQ(mf->mf_info, info) && mf->mf_ops == ops))) {
127      /*
128       * Handle cases where error ops are involved
129       */
130      if (ops == &amfs_error_ops) {
131	/*
132	 * If the existing ops are not amfs_error_ops
133	 * then continue...
134	 */
135	if (mf->mf_ops != &amfs_error_ops)
136	  continue;
137	return dup_mntfs(mf);
138      }
139
140      dlog("mf->mf_flags = %#x", mf->mf_flags);
141      mf->mf_fo = mo;
142      if ((mf->mf_flags & MFF_RESTART) && amd_state < Finishing) {
143	/*
144	 * Restart a previously mounted filesystem.
145	 */
146	dlog("Restarting filesystem %s", mf->mf_mount);
147
148	/*
149	 * If we are restarting an amd internal filesystem,
150	 * we need to initialize it a bit.
151	 *
152	 * We know it's internal because it is marked as toplvl.
153	 */
154	if (mf->mf_ops == &amfs_toplvl_ops) {
155	  mf->mf_ops = ops;
156	  mf->mf_info = strealloc(mf->mf_info, info);
157	  ops->mounted(mf);	/* XXX: not right, but will do for now */
158	}
159
160	return mf;
161      }
162
163      if (!(mf->mf_flags & (MFF_MOUNTED | MFF_MOUNTING | MFF_UNMOUNTING))) {
164	fserver *fs;
165	mf->mf_flags &= ~MFF_ERROR;
166	mf->mf_error = -1;
167	mf->mf_auto = strealloc(mf->mf_auto, auto_opts);
168	mf->mf_mopts = strealloc(mf->mf_mopts, mopts);
169	mf->mf_remopts = strealloc(mf->mf_remopts, remopts);
170	mf->mf_info = strealloc(mf->mf_info, info);
171
172	if (mf->mf_private && mf->mf_prfree) {
173	  mf->mf_prfree(mf->mf_private);
174	  mf->mf_private = 0;
175	}
176
177	fs = ops->ffserver ? (*ops->ffserver) (mf) : (fserver *) NULL;
178	if (mf->mf_server)
179	  free_srvr(mf->mf_server);
180	mf->mf_server = fs;
181      }
182      return dup_mntfs(mf);
183    } /* end of "if (STREQ(mf-> ..." */
184  } /* end of ITER */
185
186  return 0;
187}
188
189
190/* find a matching mntfs in our list, create a new one if none is found */
191mntfs *
192find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
193{
194  mntfs *mf = locate_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts);
195  if (mf)
196    return mf;
197
198  return alloc_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts);
199}
200
201
202mntfs *
203new_mntfs(void)
204{
205  return alloc_mntfs(&amfs_error_ops, (am_opts *) 0, "//nil//", ".", "", "", "");
206}
207
208
209static void
210uninit_mntfs(mntfs *mf)
211{
212  if (mf->mf_auto)
213    XFREE(mf->mf_auto);
214  if (mf->mf_mopts)
215    XFREE(mf->mf_mopts);
216  if (mf->mf_remopts)
217    XFREE(mf->mf_remopts);
218  if (mf->mf_info)
219    XFREE(mf->mf_info);
220  if (mf->mf_private && mf->mf_prfree)
221    (*mf->mf_prfree) (mf->mf_private);
222
223  if (mf->mf_mount)
224    XFREE(mf->mf_mount);
225
226  /*
227   * Clean up the file server
228   */
229  if (mf->mf_server)
230    free_srvr(mf->mf_server);
231
232  /*
233   * Don't do a callback on this mount
234   */
235  if (mf->mf_cid) {
236    untimeout(mf->mf_cid);
237    mf->mf_cid = 0;
238  }
239}
240
241
242static void
243discard_mntfs(voidp v)
244{
245  mntfs *mf = v;
246
247  rem_que(&mf->mf_q);
248
249  /*
250   * Free memory
251   */
252  uninit_mntfs(mf);
253  XFREE(mf);
254
255  --mntfs_allocated;
256}
257
258
259void
260flush_mntfs(void)
261{
262  mntfs *mf;
263
264  mf = AM_FIRST(mntfs, &mfhead);
265  while (mf != HEAD(mntfs, &mfhead)) {
266    mntfs *mf2 = mf;
267    mf = NEXT(mntfs, mf);
268    if (mf2->mf_refc == 0 && mf2->mf_cid)
269      discard_mntfs(mf2);
270  }
271}
272
273
274void
275free_mntfs(opaque_t arg)
276{
277  mntfs *mf = (mntfs *) arg;
278
279  dlog("free_mntfs <%s> type %s mf_refc %d flags %x",
280       mf->mf_mount, mf->mf_ops->fs_type, mf->mf_refc, mf->mf_flags);
281
282  /*
283   * We shouldn't ever be called to free something that has
284   * a non-positive refcount.  Something is badly wrong if
285   * we have been!  Ignore the request for now...
286   */
287  if (mf->mf_refc <= 0) {
288    plog(XLOG_ERROR, "IGNORING free_mntfs for <%s>: refc %d, flags %x (bug?)",
289         mf->mf_mount, mf->mf_refc, mf->mf_flags);
290    return;
291  }
292
293  /* don't discard last reference of a restarted/kept mntfs */
294  if (mf->mf_refc == 1 && mf->mf_flags & MFF_RSTKEEP) {
295    plog(XLOG_ERROR, "IGNORING free_mntfs for <%s>: refc %d, flags %x (restarted)",
296         mf->mf_mount, mf->mf_refc, mf->mf_flags);
297    return;
298  }
299
300  if (--mf->mf_refc == 0) {
301    if (mf->mf_flags & MFF_MOUNTED) {
302      int quoted;
303      mf->mf_flags &= ~MFF_MOUNTED;
304
305      /*
306       * Record for posterity
307       */
308      quoted = strchr(mf->mf_info, ' ') != 0;	/* cheap */
309      plog(XLOG_INFO, "%s%s%s %sed fstype %s from %s",
310	   quoted ? "\"" : "",
311	   mf->mf_info,
312	   quoted ? "\"" : "",
313	   mf->mf_error ? "discard" : "unmount",
314	   mf->mf_ops->fs_type, mf->mf_mount);
315    }
316
317    if (mf->mf_fsflags & FS_DISCARD) {
318      dlog("Immediately discarding mntfs for %s", mf->mf_mount);
319      discard_mntfs(mf);
320
321    } else {
322
323      if (mf->mf_flags & MFF_RESTART) {
324	dlog("Discarding remount hook for %s", mf->mf_mount);
325      } else {
326	dlog("Discarding last mntfs reference to %s fstype %s",
327	     mf->mf_mount, mf->mf_ops->fs_type);
328      }
329      if (mf->mf_flags & (MFF_MOUNTED | MFF_MOUNTING | MFF_UNMOUNTING))
330	dlog("mntfs reference for %s still active", mf->mf_mount);
331      mf->mf_cid = timeout(ALLOWED_MOUNT_TIME, discard_mntfs, (voidp) mf);
332    }
333  }
334}
335
336
337mntfs *
338realloc_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
339{
340  mntfs *mf2;
341
342  if (mf->mf_refc == 1 &&
343      mf->mf_flags & MFF_RESTART &&
344      STREQ(mf->mf_mount, mp)) {
345    /*
346     * If we are inheriting then just return
347     * the same node...
348     */
349    return mf;
350  }
351
352  /*
353   * Re-use the existing mntfs if it is mounted.
354   * This traps a race in nfsx.
355   */
356  if (mf->mf_ops != &amfs_error_ops &&
357      (mf->mf_flags & MFF_MOUNTED) &&
358      !FSRV_ISDOWN(mf->mf_server)) {
359    mf->mf_fo = mo;
360    return mf;
361  }
362
363  mf2 = find_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts);
364  free_mntfs(mf);
365  return mf2;
366}
367