diff options
Diffstat (limited to 'tmpfs/node.c')
-rw-r--r-- | tmpfs/node.c | 484 |
1 files changed, 484 insertions, 0 deletions
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. */ +} |