diff options
Diffstat (limited to 'tmpfs')
-rw-r--r-- | tmpfs/dir.c | 86 | ||||
-rw-r--r-- | tmpfs/node.c | 22 |
2 files changed, 71 insertions, 37 deletions
diff --git a/tmpfs/dir.c b/tmpfs/dir.c index 8c661a1b..e8776e39 100644 --- a/tmpfs/dir.c +++ b/tmpfs/dir.c @@ -62,7 +62,8 @@ diskfs_get_directs (struct node *dp, int entry, int n, >= offsetof (struct dirent, d_name)); if (bufsiz == 0) - bufsiz = dp->dn_stat.st_size + 2 * offsetof (struct dirent, d_name[4]); + bufsiz = dp->dn_stat.st_size + + 2 * ((offsetof (struct dirent, d_name[3]) + 7) & ~7); if (bufsiz > *datacnt) { *data = mmap (0, bufsiz, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); @@ -108,19 +109,26 @@ diskfs_get_directs (struct node *dp, int entry, int n, for (d = dp->dn->u.dir.entries; i < entry && d != 0; d = d->next) ++i; + if (i < entry) + { + assert (d == 0); + *datacnt = 0; + *amt = 0; + return 0; + } + /* Now fill in the buffer with real entries. */ - for (; d != 0; d = d->next) + for (; d != 0; d = d->next, i++) { - if ((char *) entp - *data >= bufsiz || (n >= 0 && ++i > n)) + size_t rlen = (offsetof (struct dirent, d_name[1]) + d->namelen + 7) & ~7; + if (rlen + (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; - ++i; + entp->d_reclen = rlen; + entp = (void *) entp + rlen; } *datacnt = (char *) entp - *data; @@ -158,6 +166,9 @@ diskfs_lookup_hard (struct node *dp, const size_t namelen = strlen (name); struct tmpfs_dirent *d, **prevp; + if (type == REMOVE || type == RENAME) + assert (np); + if (namelen == 1 && name[0] == '.') { if (np != 0) @@ -170,40 +181,54 @@ diskfs_lookup_hard (struct node *dp, if (namelen == 2 && name[0] == '.' && name[1] == '.') { struct disknode *dddn = dp->dn->u.dir.dotdot; - struct node *ddnp = 0; error_t err; assert (np != 0); if (dddn == 0) /* root directory */ return EAGAIN; - err = diskfs_cached_lookup ((int) dddn, &ddnp); - switch (type) - { - case LOOKUP|SPEC_DOTDOT: - diskfs_nput (dp); - default: - if (!err) - { - diskfs_nref (ddnp); - mutex_lock (&ddnp->lock); - } - case REMOVE|SPEC_DOTDOT: - case RENAME|SPEC_DOTDOT: - *np = ddnp; + if (type == (REMOVE|SPEC_DOTDOT) || type == (RENAME|SPEC_DOTDOT)) + { + *np = *dddn->hprevp; + assert (*np); + assert ((*np)->dn == dddn); + assert (*dddn->hprevp == *np); + return 0; + } + else + { + mutex_unlock (&dp->lock); + err = diskfs_cached_lookup ((int) dddn, np); + + if (type == (LOOKUP|SPEC_DOTDOT)) + diskfs_nrele (dp); + else + mutex_lock (&dp->lock); + + if (err) + *np = 0; + + return err; } - return err; } 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); + if (ds) + ds->prevp = prevp; + + if (np) + return diskfs_cached_lookup ((ino_t) d->dn, np); + else + return 0; } - ds->prevp = prevp; + if (ds) + ds->prevp = prevp; + if (np) + *np = 0; return ENOENT; } @@ -214,10 +239,12 @@ diskfs_direnter_hard (struct node *dp, const char *name, struct protid *cred) { const size_t namelen = strlen (name); - const size_t entsize = offsetof (struct tmpfs_dirent, name) + namelen + 1; + const size_t entsize + = (offsetof (struct dirent, d_name[1]) + namelen + 7) & ~7; struct tmpfs_dirent *new; - if (round_page (tmpfs_space_used + entsize) > tmpfs_page_limit) + if (round_page (tmpfs_space_used + entsize) / vm_page_size + > tmpfs_page_limit) return ENOSPC; new = malloc (entsize); @@ -251,7 +278,8 @@ 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; + const size_t entsize + = (offsetof (struct dirent, d_name[1]) + d->namelen + 7) & ~7; *ds->prevp = d->next; diff --git a/tmpfs/node.c b/tmpfs/node.c index 92768ee8..cfaa2d90 100644 --- a/tmpfs/node.c +++ b/tmpfs/node.c @@ -74,10 +74,8 @@ diskfs_free_node (struct node *np, mode_t mode) free (np->dn); np->dn = 0; - spin_lock (&diskfs_node_refcnt_lock); --num_files; tmpfs_space_used -= sizeof *np->dn; - spin_unlock (&diskfs_node_refcnt_lock); } void @@ -150,31 +148,36 @@ recompute_blocks (struct node *np) st->st_blocks = (st->st_blocks + 511) / 512; } +/* Fetch inode INUM, set *NPP to the node structure; + gain one user reference and lock the node. */ error_t diskfs_cached_lookup (int inum, struct node **npp) { struct disknode *dn = (void *) inum; struct node *np; + assert (npp); + if (dn->hprevp != 0) /* There is already a node. */ { np = *dn->hprevp; assert (np->dn == dn); assert (*dn->hprevp == np); - spin_lock (&diskfs_node_refcnt_lock); - np->references++; - spin_unlock (&diskfs_node_refcnt_lock); + + diskfs_nref (np); } else + /* Create the new node. */ { struct stat *st; - /* 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; + if (dn->hnext) + dn->hnext->dn->hprevp = &dn->hnext; dn->hprevp = &all_nodes; all_nodes = np; spin_unlock (&diskfs_node_refcnt_lock); @@ -303,6 +306,7 @@ diskfs_set_translator (struct node *np, { free (np->dn->trans); new = 0; + np->dn_stat.st_mode &= ~S_IPTRANS; } else { @@ -310,6 +314,7 @@ diskfs_set_translator (struct node *np, if (new == 0) return ENOSPC; memcpy (new, name, namelen); + np->dn_stat.st_mode |= S_IPTRANS; } adjust_used (namelen - np->dn->translen); np->dn->trans = new; @@ -400,6 +405,7 @@ diskfs_truncate (struct node *np, off_t size) adjust_used (size - np->allocsize); np->dn_stat.st_blocks += (size - np->allocsize) / 512; + np->dn_stat.st_size = size; np->allocsize = size; return 0; @@ -412,11 +418,11 @@ diskfs_truncate (struct node *np, off_t size) error_t diskfs_grow (struct node *np, off_t size, struct protid *cred) { + assert (np->dn->type == DT_REG); + 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; |