summaryrefslogtreecommitdiff
path: root/ext2fs
diff options
context:
space:
mode:
authorMiles Bader <miles@gnu.org>1996-02-06 22:09:54 +0000
committerMiles Bader <miles@gnu.org>1996-02-06 22:09:54 +0000
commit096e04d860668366756f202e5a95346020e1376f (patch)
treea472a4b0c71d47189bf58590b22bfc7f1fe00d70 /ext2fs
parent6a8333b229368cb1d2d126fd9d344dab79a51749 (diff)
(diskfs_get_directs): When BUFSIZ is 0, allocate enough extra space over the
directory size to account for the worst case difference between the ext2 and canonical formats.
Diffstat (limited to 'ext2fs')
-rw-r--r--ext2fs/dir.c43
1 files changed, 39 insertions, 4 deletions
diff --git a/ext2fs/dir.c b/ext2fs/dir.c
index 06125d70..1467f70d 100644
--- a/ext2fs/dir.c
+++ b/ext2fs/dir.c
@@ -1,6 +1,6 @@
/* Directory management routines
- Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
Converted for ext2fs by Miles Bader <miles@gnu.ai.mit.edu>
@@ -23,6 +23,7 @@
#include <string.h>
#include <stdio.h>
#include <dirent.h>
+#include <stddef.h>
/* This isn't quite right because a file system block may straddle several
device blocks, and so a write failure between writing two device blocks
@@ -349,7 +350,7 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name,
entry = (struct ext2_dir_entry *)currentoff;
if (!entry->rec_len
- || entry->rec_len % 4
+ || entry->rec_len % EXT2_DIR_PAD
|| entry->name_len > EXT2_NAME_LEN
|| currentoff + entry->rec_len > blockaddr + DIRBLKSIZ
|| EXT2_DIR_REC_LEN (entry->name_len) > entry->rec_len
@@ -769,6 +770,10 @@ count_dirents (struct node *dp, int nb, char *buf)
return 0;
}
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+
/* Implement the disikfs_get_directs callback as described in
<hurd/diskfs.h>. */
error_t
@@ -805,7 +810,25 @@ diskfs_get_directs (struct node *dp,
/* 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);
+ /* Allocate enough to return the entire directory. Since ext2's
+ directory format is different than the format used to return the
+ entries, we allocate enough to hold the on disk directory plus
+ whatever extra would be necessary in the worst-case. */
+ {
+ /* The minimum size of an ext2fs directory entry. */
+ size_t min_entry_size = EXT2_DIR_REC_LEN (0);
+ /* The minimum size of a returned dirent entry. The +1 is for '\0'. */
+ size_t min_dirent_size = offsetof (struct dirent, d_name) + 1;
+ /* The maximum possible number of ext2fs dir entries in this dir. */
+ size_t max_entries = dp->dn_stat.st_size / min_entry_size;
+ /* The maximum difference in size per directory entry. */
+ size_t entry_extra =
+ DIRENT_ALIGN
+ + (min_dirent_size > min_entry_size
+ ? min_dirent_size - min_entry_size : 0);
+
+ allocsize = round_page (dp->dn_stat.st_size + max_entries * entry_extra);
+ }
else
allocsize = round_page (bufsiz);
@@ -897,7 +920,19 @@ diskfs_get_directs (struct node *dp,
/* Length is structure before the name + the name + '\0', all
padded to a four-byte alignment. */
rec_len =
- (((char *)&userp->d_name - (char *)userp) + name_len + 1 + 3) & ~3;
+ ((offsetof (struct dirent, d_name)
+ + name_len + 1
+ + (DIRENT_ALIGN - 1))
+ & ~(DIRENT_ALIGN - 1));
+
+ /* See if this record would run over the end of the return buffer. */
+ if (bufsiz == 0)
+ /* It shouldn't ever, as we calculated the worst case size. */
+ assert (datap + rec_len <= *data + allocsize);
+ else
+ /* It's ok if it does, just leave off returning this entry. */
+ if (datap + rec_len > *data + allocsize)
+ break;
userp->d_fileno = entryp->inode;
userp->d_reclen = rec_len;