summaryrefslogtreecommitdiff
path: root/ufs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'ufs/dir.c')
-rw-r--r--ufs/dir.c988
1 files changed, 0 insertions, 988 deletions
diff --git a/ufs/dir.c b/ufs/dir.c
deleted file mode 100644
index 3c5f152a..00000000
--- a/ufs/dir.c
+++ /dev/null
@@ -1,988 +0,0 @@
-/* Directory management routines
-
- Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2007
- 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. */
-
-#include "ufs.h"
-#include "dir.h"
-
-#include <string.h>
-#include <stdio.h>
-#include <dirent.h>
-
-#undef d_ino
-
-enum slot_status
-{
- /* This means we haven't yet found room for a new entry. */
- LOOKING,
-
- /* This means that the specified entry is free and should be used. */
- TAKE,
-
- /* This means that the specified entry has enough room at the end
- to hold the new entry. */
- SHRINK,
-
- /* This means that there is enough space in the block, but not in
- any one single entry, so they all have to be shifted to make
- room. */
- COMPRESS,
-
- /* This means that the directory will have to be grown to hold the
- entry. */
- EXTEND,
-
- /* For removal and rename, this means that this is the location
- of the entry found. */
- HERE_TIS,
-};
-
-struct dirstat
-{
- /* Type of followp operation expected */
- enum lookup_type type;
-
- /* One of the statuses above */
- enum slot_status stat;
-
- /* Mapped address and length of directory */
- vm_address_t mapbuf;
- vm_size_t mapextent;
-
- /* Index of this directory block. */
- int idx;
-
- /* For stat COMPRESS, this is the address (inside mapbuf)
- of the first direct in the directory block to be compressed. */
- /* For stat HERE_TIS, SHRINK, and TAKE, this is the entry referenced. */
- struct directory_entry *entry;
-
- /* For stat HERE_TIS, type REMOVE, this is the address of the immediately
- previous direct in this directory block, or zero if this is the first. */
- struct directory_entry *preventry;
-
- /* For stat COMPRESS, this is the number of bytes needed to be copied
- in order to undertake the compression. */
- size_t nbytes;
-};
-
-const size_t diskfs_dirstat_size = sizeof (struct dirstat);
-
-/* Initialize DS such that diskfs_drop_dirstat will ignore it. */
-void
-diskfs_null_dirstat (struct dirstat *ds)
-{
- ds->type = LOOKUP;
-}
-
-static error_t
-dirscanblock (vm_address_t blockoff, struct node *dp, int idx,
- const char *name, int namelen, enum lookup_type type,
- struct dirstat *ds, ino_t *inum);
-
-/* Implement the diskfs_lookup from the diskfs library. See
- <hurd/diskfs.h> for the interface specification. */
-error_t
-diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type,
- struct node **npp, struct dirstat *ds, struct protid *cred)
-{
- error_t err;
- ino_t inum;
- int namelen;
- int spec_dotdot;
- struct node *np = 0;
- int retry_dotdot = 0;
- memory_object_t memobj;
- vm_prot_t prot =
- (type == LOOKUP) ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_WRITE);
- vm_address_t buf = 0;
- vm_size_t buflen = 0;
- int blockaddr;
- int idx, lastidx;
- int looped;
-
- if ((type == REMOVE) || (type == RENAME))
- assert (npp);
-
- if (npp)
- *npp = 0;
-
- spec_dotdot = type & SPEC_DOTDOT;
- type &= ~SPEC_DOTDOT;
-
- namelen = strlen (name);
-
- if (namelen > MAXNAMLEN)
- {
- if (ds)
- diskfs_null_dirstat (ds);
- return ENAMETOOLONG;
- }
-
- try_again:
- if (ds)
- {
- ds->type = LOOKUP;
- ds->mapbuf = 0;
- ds->mapextent = 0;
- }
- if (buf)
- {
- munmap ((caddr_t) buf, buflen);
- buf = 0;
- }
- if (ds && (type == CREATE || type == RENAME))
- ds->stat = LOOKING;
-
- /* Map in the directory contents. */
- memobj = diskfs_get_filemap (dp, prot);
-
- if (memobj == MACH_PORT_NULL)
- return errno;
-
- buf = 0;
- /* We allow extra space in case we have to do an EXTEND. */
- buflen = round_page (dp->dn_stat.st_size + DIRBLKSIZ);
- err = vm_map (mach_task_self (),
- &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
- mach_port_deallocate (mach_task_self (), memobj);
-
- inum = 0;
-
- diskfs_set_node_atime (dp);
-
- /* Start the lookup at DP->dn->dir_idx. */
- idx = dp->dn->dir_idx;
- if (idx * DIRBLKSIZ > dp->dn_stat.st_size)
- idx = 0; /* just in case */
- blockaddr = buf + idx * DIRBLKSIZ;
- looped = (idx == 0);
- lastidx = idx;
- if (lastidx == 0)
- lastidx = dp->dn_stat.st_size / DIRBLKSIZ;
-
- while (!looped || idx < lastidx)
- {
- err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
- if (!err)
- {
- dp->dn->dir_idx = idx;
- break;
- }
- if (err != ENOENT)
- {
- munmap ((caddr_t) buf, buflen);
- return err;
- }
-
- blockaddr += DIRBLKSIZ;
- idx++;
- if (blockaddr - buf >= dp->dn_stat.st_size && !looped)
- {
- /* We've gotten to the end; start back at the beginning */
- looped = 1;
- blockaddr = buf;
- idx = 0;
- }
- }
-
- diskfs_set_node_atime (dp);
- if (diskfs_synchronous)
- diskfs_node_update (dp, 1);
-
- /* If err is set here, it's ENOENT, and we don't want to
- think about that as an error yet. */
- err = 0;
-
- if (inum && npp)
- {
- if (namelen != 2 || name[0] != '.' || name[1] != '.')
- {
- if (inum == dp->dn->number)
- {
- np = dp;
- diskfs_nref (np);
- }
- else
- {
- err = diskfs_cached_lookup (inum, &np);
- if (err)
- goto out;
- }
- }
-
- /* We are looking up .. */
- /* Check to see if this is the root of the filesystem. */
- else if (dp->dn->number == 2)
- {
- err = EAGAIN;
- goto out;
- }
-
- /* We can't just do diskfs_cached_lookup, because we would then deadlock.
- So we do this. Ick. */
- else if (retry_dotdot)
- {
- /* Check to see that we got the same answer as last time. */
- if (inum != retry_dotdot)
- {
- /* Drop what we *thought* was .. (but isn't any more) and
- try *again*. */
- diskfs_nput (np);
- mutex_unlock (&dp->lock);
- err = diskfs_cached_lookup (inum, &np);
- mutex_lock (&dp->lock);
- if (err)
- goto out;
- retry_dotdot = inum;
- goto try_again;
- }
- /* Otherwise, we got it fine and np is already set properly. */
- }
- else if (!spec_dotdot)
- {
- /* Lock them in the proper order, and then
- repeat the directory scan to see if this is still
- right. */
- mutex_unlock (&dp->lock);
- err = diskfs_cached_lookup (inum, &np);
- mutex_lock (&dp->lock);
- if (err)
- goto out;
- retry_dotdot = inum;
- goto try_again;
- }
-
- /* Here below are the spec dotdot cases. */
- else if (type == RENAME || type == REMOVE)
- np = ifind (inum);
-
- else if (type == LOOKUP)
- {
- diskfs_nput (dp);
- err = diskfs_cached_lookup (inum, &np);
- if (err)
- goto out;
- }
- else
- assert (0);
- }
-
- if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING)
- {
- /* We didn't find any room, so mark ds to extend the dir */
- ds->type = CREATE;
- ds->stat = EXTEND;
- ds->idx = dp->dn_stat.st_size / DIRBLKSIZ;
- }
-
- /* Return to the user; if we can't, release the reference
- (and lock) we acquired above. */
- out:
- /* Deallocate or save the mapping. */
- if ((err && err != ENOENT)
- || !ds
- || ds->type == LOOKUP)
- {
- munmap ((caddr_t) buf, buflen);
- if (ds)
- ds->type = LOOKUP; /* set to be ignored by drop_dirstat */
- }
- else
- {
- ds->mapbuf = buf;
- ds->mapextent = buflen;
- }
-
- if (np)
- {
- assert (npp);
- if (err)
- {
- if (!spec_dotdot)
- {
- /* Normal case */
- if (np == dp)
- diskfs_nrele (np);
- else
- diskfs_nput (np);
- }
- else if (type == RENAME || type == REMOVE)
- /* We just did ifind to get np; that allocates
- no new references, so we don't have anything to do */
- ;
- else if (type == LOOKUP)
- /* We did diskfs_cached_lookup */
- diskfs_nput (np);
- }
- else
- *npp = np;
- }
-
- return err ? : inum ? 0 : ENOENT;
-}
-
-/* Scan block at address BLKADDR (of node DP; block index IDX), for
- name NAME of length NAMELEN. Args TYPE, DS are as for
- diskfs_lookup. If found, set *INUM to the inode number, else
- return ENOENT. */
-static error_t
-dirscanblock (vm_address_t blockaddr, struct node *dp, int idx,
- const char *name, int namelen, enum lookup_type type,
- struct dirstat *ds, ino_t *inum)
-{
- int nfree = 0;
- int needed = 0;
- vm_address_t currentoff, prevoff;
- struct directory_entry *entry = 0;
- int nentries = 0;
- size_t nbytes = 0;
- int looking = 0;
- int countcopies = 0;
- int consider_compress = 0;
-
- if (ds && (ds->stat == LOOKING
- || ds->stat == COMPRESS))
- {
- looking = 1;
- countcopies = 1;
- needed = DIRSIZ (namelen);
- }
-
- for (currentoff = blockaddr, prevoff = 0;
- currentoff < blockaddr + DIRBLKSIZ;
- prevoff = currentoff, currentoff += read_disk_entry (entry->d_reclen))
- {
- entry = (struct directory_entry *)currentoff;
-
- if (!entry->d_reclen
- || read_disk_entry (entry->d_reclen) % 4
- || DIRECT_NAMLEN (entry) > MAXNAMLEN
- || (currentoff + read_disk_entry (entry->d_reclen)
- > blockaddr + DIRBLKSIZ)
- || entry->d_name[DIRECT_NAMLEN (entry)]
- || DIRSIZ (DIRECT_NAMLEN (entry)) > read_disk_entry (entry->d_reclen)
- || memchr (entry->d_name, '\0', DIRECT_NAMLEN (entry)))
- {
- fprintf (stderr, "Bad directory entry: inode: %Ld offset: %zd\n",
- dp->dn->number, currentoff - blockaddr + idx * DIRBLKSIZ);
- return ENOENT;
- }
-
- if (looking || countcopies)
- {
- int thisfree;
-
- /* Count how much free space this entry has in it. */
- if (entry->d_ino == 0)
- thisfree = read_disk_entry (entry->d_reclen);
- else
- thisfree = (read_disk_entry (entry->d_reclen)
- - DIRSIZ (DIRECT_NAMLEN (entry)));
-
- /* If this isn't at the front of the block, then it will
- have to be copied if we do a compression; count the
- number of bytes there too. */
- if (countcopies && currentoff != blockaddr)
- nbytes += DIRSIZ (DIRECT_NAMLEN (entry));
-
- if (ds->stat == COMPRESS && nbytes > ds->nbytes)
- /* The previously found compress is better than
- this one, so don't bother counting any more. */
- countcopies = 0;
-
- if (thisfree >= needed)
- {
- ds->type = CREATE;
- ds->stat = read_disk_entry (entry->d_ino) == 0 ? TAKE : SHRINK;
- ds->entry = entry;
- ds->idx = idx;
- looking = countcopies = 0;
- }
- else
- {
- nfree += thisfree;
- if (nfree >= needed)
- consider_compress = 1;
- }
- }
-
- if (entry->d_ino)
- nentries++;
-
- if (DIRECT_NAMLEN (entry) == namelen
- && entry->d_name[0] == name[0]
- && entry->d_ino
- && !bcmp (entry->d_name, name, namelen))
- break;
- }
-
- if (consider_compress
- && (ds->type == LOOKING
- || (ds->type == COMPRESS && ds->nbytes > nbytes)))
- {
- ds->type = CREATE;
- ds->stat = COMPRESS;
- ds->entry = (struct directory_entry *) blockaddr;
- ds->idx = idx;
- ds->nbytes = nbytes;
- }
-
- if (currentoff >= blockaddr + DIRBLKSIZ)
- {
- int i;
- /* The name is not in this block. */
-
- /* Because we scanned the entire block, we should write
- down how many entries there were. */
- if (!dp->dn->dirents)
- {
- dp->dn->dirents = malloc ((dp->dn_stat.st_size / DIRBLKSIZ)
- * sizeof (int));
- for (i = 0; i < dp->dn_stat.st_size/DIRBLKSIZ; i++)
- dp->dn->dirents[i] = -1;
- }
- /* Make sure the count is correct if there is one now. */
- assert (dp->dn->dirents[idx] == -1
- || dp->dn->dirents[idx] == nentries);
- dp->dn->dirents[idx] = nentries;
-
- return ENOENT;
- }
-
- /* We have found the required name. */
-
- if (ds && type == CREATE)
- ds->type = LOOKUP; /* it's invalid now */
- else if (ds && (type == REMOVE || type == RENAME))
- {
- ds->type = type;
- ds->stat = HERE_TIS;
- ds->entry = entry;
- ds->idx = idx;
- ds->preventry = (struct directory_entry *) prevoff;
- }
-
- *inum = read_disk_entry (entry->d_ino);
- return 0;
-}
-
-/* Following a lookup call for CREATE, this adds a node to a directory.
- DP is the directory to be modified; NAME is the name to be entered;
- NP is the node being linked in; DS is the cached information returned
- by lookup; CRED describes the user making the call. This call may
- only be made if the directory has been held locked continuously since
- the preceding lookup call, and only if that call returned ENOENT. */
-error_t
-diskfs_direnter_hard(struct node *dp,
- const char *name,
- struct node *np,
- struct dirstat *ds,
- struct protid *cred)
-{
- struct directory_entry *new;
- int namelen = strlen (name);
- int needed = DIRSIZ (namelen);
- int oldneeded;
- vm_address_t fromoff, tooff;
- int totfreed;
- error_t err;
- size_t oldsize = 0;
-
- assert (ds->type == CREATE);
-
- dp->dn_set_mtime = 1;
-
- switch (ds->stat)
- {
- case TAKE:
- /* We are supposed to consume this slot. */
- assert (ds->entry->d_ino == 0
- && read_disk_entry (ds->entry->d_reclen) >= needed);
-
- write_disk_entry (ds->entry->d_ino, np->dn->number);
- DIRECT_NAMLEN (ds->entry) = namelen;
- if (direct_symlink_extension)
- ds->entry->d_type = IFTODT (np->dn_stat.st_mode);
- bcopy (name, ds->entry->d_name, namelen + 1);
-
- break;
-
- case SHRINK:
- /* We are supposed to take the extra space at the end
- of this slot. */
- oldneeded = DIRSIZ (DIRECT_NAMLEN (ds->entry));
- assert (read_disk_entry (ds->entry->d_reclen) - oldneeded >= needed);
-
- new = (struct directory_entry *) ((vm_address_t) ds->entry + oldneeded);
-
- write_disk_entry (new->d_ino, np->dn->number);
- write_disk_entry (new->d_reclen,
- read_disk_entry (ds->entry->d_reclen) - oldneeded);
- DIRECT_NAMLEN (new) = namelen;
- if (direct_symlink_extension)
- new->d_type = IFTODT (np->dn_stat.st_mode);
- bcopy (name, new->d_name, namelen + 1);
-
- write_disk_entry (ds->entry->d_reclen, oldneeded);
-
- break;
-
- case COMPRESS:
- /* We are supposed to move all the entries to the
- front of the block, giving each the minimum
- necessary room. This should free up enough space
- for the new entry. */
- fromoff = tooff = (vm_address_t) ds->entry;
-
- while (fromoff < (vm_address_t) ds->entry + DIRBLKSIZ)
- {
- struct directory_entry *from = (struct directory_entry *)fromoff;
- struct directory_entry *to = (struct directory_entry *) tooff;
- int fromreclen = read_disk_entry (from->d_reclen);
-
- if (from->d_ino != 0)
- {
- assert (fromoff >= tooff);
-
- bcopy (from, to, fromreclen);
- write_disk_entry (to->d_reclen, DIRSIZ (DIRECT_NAMLEN (to)));
-
- tooff += read_disk_entry (to->d_reclen);
- }
- fromoff += fromreclen;
- }
-
- totfreed = (vm_address_t) ds->entry + DIRBLKSIZ - tooff;
- assert (totfreed >= needed);
-
- new = (struct directory_entry *) tooff;
- write_disk_entry (new->d_ino, np->dn->number);
- write_disk_entry (new->d_reclen, totfreed);
- DIRECT_NAMLEN (new) = namelen;
- if (direct_symlink_extension)
- new->d_type = IFTODT (np->dn_stat.st_mode);
- bcopy (name, new->d_name, namelen + 1);
- break;
-
- case EXTEND:
- /* Extend the file. */
- assert (needed <= DIRBLKSIZ);
-
- oldsize = dp->dn_stat.st_size;
- if ((off_t)(oldsize + DIRBLKSIZ) != dp->dn_stat.st_size + DIRBLKSIZ)
- {
- /* We can't possibly map the whole directory in. */
- munmap ((caddr_t) ds->mapbuf, ds->mapextent);
- return EOVERFLOW;
- }
- while (oldsize + DIRBLKSIZ > dp->allocsize)
- {
- err = diskfs_grow (dp, oldsize + DIRBLKSIZ, cred);
- if (err)
- {
- munmap ((caddr_t) ds->mapbuf, ds->mapextent);
- return err;
- }
- }
-
- new = (struct directory_entry *) (ds->mapbuf + oldsize);
-
- dp->dn_stat.st_size = oldsize + DIRBLKSIZ;
- dp->dn_set_ctime = 1;
-
- write_disk_entry (new->d_ino, np->dn->number);
- write_disk_entry (new->d_reclen, DIRBLKSIZ);
- DIRECT_NAMLEN (new) = namelen;
- if (direct_symlink_extension)
- new->d_type = IFTODT (np->dn_stat.st_mode);
- bcopy (name, new->d_name, namelen + 1);
- break;
-
- default:
- assert (0);
- }
-
- dp->dn_set_mtime = 1;
-
- munmap ((caddr_t) ds->mapbuf, ds->mapextent);
-
- if (ds->stat != EXTEND)
- {
- /* If we are keeping count of this block, then keep the count up
- to date. */
- if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
- dp->dn->dirents[ds->idx]++;
- }
- else
- {
- int i;
- /* It's cheap, so start a count here even if we aren't counting
- anything at all. */
- if (dp->dn->dirents)
- {
- dp->dn->dirents = realloc (dp->dn->dirents,
- (dp->dn_stat.st_size / DIRBLKSIZ
- * sizeof (int)));
- for (i = oldsize / DIRBLKSIZ;
- i < dp->dn_stat.st_size / DIRBLKSIZ;
- i++)
- dp->dn->dirents[i] = -1;
-
- dp->dn->dirents[ds->idx] = 1;
- }
- else
- {
- dp->dn->dirents = malloc (dp->dn_stat.st_size / DIRBLKSIZ
- * sizeof (int));
- for (i = 0; i < dp->dn_stat.st_size / DIRBLKSIZ; i++)
- dp->dn->dirents[i] = -1;
- dp->dn->dirents[ds->idx] = 1;
- }
- }
-
- diskfs_file_update (dp, 1);
-
- return 0;
-}
-
-/* Following a lookup call for REMOVE, this removes the link from the
- directory. DP is the directory being changed and DS is the cached
- information returned from lookup. This call is only valid if the
- directory has been locked continuously since the call to lookup, and
- only if that call succeeded. */
-error_t
-diskfs_dirremove_hard(struct node *dp,
- struct dirstat *ds)
-{
- assert (ds->type == REMOVE);
- assert (ds->stat == HERE_TIS);
-
- dp->dn_set_mtime = 1;
-
- if (ds->preventry == 0)
- ds->entry->d_ino = 0;
- else
- {
- assert ((vm_address_t) ds->entry - (vm_address_t) ds->preventry
- == read_disk_entry (ds->preventry->d_reclen));
- write_disk_entry (ds->preventry->d_reclen,
- (read_disk_entry (ds->preventry->d_reclen)
- + read_disk_entry (ds->entry->d_reclen)));
- }
-
- dp->dn_set_mtime = 1;
-
- munmap ((caddr_t) ds->mapbuf, ds->mapextent);
-
- /* If we are keeping count of this block, then keep the count up
- to date. */
- if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
- dp->dn->dirents[ds->idx]--;
-
- diskfs_file_update (dp, 1);
-
- return 0;
-}
-
-
-/* Following a lookup call for RENAME, this changes the inode number
- on a directory entry. DP is the directory being changed; NP is
- the new node being linked in; DP is the cached information returned
- by lookup. This call is only valid if the directory has been locked
- continuously since the call to lookup, and only if that call
- succeeded. */
-error_t
-diskfs_dirrewrite_hard(struct node *dp,
- struct node *np,
- struct dirstat *ds)
-{
- assert (ds->type == RENAME);
- assert (ds->stat == HERE_TIS);
-
- dp->dn_set_mtime = 1;
- write_disk_entry (ds->entry->d_ino, np->dn->number);
- if (direct_symlink_extension)
- ds->entry->d_type = IFTODT (np->dn_stat.st_mode);
- dp->dn_set_mtime = 1;
-
- munmap ((caddr_t) ds->mapbuf, ds->mapextent);
-
- diskfs_file_update (dp, 1);
-
- return 0;
-}
-
-/* Tell if DP is an empty directory (has only "." and ".." entries). */
-/* This routine must be called from inside a catch_exception (). */
-int
-diskfs_dirempty(struct node *dp,
- struct protid *cred)
-{
- struct directory_entry *entry;
- vm_address_t buf, curoff;
- memory_object_t memobj;
- error_t err;
-
- memobj = diskfs_get_filemap (dp, VM_PROT_READ);
-
- if (memobj == MACH_PORT_NULL)
- /* XXX should reflect error properly */
- return 0;
-
- buf = 0;
-
- err = vm_map (mach_task_self (), &buf, dp->dn_stat.st_size, 0,
- 1, memobj, 0, 0, VM_PROT_READ, VM_PROT_READ, 0);
- mach_port_deallocate (mach_task_self (), memobj);
- assert (!err);
-
- diskfs_set_node_atime (dp);
-
- for (curoff = buf;
- curoff < buf + dp->dn_stat.st_size;
- curoff += read_disk_entry (entry->d_reclen))
- {
- entry = (struct directory_entry *) curoff;
-
- if (entry->d_ino != 0
- && (DIRECT_NAMLEN (entry) > 2
- || entry->d_name[0] != '.'
- || (entry->d_name[1] != '.'
- && entry->d_name[1] != '\0')))
- {
- munmap ((caddr_t) buf, dp->dn_stat.st_size);
- diskfs_set_node_atime (dp);
- if (diskfs_synchronous)
- diskfs_node_update (dp, 1);
- return 0;
- }
- }
- diskfs_set_node_atime (dp);
- if (diskfs_synchronous)
- diskfs_node_update (dp, 1);
- munmap ((caddr_t) buf, dp->dn_stat.st_size);
- return 1;
-}
-
-/* Make DS an invalid dirstat. */
-error_t
-diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
-{
- if (ds->type != LOOKUP)
- {
- assert (ds->mapbuf);
- munmap ((caddr_t) ds->mapbuf, ds->mapextent);
- ds->type = LOOKUP;
- }
- return 0;
-}
-
-
-/* Count the entries in directory block NB for directory DP and
- write the answer down in its dirents array. As a side affect
- fill BUF with the block. */
-static error_t
-count_dirents (struct node *dp, int nb, char *buf)
-{
- size_t amt;
- char *offinblk;
- struct directory_entry *entry;
- int count = 0;
- error_t err;
-
- assert (dp->dn->dirents);
- assert ((nb + 1) * DIRBLKSIZ <= dp->dn_stat.st_size);
-
- err = diskfs_node_rdwr (dp, buf, nb * DIRBLKSIZ, DIRBLKSIZ, 0, 0, &amt);
- if (err)
- return err;
- assert (amt == DIRBLKSIZ);
-
- for (offinblk = buf;
- offinblk < buf + DIRBLKSIZ;
- offinblk += read_disk_entry (entry->d_reclen))
- {
- entry = (struct directory_entry *) offinblk;
- if (entry->d_ino)
- count++;
- }
-
- assert (dp->dn->dirents[nb] == -1 || dp->dn->dirents[nb] == count);
- dp->dn->dirents[nb] = count;
- return 0;
-}
-
-/* Implement the disikfs_get_directs callback as described in
- <hurd/diskfs.h>. */
-error_t
-diskfs_get_directs (struct node *dp,
- int entry,
- int nentries,
- char **data,
- size_t *datacnt,
- vm_size_t bufsiz,
- int *amt)
-{
- int blkno;
- int nblks;
- int curentry;
- char buf[DIRBLKSIZ];
- char *bufp;
- int bufvalid;
- error_t err;
- int i;
- char *datap;
- struct directory_entry *entryp;
- int allocsize;
- size_t checklen;
- struct dirent *userp;
-
- nblks = dp->dn_stat.st_size/DIRBLKSIZ;
-
- if (!dp->dn->dirents)
- {
- dp->dn->dirents = malloc (nblks * sizeof (int));
- for (i = 0; i < nblks; i++)
- dp->dn->dirents[i] = -1;
- }
-
- /* Scan through the entries to find ENTRY. If we encounter
- a -1 in the process then stop to fill it. When we run
- off the end, ENTRY is too big. */
- curentry = 0;
- bufvalid = 0;
- for (blkno = 0; blkno < nblks; blkno++)
- {
- if (dp->dn->dirents[blkno] == -1)
- {
- err = count_dirents (dp, blkno, buf);
- if (err)
- return err;
- bufvalid = 1;
- }
-
- if (curentry + dp->dn->dirents[blkno] > entry)
- /* ENTRY starts in this block. */
- break;
-
- curentry += dp->dn->dirents[blkno];
-
- bufvalid = 0;
- }
-
- if (blkno == nblks)
- {
- /* We reached the end of the directory without seeing ENTRY.
- This is treated as an EOF condition, meaning we return
- success with empty results. */
- *datacnt = 0;
- *amt = 0;
- return 0;
- }
-
- /* Allocate enough space to hold the maximum we might return */
- if (!bufsiz || bufsiz > dp->dn_stat.st_size)
- allocsize = round_page (dp->dn_stat.st_size);
- else
- allocsize = round_page (bufsiz);
-
- if (allocsize > *datacnt)
- *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
-
- /* Set bufp appropriately */
- bufp = buf;
- if (curentry != entry)
- {
- /* Look through the block to find out where to start,
- setting bufp appropriately. */
- if (!bufvalid)
- {
- err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZ, DIRBLKSIZ,
- 0, 0, &checklen);
- if (err)
- return err;
- assert (checklen == DIRBLKSIZ);
- bufvalid = 1;
- }
- for (i = 0, bufp = buf;
- i < entry - curentry && bufp - buf < DIRBLKSIZ;
- (bufp
- += read_disk_entry (((struct directory_entry *)bufp)->d_reclen)),
- i++)
- ;
- /* Make sure we didn't run off the end. */
- assert (bufp - buf < DIRBLKSIZ);
- }
-
- i = 0;
- datap = *data;
-
- /* Copy the entries, one at a time. */
- while (((nentries == -1) || (i < nentries))
- && (!bufsiz || (datap - *data < bufsiz) )
- && blkno < nblks)
- {
- if (!bufvalid)
- {
- err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZ, DIRBLKSIZ,
- 0, 0, &checklen);
- if (err)
- return err;
- assert (checklen == DIRBLKSIZ);
- bufvalid = 1;
- bufp = buf;
- }
-
- entryp = (struct directory_entry *)bufp;
-
- if (entryp->d_ino)
- {
- userp = (struct dirent *) datap;
-
- userp->d_fileno = read_disk_entry (entryp->d_ino);
- userp->d_reclen = DIRSIZ (DIRECT_NAMLEN (entryp));
- userp->d_namlen = DIRECT_NAMLEN (entryp);
- bcopy (entryp->d_name, userp->d_name, DIRECT_NAMLEN (entryp) + 1);
- userp->d_type = DT_UNKNOWN; /* until fixed */
- i++;
- datap += DIRSIZ (DIRECT_NAMLEN (entryp));
- }
-
- bufp += read_disk_entry (entryp->d_reclen);
- if (bufp - buf == DIRBLKSIZ)
- {
- blkno++;
- bufvalid = 0;
- }
- }
-
- /* We've copied all we can. If we allocated our own array
- but didn't fill all of it, then free whatever memory we didn't use. */
- if (allocsize > *datacnt)
- {
- if (round_page (datap - *data) < allocsize)
- munmap (*data + round_page (datap - *data),
- allocsize - round_page (datap - *data));
- }
-
- /* Set variables for return */
- *datacnt = datap - *data;
- *amt = i;
- return 0;
-}