summaryrefslogtreecommitdiff
path: root/tmpfs
diff options
context:
space:
mode:
Diffstat (limited to 'tmpfs')
-rw-r--r--tmpfs/ChangeLog3
-rw-r--r--tmpfs/Makefile29
-rw-r--r--tmpfs/dir.c239
-rw-r--r--tmpfs/node.c484
-rw-r--r--tmpfs/tmpfs.c288
-rw-r--r--tmpfs/tmpfs.h89
6 files changed, 1132 insertions, 0 deletions
diff --git a/tmpfs/ChangeLog b/tmpfs/ChangeLog
new file mode 100644
index 00000000..c7d8381e
--- /dev/null
+++ b/tmpfs/ChangeLog
@@ -0,0 +1,3 @@
+2000-12-26 Roland McGrath <roland@frob.com>
+
+ * Makefile, tmpfs.c, tmpfs.h, node.c, dir.c: New files.
diff --git a/tmpfs/Makefile b/tmpfs/Makefile
new file mode 100644
index 00000000..b168b6e0
--- /dev/null
+++ b/tmpfs/Makefile
@@ -0,0 +1,29 @@
+# Makefile for tmpfs
+#
+# Copyright (C) 2000 Free Software Foundation, Inc.
+#
+# This program 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.
+#
+# This program 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.
+
+dir := tmpfs
+makemode := server
+
+target = tmpfs
+SRCS = tmpfs.c node.c dir.c
+OBJS = $(SRCS:.c=.o)
+LCLHDRS = tmpfs.h
+# XXX The shared libdiskfs requires libstore even though we don't use it here.
+HURDLIBS = diskfs pager iohelp fshelp store threads ports ihash shouldbeinlibc
+
+include ../Makeconf
diff --git a/tmpfs/dir.c b/tmpfs/dir.c
new file mode 100644
index 00000000..595e5437
--- /dev/null
+++ b/tmpfs/dir.c
@@ -0,0 +1,239 @@
+/* Directories for tmpfs.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+
+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 the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "tmpfs.h"
+#include <stdlib.h>
+
+error_t
+diskfs_init_dir (struct node *dp, struct node *pdp, struct protid *cred)
+{
+ dp->dn->u.dir.dotdot = pdp->dn;
+ dp->dn->u.dir.entries = 0;
+ return 0;
+}
+
+error_t
+diskfs_clear_directory (struct node *dp, struct node *pdp,
+ struct protid *cred)
+{
+ if (dp->dn->u.dir.entries != 0)
+ return ENOTEMPTY;
+ assert (dp->dn_stat.st_size == 0);
+ assert (dp->dn->u.dir.dotdot == pdp->dn);
+ return 0;
+}
+
+int
+diskfs_dirempty (struct node *dp, struct protid *cred)
+{
+ return dp->dn->u.dir.entries == 0;
+}
+
+error_t
+diskfs_get_directs (struct node *dp, int entry, int n,
+ char **data, u_int *datacnt,
+ vm_size_t bufsiz, int *amt)
+{
+ struct tmpfs_dirent *d;
+ struct dirent *entp;
+ int i;
+
+ assert (offsetof (struct tmpfs_dirent, name)
+ >= offsetof (struct dirent, d_name));
+
+ if (bufsiz == 0)
+ bufsiz = dp->dn_stat.st_size;
+ if (bufsiz > *datacnt)
+ {
+ *data = mmap (0, bufsiz, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ entp = *data;
+ entp->d_fileno = dp->dn_stat.st_ino;
+ entp->d_type = DT_DIR;
+ entp->d_namlen = 1;
+ entp->d_name[0] = '.';
+ entp->d_name[1] = '\0';
+ entp->d_reclen = (&entp->d_name[2] - (char *) entp + 7) & ~7;
+ entp = (void *) entp + entp->d_reclen;
+ entp->d_fileno = (ino_t) dp->dn->u.dir.dotdot;
+ entp->d_type = DT_DIR;
+ entp->d_namlen = 2;
+ entp->d_name[0] = '.';
+ entp->d_name[1] = '.';
+ entp->d_name[2] = '\0';
+ entp->d_reclen = (&entp->d_name[3] - (char *) entp + 7) & ~7;
+ entp = (void *) entp + entp->d_reclen;
+
+ d = dp->dn->u.dir.entries;
+ for (i = 2; i < entry && d != 0; ++i)
+ d = d->next;
+
+ for (i = 2; d != 0; d = d->next)
+ {
+ if ((char *) entp - *data >= bufsiz || (n >= 0 && ++i > n))
+ break;
+ entp->d_fileno = (ino_t) d->dn;
+ entp->d_type = DT_UNKNOWN;
+ entp->d_namlen = d->namelen;
+ memcpy (entp->d_name, d->name, d->namelen + 1);
+ entp->d_reclen = ((&entp->d_name[d->namelen + 1] - (char *) entp + 7)
+ & ~7);
+ entp = (void *) entp + entp->d_reclen;
+ }
+
+ *datacnt = (char *) entp - *data;
+ *amt = i;
+
+ return 0;
+}
+
+
+struct dirstat
+{
+ struct tmpfs_dirent **prevp;
+};
+const size_t diskfs_dirstat_size = sizeof (struct dirstat);
+
+void
+diskfs_null_dirstat (struct dirstat *ds)
+{
+ ds->prevp = 0;
+}
+
+error_t
+diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
+{
+ /* No need to clear the pointers. */
+ return 0;
+}
+
+error_t
+diskfs_lookup_hard (struct node *dp,
+ const char *name, enum lookup_type type,
+ struct node **np, struct dirstat *ds,
+ struct protid *cred)
+{
+ const size_t namelen = strlen (name);
+ struct tmpfs_dirent *d, **prevp;
+
+ if (namelen == 1 && name[0] == '.')
+ {
+ if (np != 0)
+ {
+ *np = dp;
+ diskfs_nref (dp);
+ }
+ return 0;
+ }
+ if (namelen == 2 && name[0] == '.' && name[1] == '.')
+ {
+ struct disknode *ddnp = dp->dn->dir.dotdot;
+ assert (np != 0);
+ if (ddnp == 0) /* root directory */
+ return EAGAIN;
+ switch (type)
+ {
+ case LOOKUP|SPEC_DOTDOT:
+ diskfs_nput (dp);
+ default:
+ diskfs_nref (ddnp);
+ mutex_lock (&ddnp->lock);
+ case REMOVE|SPEC_DOTDOT:
+ case RENAME|SPEC_DOTDOT:
+ *np = ddnp;
+ }
+ return 0;
+ }
+
+ for (d = *(prevp = &dp->dn->u.dir.entries); d != 0;
+ d = *(prevp = &d->next))
+ if (d->namelen == namelen && !memcmp (d->name, name, namelen))
+ {
+ ds->prevp = prevp;
+ return diskfs_cached_lookup ((ino_t) d->dn, np);
+ }
+
+ ds->prevp = prevp;
+ return ENOENT;
+}
+
+
+error_t
+diskfs_direnter_hard (struct node *dp, const char *name,
+ struct node *np, struct dirstat *ds,
+ struct protid *cred)
+{
+ const size_t namelen = strlen (name);
+ const size_t entsize = offsetof (struct tmpfs_dirent, name) + namelen + 1;
+ struct tmpfs_dirent *new;
+
+ if (round_page (tmpfs_space_used + entsize) > tmpfs_page_limit)
+ return ENOSPC;
+
+ new = malloc (entsize);
+ if (new == 0)
+ return ENOSPC;
+
+ new->next = 0;
+ new->dn = np->dn;
+ new->namelen = namelen;
+ memcpy (new->name, name, namelen + 1);
+ *ds->prevp = new;
+
+ dp->dn_stat.st_size += entsize;
+ adjust_used (entsize);
+
+ dp->dn_stat.st_blocks = ((sizeof *dp->dn + dp->dn->translen
+ + dp->dn_stat.st_size + 511)
+ / 512);
+ return 0;
+}
+
+error_t
+diskfs_dirrewrite_hard (struct node *dp, struct node *np,
+ struct dirstat *ds)
+{
+ (*ds->prevp)->dn = np->dn;
+ return 0;
+}
+
+error_t
+diskfs_dirremove_hard (struct node *dp, struct dirstat *ds)
+{
+ struct tmpfs_dirent *d = *ds->prevp;
+ const size_t entsize = &d->name[d->namelen + 1] - (char *) d;
+
+ *ds->prevp = d->next;
+
+ if (dp->dirmod_reqs != 0)
+ diskfs_notice_dirchange (dp, DIR_CHANGED_UNLINK, d->name);
+
+ free (d);
+
+ adjust_used (-entsize);
+ dp->dn_stat.st_size -= entsize;
+ dp->dn_stat.st_blocks = ((sizeof *dp->dn + dp->dn->translen
+ + dp->dn_stat.st_size + 511)
+ / 512);
+
+ return 0;
+}
diff --git a/tmpfs/node.c b/tmpfs/node.c
new file mode 100644
index 00000000..b06fc3d0
--- /dev/null
+++ b/tmpfs/node.c
@@ -0,0 +1,484 @@
+/* Node state and file contents for tmpfs.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+
+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 the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "tmpfs.h"
+#include <stdlib.h>
+
+unsigned int num_files;
+static unsigned int gen;
+
+struct node *all_nodes;
+
+error_t
+diskfs_alloc_node (struct node *dp, mode_t mode, struct node **npp)
+{
+ struct disknode *dn;
+ struct node *np;
+ struct stat *st;
+ error_t err;
+
+ dn = calloc (1, sizeof *dn);
+ if (dn == 0)
+ return ENOSPC;
+ spin_lock (&diskfs_node_refcnt_lock);
+ if (round_page (tmpfs_space_used + sizeof *dn) / vm_page_size
+ > tmpfs_page_limit)
+ {
+ spin_unlock (&diskfs_node_refcnt_lock);
+ free (dn);
+ return ENOSPC;
+ }
+ dn->gen = gen++;
+ ++num_files;
+ tmpfs_space_used += sizeof *dn;
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ dn->type = IFTODT (mode & S_IFMT);
+ return diskfs_cached_lookup ((ino_t) dn, npp);
+}
+
+void
+diskfs_free_node (struct node *np, mode_t mode)
+{
+ switch (np->dn->type)
+ {
+ case DT_REG:
+ if (np->dn->u.reg.memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), np->dn->u.reg.memobj);
+ break;
+ case DT_DIR:
+ assert (np->dn->u.dir.entries == 0);
+ break;
+ case DT_LNK:
+ free (np->dn->u.lnk);
+ break;
+ }
+ *np->dn->hprevp = np->dn->hnext;
+ free (np->dn);
+ np->dn = 0;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ --num_files;
+ tmpfs_space_used -= sizeof *dn;
+ spin_unlock (&diskfs_node_refcnt_lock);
+}
+
+void
+diskfs_node_norefs (struct node *np)
+{
+ if (np->dn != 0)
+ {
+ /* We don't bother to do this in diskfs_write_disknode, since it only
+ ever matters here. The node state goes back into the `struct
+ disknode' while it has no associated diskfs node. */
+
+ np->dn->size = np->dn_stat.st_size;
+ np->dn->mode = np->dn_stat.st_mode;
+ np->dn->nlink = np->dn_stat.st_nlink;
+ np->dn->uid = np->dn_stat.st_uid;
+ np->dn->author = np->dn_stat.st_author;
+ np->dn->gid = np->dn_stat.st_gid;
+ np->dn->atime = np->dn_stat.st_atime;
+ np->dn->mtime = np->dn_stat.st_mtime;
+ np->dn->ctime = np->dn_stat.st_ctime;
+ np->dn->flags = np->dn_stat.st_flags;
+
+ switch (np->dn->type)
+ {
+ case DT_REG:
+ assert (np->allocsize % vm_page_size == 0);
+ np->dn->u.reg.allocpages = np->allocsize / vm_page_size;
+ break;
+ case DT_CHR:
+ case DT_DEV:
+ np->dn->u.chr = np->dn_stat.st_rdev;
+ break;
+ }
+
+ /* Remove this node from the cache list rooted at `all_nodes'. */
+ *np->dn->hprevp = np->dn->hnext;
+ np->dn->hnext = 0;
+ np->dn->hprevp = 0;
+ }
+
+ free (np);
+}
+
+error_t
+diskfs_cached_lookup (int inum, struct node **npp)
+{
+ struct disknode *dn = (void *) inum;
+ struct node *np;
+
+ if (dn->hnext != 0) /* There is already a node. */
+ {
+ np = (void *) dn->hnext - offsetof (struct disknode, hnext);
+ assert (np->dn == dn);
+ assert (*dn->hprevp == np);
+ spin_lock (&diskfs_node_refcnt_lock);
+ np->references++;
+ spin_unlock (&diskfs_node_refcnt_lock);
+ }
+ else
+ {
+ /* Create the new node. */
+ np = diskfs_make_node (dn);
+ np->cache_id = (ino_t) dn;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ dn->hnext = all_nodes;
+ dn->hprevp = &all_nodes;
+ all_nodes = np;
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ st = &dn->dn_stat;
+ memset (st, 0, sizeof *st);
+ st->st_fstype = FSTYPE_MEMFS;
+ st->st_fsid = getpid ();
+ st->st_blksize = vm_page_size;
+
+ st->st_ino = (ino_t) dn;
+ st->st_gen = dn->gen;
+
+ st->st_size = dn->size;
+ st->st_mode = dn->mode;
+ st->st_nlink = dn->nlink;
+ st->st_uid = dn->uid;
+ st->st_author = dn->author;
+ st->st_gid = dn->gid;
+ st->st_atime = dn->atime;
+ st->st_mtime = dn->mtime;
+ st->st_ctime = dn->ctime;
+ st->st_flags = dn->flags;
+
+ st->st_rdev = 0;
+ np->allocsize = 0;
+ st->st_blocks = sizeof *dn + dn->translen;
+ switch (dn->type)
+ {
+ case DT_REG:
+ np->allocsize = dn->u.reg.allocpages * vm_page_size;
+ st->st_blocks += np->allocsize;
+ break;
+ case DT_LNK:
+ st->st_blocks += st->st_size + 1;
+ break;
+ case DT_CHR:
+ case DT_DEV:
+ st->st_rdev = dn->u.chr;
+ break;
+ case DT_DIR:
+ st->st_blocks += dn->size;
+ break;
+ }
+ st->st_blocks = (st->st_blocks + 511) / 512;
+ }
+
+ mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+}
+
+error_t
+diskfs_node_iterate (error_t (*fun) (struct node *))
+{
+ error_t err = 0;
+ unsigned int num_nodes = 0;
+ struct node *node, **node_list, **p;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+
+ /* We must copy everything from the hash table into another data structure
+ to avoid running into any problems with the hash-table being modified
+ during processing (normally we delegate access to hash-table with
+ diskfs_node_refcnt_lock, but we can't hold this while locking the
+ individual node locks). */
+
+ for (node = all_nodes; node != 0; node = node->dn->hnext)
+ num_nodes++;
+
+ p = node_list = alloca (num_nodes * sizeof (struct node *));
+ for (node = all_nodes; node != 0; node = node->dn->hnext)
+ {
+ *p++ = node;
+ node->references++;
+ }
+
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ p = node_list;
+ while (num_nodes-- > 0)
+ {
+ node = *p++;
+ if (!err)
+ {
+ mutex_lock (&node->lock);
+ err = (*fun) (node);
+ mutex_unlock (&node->lock);
+ }
+ diskfs_nrele (node);
+ }
+
+ return err;
+}
+
+/* The user must define this function. Node NP has some light
+ references, but has just lost its last hard references. Take steps
+ so that if any light references can be freed, they are. NP is locked
+ as is the pager refcount lock. This function will be called after
+ diskfs_lost_hardrefs. */
+void
+diskfs_try_dropping_softrefs (struct node *np)
+{
+}
+
+/* The user must define this funcction. Node NP has some light
+ references but has just lost its last hard reference. NP is locked. */
+void
+diskfs_lost_hardrefs (struct node *np)
+{
+}
+
+/* The user must define this function. Node NP has just acquired
+ a hard reference where it had none previously. It is thus now
+ OK again to have light references without real users. NP is
+ locked. */
+void
+diskfs_new_hardrefs (struct node *np)
+{
+}
+
+
+
+error_t
+diskfs_get_translator (struct node *np, char **namep, u_int *namelen)
+{
+ *namelen = np->dn->translen;
+ if (*namelen == 0)
+ return 0;
+ *namep = malloc (*namelen);
+ if (*namep == 0)
+ return ENOMEM;
+ memcpy (*namep, np->dn->trans, *namelen);
+ return 0;
+}
+
+error_t
+diskfs_set_translator (struct node *np,
+ const char *name, u_int namelen,
+ struct protid *cred)
+{
+ char *new;
+ if (namelen == 0)
+ {
+ free (np->dn->trans);
+ new = 0;
+ }
+ else
+ {
+ new = realloc (np->dn->trans, namelen);
+ if (new == 0)
+ return ENOSPC;
+ memcpy (new, name, namelen);
+ }
+ adjust_used (namelen - np->dn->translen);
+ if (np->dn->translen != 0)
+ np->dn_stat.st_blocks -= (np->dn->translen + 511) / 512;
+ if (namelen != 0)
+ np->dn_stat.st_blocks += (namelen + 511) / 512;
+ np->dn->trans = new;
+ np->dn->translen = namelen;
+ return 0;
+}
+
+static error_t
+create_symlink_hook (struct node *np, const char *target)
+{
+ const size_t size = np->dn_stat.st_size + 1;
+ assert (np->dn->u.link == 0);
+ char *new = malloc (np->dn->u.lnk, size);
+ if (new == 0)
+ return ENOSPC;
+ memcpy (np->dn->u.lnk, target, size);
+ adjust_used (size);
+ return 0;
+}
+error_t (*diskfs_read_symlink_hook)(struct node *np, char *target)
+ = read_symlink_hook;
+
+static error_t
+read_symlink_hook (struct node *np, char *target)
+{
+ memcpy (target, np->dn->u.lnk, np->dn_stat.st_size + 1);
+}
+error_t (*diskfs_create_symlink_hook)(struct node *np, const char *target)
+ = create_symlink_hook;
+
+void
+diskfs_write_disknode (struct node *np, int wait)
+{
+}
+
+void
+diskfs_file_update (struct node *np, int wait)
+{
+ diskfs_node_update (np, wait);
+}
+
+error_t
+diskfs_node_reload (struct node *node)
+{
+ return 0;
+}
+
+
+/* The user must define this function. Truncate locked node NP to be SIZE
+ bytes long. (If NP is already less than or equal to SIZE bytes
+ long, do nothing.) If this is a symlink (and diskfs_shortcut_symlink
+ is set) then this should clear the symlink, even if
+ diskfs_create_symlink_hook stores the link target elsewhere. */
+error_t
+diskfs_truncate (struct node *np, off_t size)
+{
+ if (np->allocsize <= size)
+ return 0;
+
+ if (np->dn->type == DT_LNK)
+ {
+ free (np->dn->u.lnk);
+ adjust_used (size - np->dn_stat.st_size);
+ np->dn->u.lnk = 0;
+ np->dn_stat.st_size = size;
+ return 0;
+ }
+
+ assert (np->dn->type == DT_REG);
+
+ if (default_pager == MACH_PORT_NULL)
+ return EIO;
+
+ size = round_page (size);
+
+ if (np->dn->u.reg.memobj != MACH_PORT_NULL)
+ {
+ /* XXX We have no way to truncate the memory object. */
+ return 0;
+ }
+ /* Otherwise it never had any real contents. */
+
+ adjust_used (size - np->allocsize);
+ np->dn_stat.st_blocks += (size - np->allocsize) / 512;
+ np->allocsize = size;
+
+ return 0;
+}
+
+/* The user must define this function. Grow the disk allocated to locked node
+ NP to be at least SIZE bytes, and set NP->allocsize to the actual
+ allocated size. (If the allocated size is already SIZE bytes, do
+ nothing.) CRED identifies the user responsible for the call. */
+error_t
+diskfs_grow (struct node *np, off_t size, struct protid *cred)
+{
+ if (np->allocsize >= size)
+ return 0;
+
+ assert (np->dn->type == DT_REG);
+
+ size = round_page (size);
+ if (round_page (tmpfs_space_used + size) / vm_page_size > tmpfs_page_limit)
+ return ENOSPC;
+
+ if (default_pager == MACH_PORT_NULL)
+ return EIO;
+
+ adjust_used (size - np->allocsize);
+ np->dn_stat.st_blocks += (size - np->allocsize) / 512;
+ np->allocsize = size;
+ return 0;
+}
+
+mach_port_t
+diskfs_get_filemap (struct node *np, vm_prot_t prot)
+{
+ if (np->dn->type != DT_REG)
+ {
+ errno = EOPNOTSUPP; /* ? */
+ return MACH_PORT_NULL;
+ }
+
+ if (default_pager == MACH_PORT_NULL)
+ return EIO;
+
+ /* We don't bother to create the memory object until the first time we
+ need it (i.e. first mapping or i/o). This way we might have a clue
+ what size it's going to be beforehand, so we can tell the default
+ pager how big to make its bitmaps. This is just an optimization for
+ the default pager; the memory object can be expanded at any time just
+ by accessing more of it. (It also optimizes the case of empty files
+ so we might never make a memory object at all.) If a user accesses
+ areas outside the bounds of the file, he will just get to diddle the
+ contents of the future larger file. */
+ if (np->dn->u.reg.memobj == MACH_PORT_NULL)
+ {
+ error_t err = default_pager_object_create (default_pager,
+ &np->dn->u.reg.memobj,
+ np->allocsize);
+ if (err)
+ {
+ errno = err;
+ return MACH_PORT_NULL;
+ }
+ assert (np->dn->u.reg.memobj != MACH_PORT_NULL);
+ }
+
+ /* XXX always writable */
+ return np->dn->u.reg.memobj;
+}
+
+/* The user must define this function. Return a `struct pager *' suitable
+ for use as an argument to diskfs_register_memory_fault_area that
+ refers to the pager returned by diskfs_get_filemap for node NP.
+ NP is locked. */
+struct pager *
+diskfs_get_filemap_pager_struct (struct node *np)
+{
+ assert (!"fault on default pager object?");
+ return 0;
+}
+
+/* We have no pager of our own, so there is no need to worry about
+ users of it, or to shut it down. */
+int
+diskfs_pager_users ()
+{
+ return 0;
+}
+void
+diskfs_shutdown_pager ()
+{
+}
+
+/* The purpose of this is to decide that it's ok to make the fs read-only.
+ Turning a temporary filesystem read-only seem pretty useless. */
+vm_prot_t
+diskfs_max_user_pager_prot ()
+{
+ return VM_PROT_READ; /* Probable lie that lets us go read-only. */
+}
diff --git a/tmpfs/tmpfs.c b/tmpfs/tmpfs.c
new file mode 100644
index 00000000..1df0e04b
--- /dev/null
+++ b/tmpfs/tmpfs.c
@@ -0,0 +1,288 @@
+/* Main program and global state for tmpfs.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+
+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 the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "tmpfs.h"
+#include <limits.h>
+#include <version.h>
+
+char *diskfs_server_name = "tmpfs";
+char *diskfs_server_version = HURD_VERSION;
+char *diskfs_disk_name = "swap";
+
+/* We ain't got to show you no stinkin' sync'ing. */
+int diskfs_default_sync_interval = 0;
+
+/* We must supply some claimed limits, though we don't impose any new ones. */
+int diskfs_link_max = (1ULL << (sizeof (nlink_t) * CHAR_BIT)) - 1;
+int diskfs_name_max = 255; /* dirent d_namlen limit */
+int diskfs_maxsymlinks = 8;
+
+/* Yeah, baby, we do it all! */
+int diskfs_shortcut_symlink = 1;
+int diskfs_shortcurt_chrdev = 1;
+int diskfs_shortcurt_blkdev = 1;
+int diskfs_shortcurt_fifo = 1;
+int diskfs_shortcurt_ifsock = 1;
+
+struct node *diskfs_root_node;
+
+
+error_t
+diskfs_set_statfs (struct statfs *st)
+{
+ fsblkcnt_t pages;
+
+ st->f_type = FSTYPE_MEMFS;
+ st->f_fsid = getpid ();
+
+ st->f_bsize = vm_page_size;
+ st->f_blocks = tmpfs_page_limit;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ st->f_files = num_files;
+ pages = round_page (tmpfs_space_used) / vm_page_size;
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ st->f_bfree = pages < tmpfs_page_limit ? tmpfs_page_limit - pages : 0;
+ st->f_bavail = st->f_bfree;
+ st->f_ffree = st->f_bavail / sizeof (struct disknode); /* Well, sort of. */
+
+ return 0;
+}
+
+
+error_t
+diskfs_set_hypermetadata (int wait, int clean)
+{
+ /* All the state always just lives in core, so we have nothing to do. */
+ return 0;
+}
+
+void
+diskfs_sync_everything (int wait)
+{
+}
+
+error_t
+diskfs_reload_global_state ()
+{
+ return 0;
+}
+
+
+
+/* Parse a command line option. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ /* We save our parsed values in this structure, hung off STATE->hook.
+ Only after parsing all options successfully will we use these values. */
+ struct
+ {
+ off_t size;
+ } *values = state->hook;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input;
+ values = malloc (sizeof *values);
+ if (values == 0)
+ return ENOMEM;
+ state->hook = values;
+ bzero (values, sizeof *values);
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_error (state, "must supply maximum size");
+ return EINVAL;
+
+ case ARGP_KEY_ARGS:
+ if (state->argv[state->next + 1] != 0)
+ {
+ argp_error (state, "too many arguments");
+ return EINVAL;
+ }
+ else
+ {
+ char *end = NULL;
+ intmax_t size = strtoimax (state->argv[state->next], 0, &end);
+ if (end == NULL || end == arg)
+ {
+ argp_error (state, "argument must be a number");
+ return EINVAL;
+ }
+ if (size < 0)
+ {
+ argp_error (state, "negative size not meaningful");
+ return EINVAL;
+ }
+ switch (*end)
+ {
+ case 'g':
+ case 'G':
+ size <<= 10;
+ case 'm':
+ case 'M':
+ size <<= 10;
+ case 'k':
+ case 'K':
+ size <<= 10;
+ break;
+ }
+ size = (off_t) size;
+ if (size < 0)
+ {
+ argp_error (state, "size too large");
+ return EINVAL;
+ }
+ values->size = size;
+ }
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ /* All options parse successfully, so implement ours if possible. */
+ tmpfs_page_limit = values->size / vm_page_size;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Override the standard diskfs routine so we can add our own output. */
+error_t
+diskfs_append_args (char **argz, unsigned *argz_len)
+{
+ error_t err;
+
+ /* Get the standard things. */
+ err = diskfs_append_std_options (argz, argz_len);
+
+ if (!err)
+ {
+ off_t lim = tmpfs_page_limit * vm_page_size;
+ char buf[100], sfx;
+#define S(n, c) if ((lim & ((1 << n) - 1)) == 0) sfx = c, lim >>= n
+ S (30, 'G'); else S (20, 'M'); else S (10, 'K'); else sfx = '\0';
+#undef S
+ snprintf (buf, sizeof buf, "%ld%c", lim, sfx);
+ err = argz_add (argz, argz_len, buf);
+ }
+
+ return err;
+}
+
+/* Add our startup arguments to the standard diskfs set. */
+static const struct argp_child startup_children[] =
+ {{&diskfs_startup_argp}, {0}};
+static struct argp startup_argp = {0, parse_opt, "MAX-BYTES", "\
+\v\
+MAX-BYTES may be followed by k or K for kilobytes,\n\
+m or M for megabytes, g or G for gigabytes.",
+ startup_children};
+
+/* Similarly at runtime. */
+static const struct argp_child runtime_children[] =
+ {{&diskfs_std_runtime_argp}, {0}};
+static struct argp runtime_argp = {0, parse_opt, 0, 0, runtime_children};
+
+struct argp *diskfs_runtime_argp = (struct argp *)&runtime_argp;
+
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap, realnode, host_priv;
+ struct stat st;
+
+ err = argp_parse (&startup_argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
+ assert_perror (err);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ /* Get our port to the default pager. Without that,
+ we have no place to put file contents. */
+ err = get_privileged_ports (&host_priv, NULL);
+ if (err)
+ error (0, err, "Cannot get host privileged port");
+ else
+ {
+ err = vm_set_default_memory_manager (host, &default_pager);
+ mach_port_deallocate (mach_task_self (), host);
+ if (err)
+ error (0, err, "Cannot get default pager port");
+ }
+ if (default_pager == MACH_PORT_NULL)
+ error (0, 0, "files cannot have contents with no default pager port");
+
+ /* Initialize the diskfs library. Must come before any other diskfs call. */
+ err = diskfs_init_diskfs ();
+ if (err)
+ error (4, err, "init");
+
+ err = diskfs_alloc_node (0, S_IFDIR, &diskfs_root_node);
+ if (err)
+ error (4, err, "cannot create root directory");
+
+ mutex_lock (&diskfs_root_node->lock);
+
+ diskfs_spawn_first_thread ();
+
+ /* Now that we are all set up to handle requests, and diskfs_root_node is
+ set properly, it is safe to export our fsys control port to the
+ outside world. */
+ realnode = diskfs_startup_diskfs (bootstrap, 0);
+
+ /* Propagate permissions, owner, etc. from underlying node to
+ the root directory of the new (empty) filesystem. */
+ err = io_stat (realnode, &st);
+ mach_port_deallocate (mach_task_self (), realnode);
+ if (err)
+ {
+ error (0, err, "cannot stat underlying node");
+ diskfs_root_node->dn_stat.st_mode = S_IFDIR | 0777 | S_ISVTX;
+ diskfs_root_node->dn_set_ctime = 1;
+ diskfs_root_node->dn_set_mtime = 1;
+ diskfs_root_node->dn_set_atime = 1;
+ }
+ else
+ {
+ diskfs_root_node->dn_stat.st_mode = S_IFDIR | (st.st_mode &~ S_IFMT);
+ diskfs_root_node->dn_stat.st_uid = st.st_uid;
+ diskfs_root_node->dn_stat.st_author = st.st_author;
+ diskfs_root_node->dn_stat.st_gid = st.st_gid;
+ diskfs_root_node->dn_stat.st_atime = st.st_atime;
+ diskfs_root_node->dn_stat.st_mtime = st.st_mtime;
+ diskfs_root_node->dn_stat.st_ctime = st.st_ctime;
+ diskfs_root_node->dn_stat.st_flags = st.st_flags;
+ }
+
+ mutex_unlock (&diskfs_root_node->lock);
+
+ /* and so we die, leaving others to do the real work. */
+ cthread_exit (0);
+ /* NOTREACHED */
+ return 0;
+}
diff --git a/tmpfs/tmpfs.h b/tmpfs/tmpfs.h
new file mode 100644
index 00000000..19f05de5
--- /dev/null
+++ b/tmpfs/tmpfs.h
@@ -0,0 +1,89 @@
+/* Private data structures for tmpfs.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+
+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 the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _tmpfs_h
+#define _tmpfs_h 1
+
+#include <hurd/diskfs.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdint.h>
+
+struct disknode
+{
+ uint_fast8_t type; /* DT_REG et al */
+
+ unsigned int gen;
+ off_t size;
+ mode_t mode;
+ nlink_t nlink;
+ uid_t uid, author;
+ gid_t gid;
+ time_t atime, mtime, ctime;
+ unsigned int flags;
+
+ char *trans;
+ size_t translen;
+
+ union
+ {
+ char *lnk; /* malloc'd symlink target */
+ struct
+ {
+ mach_port_t memobj;
+ unsigned int allocpages; /* largest size while memobj was live */
+ } reg;
+ struct
+ {
+ mach_port_t memobj;
+ unsigned int allocpages; /* largest size while memobj was live */
+ };
+ struct
+ {
+ struct tmpfs_dirent *entries;
+ struct disknode *dotdot;
+ } dir;
+ dev_t chr, blk;
+ } u;
+
+ struct node *hnext, **hprevp;
+};
+
+struct tmpfs_dirent
+{
+ struct tmpfs_dirent *next;
+ struct disknode *dn;
+ uint8_t namelen;
+ char name[0];
+};
+
+extern unsigned int num_files;
+extern off_t tmpfs_page_limit, tmpfs_space_used;
+
+extern mach_port_t default_pager;
+
+static inline void
+adjust_used (off_t change)
+{
+ spin_lock (&diskfs_node_refcnt_lock);
+ tmpfs_space_used += change;
+ spin_unlock (&diskfs_node_refcnt_lock);
+}
+
+#endif