1 |
/* A translator for returning FS_RETRY_MAGIC strings. |
/* A translator for returning FS_RETRY_MAGIC strings. |
2 |
|
|
3 |
Copyright (C) 1999,2001,02 Free Software Foundation, Inc. |
Copyright (C) 1999,2001,02, 03 Free Software Foundation, Inc. |
4 |
|
|
5 |
This program is free software; you can redistribute it and/or |
This program is free software; you can redistribute it and/or |
6 |
modify it under the terms of the GNU General Public License as |
modify it under the terms of the GNU General Public License as |
19 |
#include <hurd.h> |
#include <hurd.h> |
20 |
#include <hurd/ports.h> |
#include <hurd/ports.h> |
21 |
#include <hurd/trivfs.h> |
#include <hurd/trivfs.h> |
22 |
|
#include <hurd/fshelp.h> |
23 |
#include <hurd/fsys.h> |
#include <hurd/fsys.h> |
24 |
#include <version.h> |
#include <version.h> |
25 |
|
|
37 |
#include <assert.h> |
#include <assert.h> |
38 |
|
|
39 |
const char *argp_program_version = STANDARD_HURD_VERSION (magic); |
const char *argp_program_version = STANDARD_HURD_VERSION (magic); |
40 |
static char args_doc[] = "MAGIC"; |
|
41 |
static char doc[] = "A translator that returns the magic retry result MAGIC."; |
/* This structure has all the state about one filesystem. |
42 |
static const struct argp_option options[] = |
It hangs off trivfs_control->hook. */ |
43 |
|
struct magic |
44 |
{ |
{ |
45 |
{"directory", 'd', 0, 0, "Provide virtual (empty) directory node"}, |
/* We chain all filesystems together so we can tell easily when they are |
46 |
{0} |
all unused. */ |
47 |
}; |
struct trivfs_control *next; |
48 |
|
|
49 |
|
/* The magic string we return for lookups. */ |
50 |
|
char *magic; |
51 |
|
|
52 |
/* The magic string we return for lookups. */ |
int directory; /* --directory flag */ |
|
static char *magic; |
|
53 |
|
|
54 |
static int directory; /* --directory flag */ |
/* Pre-fab contents of dummy directory for dir_readdir. |
55 |
|
Set up only under --directory. */ |
56 |
|
void *dirbuf; |
57 |
|
size_t dirbufsize; |
58 |
|
|
59 |
/* Pre-fab contents of dummy directory for dir_readdir. |
unsigned int nusers; /* Count of users, only with --directory. */ |
60 |
Set up only under --directory. */ |
}; |
61 |
static void *dirbuf; |
|
62 |
static size_t dirbufsize; |
static inline void |
63 |
|
free_magic (struct magic *m) |
64 |
|
{ |
65 |
|
free (m->magic); |
66 |
|
if (m->dirbuf) |
67 |
|
munmap (m->dirbuf, m->dirbufsize); |
68 |
|
free (m); |
69 |
|
} |
70 |
|
|
71 |
|
static struct trivfs_control *all_fsys; |
72 |
|
|
73 |
/* Trivfs hooks */ |
/* Trivfs hooks */ |
74 |
|
|
84 |
void |
void |
85 |
trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) |
trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) |
86 |
{ |
{ |
87 |
st->st_size = dirbufsize; |
struct magic *const m = cred->po->cntl->hook; |
88 |
|
|
89 |
|
st->st_size = m->dirbufsize; |
90 |
st->st_blocks = getpagesize () / S_BLKSIZE; |
st->st_blocks = getpagesize () / S_BLKSIZE; |
91 |
|
|
92 |
st->st_mode = ((st->st_mode & ~S_IFMT & ~ALLPERMS) |
st->st_mode = ((st->st_mode & ~S_IFMT & ~ALLPERMS) |
97 |
error_t |
error_t |
98 |
trivfs_goaway (struct trivfs_control *fsys, int flags) |
trivfs_goaway (struct trivfs_control *fsys, int flags) |
99 |
{ |
{ |
100 |
exit (0); |
struct magic *const m = fsys->hook; |
101 |
|
|
102 |
|
/* We are single-threaded, so no fancy stuff is needed here. */ |
103 |
|
|
104 |
|
if (m->nusers > 0 && !(flags & FSYS_GOAWAY_FORCE)) |
105 |
|
return EBUSY; |
106 |
|
|
107 |
|
/* No more communication with the parent filesystem. |
108 |
|
This running RPC should now be the only ref keeping FSYS alive. */ |
109 |
|
ports_destroy_right (fsys); |
110 |
|
return 0; |
111 |
} |
} |
112 |
|
|
113 |
|
|
114 |
|
/* Clean pointers in a struct trivfs_control when its last reference |
115 |
|
vanishes before it's freed. This overrides the libtrivfs version |
116 |
|
so we can clean up our hook data. */ |
117 |
|
void |
118 |
|
trivfs_clean_cntl (void *arg) |
119 |
|
{ |
120 |
|
struct trivfs_control *cntl = arg; |
121 |
|
struct magic *const m = cntl->hook; |
122 |
|
|
123 |
|
/* Remove us from the list of all filesystems. */ |
124 |
|
struct trivfs_control **lastp = &all_fsys; |
125 |
|
while (*lastp != cntl) |
126 |
|
lastp = &((struct magic *) (*lastp)->hook)->next; |
127 |
|
*lastp = m->next; |
128 |
|
|
129 |
|
if (all_fsys == 0) |
130 |
|
/* Nothing more to do in this life. */ |
131 |
|
exit (0); |
132 |
|
|
133 |
|
mach_port_destroy (mach_task_self (), cntl->filesys_id); |
134 |
|
mach_port_destroy (mach_task_self (), cntl->file_id); |
135 |
|
mach_port_deallocate (mach_task_self (), cntl->underlying); |
136 |
|
|
137 |
|
free_magic (m); |
138 |
|
} |
139 |
|
|
140 |
/* This hook is used when running without --directory; |
/* This hook is used when running without --directory; |
141 |
it circumvents basically all the trivfs machinery. */ |
it circumvents basically all the trivfs machinery. */ |
150 |
retry_type *do_retry, char *retry_name, |
retry_type *do_retry, char *retry_name, |
151 |
mach_port_t *node, mach_msg_type_name_t *node_type) |
mach_port_t *node, mach_msg_type_name_t *node_type) |
152 |
{ |
{ |
153 |
strcpy (retry_name, magic); |
struct magic *const m = cntl->hook; |
154 |
|
|
155 |
|
if (m->directory) |
156 |
|
return EAGAIN; /* Do normal trivfs getroot processing. */ |
157 |
|
|
158 |
|
strcpy (retry_name, m->magic); |
159 |
*do_retry = FS_RETRY_MAGICAL; |
*do_retry = FS_RETRY_MAGICAL; |
160 |
*node = MACH_PORT_NULL; |
*node = MACH_PORT_NULL; |
161 |
*node_type = MACH_MSG_TYPE_COPY_SEND; |
*node_type = MACH_MSG_TYPE_COPY_SEND; |
184 |
assert_perror (err); |
assert_perror (err); |
185 |
err = mach_port_deallocate (mach_task_self (), dotdot); |
err = mach_port_deallocate (mach_task_self (), dotdot); |
186 |
assert_perror (err); |
assert_perror (err); |
187 |
|
struct magic *const m = cntl->hook; |
188 |
|
m->nusers++; |
189 |
} |
} |
190 |
return err; |
return err; |
191 |
} |
} |
192 |
|
|
193 |
|
/* We have this hook only for simple tracking of the live user ports. */ |
194 |
|
static void |
195 |
|
magic_protid_destroy (struct trivfs_protid *cred) |
196 |
|
{ |
197 |
|
struct magic *const m = cred->po->cntl->hook; |
198 |
|
m->nusers--; |
199 |
|
} |
200 |
|
|
201 |
|
|
202 |
/* Do a directory lookup. */ |
/* Do a directory lookup. */ |
203 |
|
|
223 |
|
|
224 |
if (name[0] != '\0') |
if (name[0] != '\0') |
225 |
{ |
{ |
226 |
if (!directory) |
struct magic *const m = cred->po->cntl->hook; |
227 |
|
|
228 |
|
if (!m->directory) |
229 |
return ENOTDIR; |
return ENOTDIR; |
|
else |
|
|
{ |
|
|
/* We have a real lookup in the dummy directory. |
|
|
Handle `.' and `..' specially, and anything else |
|
|
gets redirected to the magical retry. */ |
|
230 |
|
|
231 |
|
/* We have a real lookup in the dummy directory. |
232 |
|
Handle `.' and `..' specially, and anything else |
233 |
|
gets redirected to the magical retry. */ |
234 |
|
|
235 |
|
while (*name == '/') |
236 |
|
++name; |
237 |
|
while (!strncmp (name, "./", 2)) |
238 |
|
{ |
239 |
|
name += 2; |
240 |
while (*name == '/') |
while (*name == '/') |
241 |
++name; |
++name; |
242 |
while (!strncmp (name, "./", 2)) |
} |
|
{ |
|
|
name += 2; |
|
|
while (*name == '/') |
|
|
++name; |
|
|
} |
|
243 |
|
|
244 |
if (!strcmp (name, "..") || !strncmp (name, "../", 3)) |
if (!strcmp (name, "..") || !strncmp (name, "../", 3)) |
245 |
{ |
{ |
246 |
name += 2; |
name += 2; |
247 |
while (*name == '/') |
while (*name == '/') |
248 |
++name; |
++name; |
249 |
strcpy (retry_name, name); |
strcpy (retry_name, name); |
250 |
*retry_type = FS_RETRY_REAUTH; |
*retry_type = FS_RETRY_REAUTH; |
251 |
*retrypt = (mach_port_t) cred->po->hook; |
*retrypt = (mach_port_t) cred->po->hook; |
252 |
*retrypt_type = MACH_MSG_TYPE_COPY_SEND; |
*retrypt_type = MACH_MSG_TYPE_COPY_SEND; |
253 |
return 0; |
return 0; |
254 |
} |
} |
255 |
else if (name[0] != '\0' && strcmp (name, ".")) |
else if (name[0] != '\0' && strcmp (name, ".")) |
256 |
|
{ |
257 |
|
if (m->magic == 0) |
258 |
|
strcpy (retry_name, name); |
259 |
|
else |
260 |
{ |
{ |
261 |
char *p = stpcpy (retry_name, magic); |
char *p = stpcpy (retry_name, m->magic); |
262 |
*p++ = '/'; |
*p++ = '/'; |
263 |
strcpy (p, name); |
strcpy (p, name); |
|
*retry_type = FS_RETRY_MAGICAL; |
|
|
*retrypt = MACH_PORT_NULL; |
|
|
*retrypt_type = MACH_MSG_TYPE_COPY_SEND; |
|
|
return 0; |
|
264 |
} |
} |
265 |
|
*retry_type = FS_RETRY_MAGICAL; |
266 |
|
*retrypt = MACH_PORT_NULL; |
267 |
|
*retrypt_type = MACH_MSG_TYPE_COPY_SEND; |
268 |
|
return 0; |
269 |
} |
} |
270 |
} |
} |
271 |
|
|
327 |
if (!cred) |
if (!cred) |
328 |
return EOPNOTSUPP; |
return EOPNOTSUPP; |
329 |
|
|
330 |
|
struct magic *const m = cred->po->cntl->hook; |
331 |
|
|
332 |
if (entry > 0) |
if (entry > 0) |
333 |
{ |
{ |
334 |
void *p; |
void *p; |
335 |
int i; |
int i; |
336 |
i = 0; |
i = 0; |
337 |
for (p = dirbuf; p < dirbuf + dirbufsize; |
for (p = m->dirbuf; p < m->dirbuf + m->dirbufsize; |
338 |
p += ((struct dirent *) p)->d_reclen) |
p += ((struct dirent *) p)->d_reclen) |
339 |
if (i++ == entry) |
if (i++ == entry) |
340 |
break; |
break; |
341 |
*data = p; |
*data = p; |
342 |
*datalen = dirbuf + dirbufsize - p; |
*datalen = m->dirbuf + m->dirbufsize - p; |
343 |
*amount = 2 - entry; |
*amount = 2 - entry; |
344 |
} |
} |
345 |
else |
else |
346 |
{ |
{ |
347 |
*data = dirbuf; |
*data = m->dirbuf; |
348 |
*datalen = dirbufsize; |
*datalen = m->dirbufsize; |
349 |
*amount = 2; |
*amount = 2; |
350 |
} |
} |
351 |
|
|
354 |
} |
} |
355 |
|
|
356 |
|
|
357 |
|
#include <hurd/paths.h> |
358 |
|
#define _SERVERS_MAGIC _SERVERS "magic" |
359 |
|
|
360 |
|
/* To whom should we try to delegate on startup? */ |
361 |
|
static const char *delegate = _SERVERS_MAGIC; |
362 |
|
|
363 |
|
static const struct argp_option options[] = |
364 |
|
{ |
365 |
|
{"directory", 'd', 0, 0, "Provide virtual (empty) directory node"}, |
366 |
|
{"use-server", 'U', "NAME", 0, |
367 |
|
"Delegate to server NAME instead of " _SERVERS_MAGIC}, |
368 |
|
{0} |
369 |
|
}; |
370 |
|
|
371 |
static error_t |
static error_t |
372 |
parse_opt (int opt, char *arg, struct argp_state *state) |
parse_opt (int opt, char *arg, struct argp_state *state) |
373 |
{ |
{ |
374 |
|
struct magic *const m = state->input; |
375 |
|
|
376 |
switch (opt) |
switch (opt) |
377 |
{ |
{ |
378 |
case 'd': |
case 'U': |
379 |
directory = 1; |
/* This is only valid for the startup options, not delegates. */ |
380 |
|
if (all_fsys != 0) |
381 |
|
return EINVAL; |
382 |
|
delegate = arg; |
383 |
return 0; |
return 0; |
384 |
|
|
385 |
|
case 'd': |
386 |
case ARGP_KEY_NO_ARGS: |
case ARGP_KEY_NO_ARGS: |
387 |
argp_usage (state); |
m->directory = 1; |
388 |
return EINVAL; |
return 0; |
389 |
|
|
390 |
case ARGP_KEY_ARGS: |
case ARGP_KEY_ARG: |
391 |
if (state->next != state->argc - 1) |
if (m->magic != 0) |
392 |
{ |
{ |
393 |
argp_usage (state); |
argp_usage (state); |
394 |
return EINVAL; |
return EINVAL; |
395 |
} |
} |
396 |
else |
m->magic = strdup (arg); |
397 |
|
return m->magic == 0 ? ENOMEM : 0; |
398 |
|
|
399 |
|
case ARGP_KEY_SUCCESS: |
400 |
|
if (m->directory) |
401 |
{ |
{ |
402 |
magic = state->argv[state->next]; |
inline struct dirent *add (struct dirent *d, const char *name) |
403 |
return 0; |
{ |
404 |
|
d->d_fileno = 2; /* random */ |
405 |
|
d->d_type = DT_DIR; |
406 |
|
d->d_namlen = strlen (name); |
407 |
|
strcpy (d->d_name, name); |
408 |
|
d->d_name[d->d_namlen] = '\0'; |
409 |
|
d->d_reclen = &d->d_name[d->d_namlen + 1] - (char *) d; |
410 |
|
d->d_reclen = ((d->d_reclen + __alignof (struct dirent) - 1) |
411 |
|
& ~(__alignof (struct dirent) - 1)); |
412 |
|
return (struct dirent *) ((char *) d + d->d_reclen); |
413 |
|
} |
414 |
|
struct dirent *d; |
415 |
|
m->dirbuf = mmap (0, getpagesize (), PROT_READ|PROT_WRITE, |
416 |
|
MAP_ANON, 0, 0); |
417 |
|
d = add (m->dirbuf, "."); |
418 |
|
d = add (d, ".."); |
419 |
|
m->dirbufsize = (char *) d - (char *) m->dirbuf; |
420 |
} |
} |
421 |
break; |
return 0; |
422 |
} |
} |
423 |
|
|
424 |
return ARGP_ERR_UNKNOWN; |
return ARGP_ERR_UNKNOWN; |
428 |
trivfs_append_args (struct trivfs_control *fsys, |
trivfs_append_args (struct trivfs_control *fsys, |
429 |
char **argz, size_t *argz_len) |
char **argz, size_t *argz_len) |
430 |
{ |
{ |
431 |
return ((directory ? argz_add (argz, argz_len, "--directory") : 0) |
struct magic *const m = fsys->hook; |
432 |
?: argz_add (argz, argz_len, magic)); |
return ((m->directory ? argz_add (argz, argz_len, "--directory") : 0) |
433 |
|
?: (m->magic ? argz_add (argz, argz_len, m->magic) : 0)); |
434 |
} |
} |
435 |
|
|
436 |
|
static struct argp argp = |
437 |
|
{ |
438 |
|
options, parse_opt, "MAGIC", |
439 |
|
"A translator that returns the magic retry result MAGIC." |
440 |
|
}; |
441 |
|
|
442 |
int |
int |
443 |
main (int argc, char **argv) |
main (int argc, char **argv) |
445 |
error_t err; |
error_t err; |
446 |
mach_port_t bootstrap; |
mach_port_t bootstrap; |
447 |
struct trivfs_control *fsys; |
struct trivfs_control *fsys; |
448 |
struct argp argp = { options, parse_opt, args_doc, doc }; |
struct magic *m = calloc (1, sizeof *m); |
449 |
|
|
450 |
argp_parse (&argp, argc, argv, 0, 0, 0); |
argp_parse (&argp, argc, argv, 0, 0, m); |
451 |
|
|
452 |
task_get_bootstrap_port (mach_task_self (), &bootstrap); |
task_get_bootstrap_port (mach_task_self (), &bootstrap); |
453 |
if (bootstrap == MACH_PORT_NULL) |
if (bootstrap == MACH_PORT_NULL) |
454 |
error (1, 0, "Must be started as a translator"); |
error (1, 0, "Must be started as a translator"); |
455 |
|
|
456 |
|
if (delegate != 0) |
457 |
|
{ |
458 |
|
/* First, try to have the canonical server sitting on /servers/magic |
459 |
|
take over for us. */ |
460 |
|
err = fshelp_delegate_translation (delegate, bootstrap, argv); |
461 |
|
if (err == 0) |
462 |
|
return 0; |
463 |
|
} |
464 |
|
|
465 |
|
/* Nope, we are doing it ourselves. */ |
466 |
|
|
467 |
|
trivfs_getroot_hook = &magic_getroot; |
468 |
|
trivfs_open_hook = &magic_open; |
469 |
|
trivfs_protid_destroy_hook = &magic_protid_destroy; |
470 |
|
|
471 |
/* Reply to our parent */ |
/* Reply to our parent */ |
472 |
err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys); |
err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys); |
473 |
mach_port_deallocate (mach_task_self (), bootstrap); |
mach_port_deallocate (mach_task_self (), bootstrap); |
474 |
if (err) |
if (err) |
475 |
error (3, err, "Contacting parent"); |
error (3, err, "Contacting parent"); |
476 |
|
fsys->hook = m; |
477 |
|
all_fsys = fsys; |
478 |
|
|
479 |
if (directory) |
/* Launch. */ |
480 |
|
while (1) |
481 |
{ |
{ |
482 |
inline struct dirent *add (struct dirent *d, const char *name) |
ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, |
483 |
|
10 * 60 * 1000); |
484 |
|
|
485 |
|
/* That returns when 10 minutes pass without an RPC. Try shutting down |
486 |
|
as if sent fsys_goaway; if we have any users who need us to stay |
487 |
|
around, this returns EBUSY and we loop to service more RPCs. */ |
488 |
|
|
489 |
|
struct trivfs_control *fs = all_fsys; |
490 |
|
do |
491 |
{ |
{ |
492 |
d->d_fileno = 2; /* random */ |
struct magic *const m = fs->hook; |
493 |
d->d_type = DT_DIR; |
struct trivfs_control *const next = m->next; |
494 |
d->d_namlen = strlen (name); |
trivfs_goaway (fs, 0); |
495 |
strcpy (d->d_name, name); |
fs = next; |
496 |
d->d_name[d->d_namlen] = '\0'; |
} while (fs != 0); |
497 |
d->d_reclen = &d->d_name[d->d_namlen + 1] - (char *) d; |
} |
498 |
d->d_reclen = ((d->d_reclen + __alignof (struct dirent) - 1) |
|
499 |
& ~(__alignof (struct dirent) - 1)); |
return 0; |
500 |
return (struct dirent *) ((char *) d + d->d_reclen); |
} |
501 |
} |
|
502 |
struct dirent *d; |
|
503 |
dirbuf = mmap (0, getpagesize (), PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); |
/* Handle delegated filesystems. */ |
504 |
d = add (dirbuf, "."); |
error_t |
505 |
d = add (d, ".."); |
trivfs_S_fsys_forward (mach_port_t server, |
506 |
dirbufsize = (char *) d - (char *) dirbuf; |
mach_port_t reply, |
507 |
|
mach_msg_type_name_t replytype, |
508 |
|
mach_port_t requestor, |
509 |
|
char *argz, size_t argz_len) |
510 |
|
{ |
511 |
|
struct trivfs_protid *cred |
512 |
|
= ports_lookup_port (all_fsys->pi.bucket, server, |
513 |
|
trivfs_protid_portclasses[0]); |
514 |
|
if (!cred) |
515 |
|
return EOPNOTSUPP; |
516 |
|
ports_port_deref (cred); |
517 |
|
|
518 |
trivfs_open_hook = &magic_open; |
/* Allocate a new structure for parameters, and parse the arguments |
519 |
|
to fill it in. */ |
520 |
|
struct magic *m = calloc (1, sizeof *m); |
521 |
|
if (!m) |
522 |
|
return ENOMEM; |
523 |
|
|
524 |
|
int argc = argz_count (argz, argz_len); |
525 |
|
char *argv[argc + 1]; |
526 |
|
argz_extract (argz, argz_len, argv); |
527 |
|
error_t err = argp_parse (&argp, argc, argv, |
528 |
|
ARGP_NO_ERRS | ARGP_NO_HELP, 0, m); |
529 |
|
if (err) |
530 |
|
{ |
531 |
|
free_magic (m); |
532 |
|
return err; |
533 |
} |
} |
|
else |
|
|
trivfs_getroot_hook = &magic_getroot; |
|
534 |
|
|
535 |
/* Launch. */ |
/* Now we are ready to start up the filesystem. Contact the parent. */ |
536 |
ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, |
struct trivfs_control *fsys; |
537 |
2 * 60 * 1000); |
err = trivfs_startup (requestor, 0, |
538 |
|
trivfs_cntl_portclasses[0], all_fsys->pi.bucket, |
539 |
|
trivfs_protid_portclasses[0], all_fsys->pi.bucket, |
540 |
|
&fsys); |
541 |
|
if (err) |
542 |
|
{ |
543 |
|
free_magic (m); |
544 |
|
return err; |
545 |
|
} |
546 |
|
mach_port_deallocate (mach_task_self (), requestor); |
547 |
|
|
548 |
|
/* The new filesystem is all hooked up. |
549 |
|
Put it on the list of all filesystems we are serving. */ |
550 |
|
m->next = all_fsys; |
551 |
|
fsys->hook = m; |
552 |
|
all_fsys = fsys; |
553 |
|
|
554 |
return 0; |
return 0; |
555 |
} |
} |