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