summaryrefslogtreecommitdiff
path: root/libtreefs/treefs.h
blob: 0e49ef51b1761813265936020130ae6f4e26bb10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/* Hierarchial filesystem support

   Copyright (C) 1995, 2002 Free Software Foundation, Inc.

   Written by Miles Bader <miles@gnu.org>

   This file is part of the GNU Hurd.

   The GNU Hurd is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   The GNU Hurd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#ifndef __TREEFS_H__
#define __TREEFS_H__

#include <errno.h>
#include <pthread.h>
#include <assert.h>
#include <features.h>

#include <sys/stat.h>

#include <hurd/hurd_types.h>
#include <hurd/ports.h>
#include <hurd/fshelp.h>

/* Include the hook calling macros and non-rpc hook definitions (to get
   those, include "trees-s-hooks.h").  */
#include "treefs-hooks.h"

#ifdef TREEFS_DEFINE_EI
#define TREEFS_EI
#else
#define TREEFS_EI __extern_inline
#endif

/* ---------------------------------------------------------------- */

typedef void (**treefs_hook_vector_t)();

/* A list of nodes.  */
struct treefs_node_list;

/* Each user port referring to a file points to one of these.  */
struct treefs_handle
{
  struct port_info pi;
  struct treefs_auth *auth;	/* User identification */
  struct treefs_peropen *po;	/* The io object itself */
  void *u;			/* for user use */
};

/* An authentication cookie.  */
struct treefs_auth
{
  int refs;
  uid_t *uids, *gids;
  int nuids, ngids;
  int isroot;
  void *u;			/* for user use */
};

/* Bits the user is permitted to set with io_*_openmodes */
#define TREEFS_SETTABLE_FLAGS (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK|O_NOATIME)

/* Bits that are turned off after open */
#define TREEFS_OPENONLY_FLAGS (O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK)

struct treefs_peropen
{
  int refs;
  int open_flags;
  int user_lock_state;

  /* A port to the directory through which this open file was reached.  */
  mach_port_t parent_port;

  void *u;			/* for user use */

  struct treefs_node *node;
};

/* A filesystem node in the tree.  */
struct treefs_node
{
  io_statbuf_t stat;
  struct treefs_fsys *fsys;

  struct trans_link active_trans;
  char *passive_trans;
  struct lock_box user_lock;

  pthread_mutex_t lock;
  unsigned refs, weak_refs;

  /* Node ops */
  treefs_hook_vector_t hooks;

  /* If this node is a directory, then this is the directory state.  */
  struct treefs_node_list *children;

  void *u;			/* for user use */
};

struct treefs_node_list
{
  struct treefs_node **nodes;
  unsigned short num_nodes, nodes_alloced;
  char *names;
  unsigned short names_len, names_alloced;
};

struct treefs_fsys
{
  struct port_info pi;
  pthread_mutex_t lock;

  /* The root node in this filesystem.  */
  struct treefs_node *root;

  /* The port for the node which this filesystem is translating.  */
  mach_port_t underlying_port;
  /* And stat info for it.  */
  io_statbuf_t underlying_stat;

  /* Flags from the TREEFS_FSYS_ set.  */
  int flags;
  /* Max number of symlink expansions allowed.  */
  unsigned max_symlinks;
  /* Sync interval (in seconds).  0 means all operations should be
     synchronous, any negative value means never sync.  */
  int sync_interval;

  /* Values to return from a statfs. */
  int fs_type;
  int fs_id;

  /* This is the hook vector that each new node in this filesystem starts out
     with.  */
  treefs_hook_vector_t hooks;

  /* The port bucket to which all of our ports belongs.  */
  struct ports_bucket *port_bucket;

  /* Various classes of ports we know about.  */
  struct port_class *handle_port_class;

  void *u;			/* for user use */
};

/* Filesystem flags.  */
#define TREEFS_FSYS_READONLY  	0x1

/* ---------------------------------------------------------------- */
/* In-core directory management routines (`mdir' == `memory dir').  These are
   intended for keeping non-permanent directory state.  If called on a
   non-dir, ENOTDIR is returned.  */

/* Add CHILD to DIR as NAME, replacing any existing entry.  If OLD_CHILD is
   NULL, and NAME already exists in dir, EEXIST is returned, otherwise, any
   previous child is replaced and returned in OLD_CHILD.  DIR should be
   locked.  */
error_t treefs_mdir_add (struct treefs_node *dir, char *name,
			 struct treefs_node *child,
			 struct treefs_node **old_child);

/* Remove any entry in DIR called NAME.  If there is no such entry, ENOENT is
   returned.  If OLD_CHILD is non-NULL, any removed entry is returned in it.
   DIR should be locked.  */
error_t treefs_mdir_remove (struct treefs_node *dir, char *name,
			    struct treefs_node **old_child);

/* Returns in NODE any entry called NAME in DIR, or NULL (and ENOENT) if
   there isn't such.  DIR should be locked.  */
error_t treefs_mdir_get (struct treefs_node *dir, char *name,
			 struct treefs_node **node);

/* Call FUN on each child of DIR; if FUN returns a non-zero value at any
   point, stop iterating and return that value immediately.  */
error_t treefs_mdir_for_each (struct treefs_node *dir,
			     error_t (*fun)(struct treefs_node *node));

/* ---------------------------------------------------------------- */
/* Functions for dealing with node lists.  */

/* Return a new node list, or NULL if a memory allocation error occurs.  */
struct treefs_node_list *treefs_make_node_list ();

/* Add NODE to LIST as NAME, replacing any existing entry.  If OLD_NODE is
   NULL, and an entry NAME already exists, EEXIST is returned, otherwise, any
   previous child is replaced and returned in OLD_NODE.  */
error_t treefs_node_list_add (struct treefs_node_list *list, char *name,
			      struct treefs_node *node,
			      struct treefs_node **old_node);

/* Remove any entry in LIST called NAME.  If there is no such entry, ENOENT is
   returned.  If OLD_NODE is non-NULL, any removed entry is returned in it.  */
error_t treefs_node_list_remove (struct treefs_node_list *list, char *name,
				 struct treefs_node **old_node);

/* Returns in NODE any entry called NAME in LIST, or NULL (and ENOENT) if
   there isn't such.  */
error_t treefs_node_list_get (struct treefs_node_list *list, char *name,
			      struct treefs_node **node);

/* Call FUN on each node in LIST; if FUN returns a non-zero value at any
   point, stop iterating and return that value immediately.  */
error_t treefs_node_list_for_each (struct treefs_node_list *list,
				   error_t (*fun)(char *name,
						  struct treefs_node *node));

/* ---------------------------------------------------------------- */
/* Functions for manipulating hook vectors.  */

typedef void (*treefs_hook_vector_init_t[TREEFS_NUM_HOOKS])();

extern treefs_hook_vector_init_t treefs_default_hooks;

/* Returns a copy of the treefs hook vector HOOKS, or a zero'd vector if HOOKS
   is NULL.  If HOOKS is NULL, treefs_default_hooks is used.  If a memory
   allocation error occurs, NULL is returned.  */
treefs_hook_vector_t treefs_hooks_clone (treefs_hook_vector_t hooks);

/* Copies each non-NULL entry in OVERRIDES into HOOKS.  */
void treefs_hooks_override (treefs_hook_vector_t hooks,
			    treefs_hook_vector_t overrides);

/* Sets the hook NUM in HOOKS to HOOK.  */
void treefs_hooks_set (treefs_hook_vector_t hooks,
		       unsigned num, void (*hook)());

/* ---------------------------------------------------------------- */
/* Reference counting function (largely stolen from diskfs).  */

extern pthread_spinlock_t treefs_node_refcnt_lock;

extern void treefs_node_ref (struct treefs_node *node);
extern void treefs_node_release (struct treefs_node *node);
extern void treefs_node_unref (struct treefs_node *node);
extern void treefs_node_ref_weak (struct treefs_node *node);
extern void treefs_node_release_weak (struct treefs_node *node);
extern void treefs_node_unref_weak (struct treefs_node *node);

#if defined(__USE_EXTERN_INLINES) || defined(TREEFS_DEFINE_EI)
/* Add a hard reference to a node.  If there were no hard
   references previously, then the node cannot be locked
   (because you must hold a hard reference to hold the lock). */
TREEFS_EI void
treefs_node_ref (struct treefs_node *node)
{
  int new_ref;
  pthread_spin_lock (&treefs_node_refcnt_lock);
  node->refs++;
  new_ref = (node->refs == 1);
  pthread_spin_unlock (&treefs_node_refcnt_lock);
  if (new_ref)
    {
      pthread_mutex_lock (&node->lock);
      treefs_node_new_refs (node);
      pthread_mutex_unlock (&node->lock);
    }
}

/* Unlock node NODE and release a hard reference; if this is the last
   hard reference and there are no links to the file then request
   weak references to be dropped.  */
TREEFS_EI void
treefs_node_release (struct treefs_node *node)
{
  int tried_drop_weak_refs = 0;

 loop:
  pthread_spin_lock (&treefs_node_refcnt_lock);
  assert (node->refs);
  node->refs--;
  if (node->refs + node->weak_refs == 0)
    treefs_node_drop (node);
  else if (node->refs == 0 && !tried_drop_weak_refs)
    {
      pthread_spin_unlock (&treefs_node_refcnt_lock);
      treefs_node_lost_refs (node);
      if (treefs_node_unlinked (node))
	{
	  /* There are no links.  If there are weak references that
	     can be dropped, we can't let them postpone deallocation.
	     So attempt to drop them.  But that's a user-supplied
	     routine, which might result in further recursive calls to
	     the ref-counting system.  So we have to reacquire our
	     reference around the call to forestall disaster. */
	  pthread_spin_unlock (&treefs_node_refcnt_lock);
	  node->refs++;
	  pthread_spin_unlock (&treefs_node_refcnt_lock);

	  treefs_node_try_dropping_weak_refs (node);

	  /* But there's no value in looping forever in this
	     routine; only try to drop weak references once. */
	  tried_drop_weak_refs = 1;

	  /* Now we can drop the reference back... */
	  goto loop;
	}
    }
  else
    pthread_spin_unlock (&treefs_node_refcnt_lock);
  pthread_mutex_unlock (&node->lock);
}

/* Release a hard reference on NODE.  If NODE is locked by anyone, then
   this cannot be the last hard reference (because you must hold a
   hard reference in order to hold the lock).  If this is the last
   hard reference and there are no links, then request weak references
   to be dropped.  */
TREEFS_EI void
treefs_node_unref (struct treefs_node *node)
{
  int tried_drop_weak_refs = 0;

 loop:
  pthread_spin_lock (&treefs_node_refcnt_lock);
  assert (node->refs);
  node->refs--;
  if (node->refs + node->weak_refs == 0)
    {
      pthread_mutex_lock (&node->lock);
      treefs_node_drop (node);
    }
  else if (node->refs == 0)
    {
      pthread_mutex_lock (&node->lock);
      pthread_spin_unlock (&treefs_node_refcnt_lock);
      treefs_node_lost_refs (node);
      if (treefs_node_unlinked(node) && !tried_drop_weak_refs)
	{
	  /* Same issue here as in nodeut; see that for explanation */
	  pthread_spin_unlock (&treefs_node_refcnt_lock);
	  node->refs++;
	  pthread_spin_unlock (&treefs_node_refcnt_lock);

	  treefs_node_try_dropping_weak_refs (node);
	  tried_drop_weak_refs = 1;

	  /* Now we can drop the reference back... */
	  pthread_mutex_unlock (&node->lock);
	  goto loop;
	}
      pthread_mutex_unlock (&node->lock);
    }
  else
    pthread_spin_unlock (&treefs_node_refcnt_lock);
}

/* Add a weak reference to a node. */
TREEFS_EI void
treefs_node_ref_weak (struct treefs_node *node)
{
  pthread_spin_lock (&treefs_node_refcnt_lock);
  node->weak_refs++;
  pthread_spin_unlock (&treefs_node_refcnt_lock);
}

/* Unlock node NODE and release a weak reference */
TREEFS_EI void
treefs_node_release_weak (struct treefs_node *node)
{
  pthread_spin_lock (&treefs_node_refcnt_lock);
  assert (node->weak_refs);
  node->weak_refs--;
  if (node->refs + node->weak_refs == 0)
    treefs_node_drop (node);
  else
    {
      pthread_spin_unlock (&treefs_node_refcnt_lock);
      pthread_mutex_unlock (&node->lock);
    }
}

/* Release a weak reference on NODE.  If NODE is locked by anyone, then
   this cannot be the last reference (because you must hold a
   hard reference in order to hold the lock).  */
TREEFS_EI void
treefs_node_unref_weak (struct treefs_node *node)
{
  pthread_spin_lock (&treefs_node_refcnt_lock);
  assert (node->weak_refs);
  node->weak_refs--;
  if (node->refs + node->weak_refs == 0)
    {
      pthread_mutex_lock (&node->lock);
      treefs_node_drop (node);
    }
  else
    pthread_spin_unlock (&treefs_node_refcnt_lock);
}
#endif /* Use extern inlines.  */

/* ---------------------------------------------------------------- */

/* Return in PORT a send right for a new handle, pointing at the peropen PO,
   with rights initialized from AUTH.  */
error_t
treefs_peropen_create_right (struct treefs_peropen *po,
			     struct treefs_auth *auth,
			     mach_port_t *port);

/* Return a send right for a new handle and a new peropen, pointing at NODE,
   with rights initialized from AUTH.  MODE and PARENT_PORT are used to
   initialize the corresponding fields in the new peropen.  */
error_t
treefs_node_create_right (struct treefs_node *node, int flags,
			  mach_port_t parent_port, struct treefs_auth *auth,
			  mach_port_t *port);

/* ---------------------------------------------------------------- */
/* Auth functions; copied from diskfs.  */

extern int treefs_auth_has_uid (struct treefs_auth *auth, uid_t uid);
extern int treefs_auth_in_group (struct treefs_auth *auth, gid_t gid);

#if defined(__USE_EXTERN_INLINES) || defined(TREEFS_DEFINE_EI)
/* Return nonzero iff the user identified by AUTH has uid UID. */
TREEFS_EI int
treefs_auth_has_uid (struct treefs_auth *auth, uid_t uid)
{
  int i;
  for (i = 0; i < auth->nuids; i++)
    if (auth->uids[i] == uid)
      return 1;
  return 0;
}

/* Return nonzero iff the user identified by AUTH has group GID. */
TREEFS_EI int
treefs_auth_in_group (struct treefs_auth *auth, gid_t gid)
{
  int i;
  for (i = 0; i < auth->ngids; i++)
    if (auth->gids[i] == gid)
      return 1;
  return 0;
}
#endif /* Use extern inlines.  */

/* ---------------------------------------------------------------- */
/* Helper routines for dealing with translators.  */

/* Return the active translator control port for NODE.  If there is no
   translator, active or passive, MACH_PORT_NULL is returned in CONTROL_PORT.
   If there is a translator, it is started if necessary, and returned in
   CONTROL_PORT.  *DIR_PORT should be a port right to use as the new
   translators parent directory.  If it is MACH_PORT_NULL, a port is created
   from DIR and PARENT_PORT and stored in *DIR_PORT; otherwise DIR and
   PARENT_PORT are not used.  Neither NODE or DIR should be locked when
   calling this function.  */
error_t treefs_node_get_active_trans (struct treefs_node *node,
				      struct treefs_node *dir,
				      mach_port_t parent_port,
				      mach_port_t *control_port,
				      mach_port_t *dir_port);

/* Drop the active translator CONTROL_PORT on NODE, unless it's no longer the
   current active translator, in which case just drop a reference to it.  */
void treefs_node_drop_active_trans (struct treefs_node *node,
				    mach_port_t control_port);

/* ---------------------------------------------------------------- */
/* Basic node creation.  */

/* Create a basic node, with one reference and no user-specific fields
   initialized, and return it in NODE */
error_t
treefs_create_node (struct treefs_fsys *fsys, struct treefs_node **node);

/* Immediately destroy NODE, with no user-finalization.  */
error_t treefs_free_node (struct treefs_node *node);

/* ---------------------------------------------------------------- */
/* Some global variables.  */

/* The port class used by treefs to make filesystem control ports.  */
struct port_class *treefs_fsys_port_class;

#endif /* __TREEFS_H__ */