efs_dir.h revision 1.2 1 1.2 msaitoh /* $NetBSD: efs_dir.h,v 1.2 2016/07/07 06:55:42 msaitoh Exp $ */
2 1.1 rumble
3 1.1 rumble /*
4 1.1 rumble * Copyright (c) 2006 Stephen M. Rumble <rumble (at) ephemeral.org>
5 1.1 rumble *
6 1.1 rumble * Permission to use, copy, modify, and distribute this software for any
7 1.1 rumble * purpose with or without fee is hereby granted, provided that the above
8 1.1 rumble * copyright notice and this permission notice appear in all copies.
9 1.1 rumble *
10 1.1 rumble * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 rumble * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 rumble * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 rumble * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 rumble * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1.1 rumble * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1.1 rumble * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 rumble */
18 1.1 rumble
19 1.1 rumble /*
20 1.1 rumble * EFS directory block and directory entry formats.
21 1.1 rumble *
22 1.1 rumble * See IRIX dir(4)
23 1.1 rumble */
24 1.1 rumble
25 1.1 rumble #ifndef _FS_EFS_EFS_DIR_H_
26 1.1 rumble #define _FS_EFS_EFS_DIR_H_
27 1.1 rumble
28 1.1 rumble /*
29 1.1 rumble * EFS directory block (512 bytes on disk)
30 1.1 rumble */
31 1.1 rumble
32 1.1 rumble #define EFS_DIRBLK_MAGIC 0xbeef
33 1.1 rumble #define EFS_DIRBLK_SIZE EFS_BB_SIZE
34 1.1 rumble #define EFS_DIRBLK_HEADER_SIZE 4
35 1.1 rumble #define EFS_DIRBLK_SPACE_SIZE (EFS_DIRBLK_SIZE - EFS_DIRBLK_HEADER_SIZE)
36 1.1 rumble
37 1.1 rumble struct efs_dirblk {
38 1.1 rumble uint16_t db_magic; /* must be EFS_DIRBLK_MAGIC */
39 1.1 rumble uint8_t db_firstused; /* first dir entry offset (compacted) */
40 1.1 rumble uint8_t db_slots; /* total number of entry offsets */
41 1.1 rumble
42 1.1 rumble /*
43 1.1 rumble * The following db_space is used for three things:
44 1.1 rumble * 1) Array of entry offsets, one byte each, relative to the
45 1.1 rumble * efs_dirblk structure (not db_space!). These are stored right
46 1.1 rumble * shifted by one, thus providing 9 bits to address the entries.
47 1.1 rumble * 2) Array of even-sized directory entries, which exist at even
48 1.1 rumble * offsets, of course.
49 1.1 rumble * 3) Free space between the two arrays used for expanding either.
50 1.1 rumble *
51 1.1 rumble * The entry offsets exist in the lower offset range of de_space,
52 1.1 rumble * followed by efs_dirent structures higher up:
53 1.1 rumble *
54 1.1 rumble * db_space[sizeof(db_space)] _______________________ _
55 1.1 rumble * | | |
56 1.1 rumble * | efs_dirent at z << 1 | |
57 1.1 rumble * |_______________________| |
58 1.1 rumble * | | |
59 1.1 rumble * | efs_dirent at x << 1 | |-- directory
60 1.1 rumble * | | | entries
61 1.1 rumble * |_______________________| |
62 1.1 rumble * | | |
63 1.1 rumble * | efs_dirent at y << 1 | |
64 1.1 rumble * db_space[db_firstused << 1] |_______________________| _|
65 1.1 rumble * | ... |
66 1.1 rumble * | free space |
67 1.1 rumble * | ... |
68 1.1 rumble * db_space[db_slots] |_______________________| _
69 1.1 rumble * |___________z___________| |
70 1.1 rumble * |___________0___________| |-- directory
71 1.1 rumble * |___________y___________| | entry
72 1.1 rumble * db_space[0] |___________x___________| _| offsets
73 1.1 rumble *
74 1.1 rumble * In the above diagram, db_firstused would be equal to y. Note that
75 1.1 rumble * directory entry offsets need not occur in the same order as their
76 1.1 rumble * corresponding entries. The size of the offset array is indicated
77 1.1 rumble * by 'db_slots'. Unused slots in the middle of the array are zeroed.
78 1.1 rumble *
79 1.1 rumble * A range of free space between the end of the offset array and the
80 1.1 rumble * first directory entry is used for allocating new entry offsets and
81 1.1 rumble * directory entries. Its size is equal to ('db_firstused' << 1) -
82 1.1 rumble * 'db_slots'.
83 1.1 rumble *
84 1.1 rumble * When a directory entry is added, the directory offset array is
85 1.1 rumble * searched for a zeroed entry to use. If none is available and space
86 1.1 rumble * permits, it is allocated from the bottom of the free space region
87 1.1 rumble * and 'db_slots' is incremented. The space for the directory entry is
88 1.1 rumble * allocated from the top of free space, and the offset is stored.
89 1.1 rumble *
90 1.1 rumble * When a directory entry is removed, all directory entries below it
91 1.1 rumble * are moved up in order to expand the free space region. If the
92 1.1 rumble * corresponding entry offset borders the free space (it is last in the
93 1.1 rumble * array), it is coalesced into the free space region and 'db_slots' is
94 1.1 rumble * decremented.
95 1.1 rumble *
96 1.1 rumble * XXX when all entries removed, (how) do we free the dirblk?
97 1.1 rumble *
98 1.1 rumble * According to IRIX dir(4), the offset of a directory entry's offset
99 1.1 rumble * within the array of offsets does not change (say what?). That is, if
100 1.1 rumble * directory entry P's offset is contained in db_space[3], it will
101 1.1 rumble * remain in db_space[3] until it is removed. In other words, they do
102 1.1 rumble * not reshuffle the entry offsets in order to coalesce the unused
103 1.1 rumble * offset array entries into the free space region. Since we allocate
104 1.1 rumble * from zeroed ones before dipping into free space, this is typically
105 1.1 rumble * not a problem. However, it leaves open the case where many older
106 1.1 rumble * files are removed, thus leaving a valid array offset at the top,
107 1.1 rumble * which reduces free space and potentially keeps a large directory
108 1.1 rumble * entry from being added. Since there's no technical reason why moving
109 1.1 rumble * them around would violate the format, I'm guessing that IRIX does
110 1.1 rumble * some sort of caching of index offsets within the array. A few quick
111 1.1 rumble * tests seems to indicate that coalescing can be slightly more
112 1.1 rumble * performant. One could also sort array offsets by de_namelen and
113 1.1 rumble * binary search on lookup, but I am not sure how much performance could
114 1.1 rumble * be gained since there are only 72 entries at maximum, far less on
115 1.1 rumble * average, and many unix files have similar length. Quick tests show
116 1.1 rumble * no appreciable difference when using binary search, as one would
117 1.1 rumble * suspect.
118 1.1 rumble */
119 1.1 rumble uint8_t db_space[EFS_DIRBLK_SPACE_SIZE];
120 1.2 msaitoh } __packed;
121 1.1 rumble
122 1.1 rumble /*
123 1.1 rumble * 'db_slots' (directory entry offset array size) can be no larger
124 1.1 rumble * than (EFS_DIRBLK_SPACE_SIZE / 9), as each efs_dirent struct is
125 1.1 rumble * minimally 6 bytes and requires one 1-byte offset entry.
126 1.1 rumble */
127 1.1 rumble #define EFS_DIRBLK_SLOTS_MAX (EFS_DIRBLK_SPACE_SIZE / 7)
128 1.1 rumble
129 1.1 rumble #define EFS_DIRBLK_SLOT_FREE (0) /* free, uncoalesced slots are zeroed */
130 1.1 rumble
131 1.1 rumble /*
132 1.1 rumble * Directory entry structure, which resides in efs_dirblk->space. Minimally
133 1.1 rumble * 6 bytes on-disk, maximally 260 bytes.
134 1.1 rumble *
135 1.1 rumble * The allocation within efs_dirblk->space must always be even, so the
136 1.1 rumble * structure is always padded by one byte if the efs_dirent struct is odd. This
137 1.1 rumble * occurs when de_namelen is even. The macros below handle this irregularity. It
138 1.1 rumble * should be noted that despite this, de_namelen will always reflect the true
139 1.1 rumble * length of de_name, which is NOT nul-terminated. Therefore without a priori
140 1.1 rumble * knowledge of this scheme, one cannot accurately calculate the efs_dirent size
141 1.1 rumble * based on the de_namelen field alone, rather EFS_DIRENT_SIZE() must be used.
142 1.1 rumble */
143 1.1 rumble struct efs_dirent {
144 1.1 rumble /* entry's inode number */
145 1.1 rumble union {
146 1.1 rumble uint32_t l;
147 1.1 rumble uint16_t s[2];
148 1.1 rumble } de_u;
149 1.1 rumble
150 1.1 rumble /*
151 1.1 rumble * de_name is of variable length (1 <= de_namelen <= 255). Note that
152 1.1 rumble * the string is NOT nul-terminated.
153 1.1 rumble */
154 1.1 rumble uint8_t de_namelen;
155 1.1 rumble char de_name[1]; /* variably sized */
156 1.2 msaitoh } __packed;
157 1.1 rumble
158 1.1 rumble #define de_inumber de_u.l
159 1.1 rumble
160 1.1 rumble #define EFS_DIRBLK_TO_DIRENT(_d, _o) (struct efs_dirent *)((char *)(_d) + _o)
161 1.1 rumble
162 1.1 rumble /*
163 1.1 rumble * Offsets are stored on-disk right shifted one to squeeze 512 even-byte
164 1.1 rumble * boundary offsets into a uint8_t. Before being compacted, the least
165 1.1 rumble * significant bits of an offset must, of course, be zero.
166 1.1 rumble */
167 1.1 rumble #define EFS_DIRENT_OFF_SHFT 1
168 1.1 rumble #define EFS_DIRENT_OFF_EXPND(_x) ((_x) << EFS_DIRENT_OFF_SHFT)
169 1.1 rumble #define EFS_DIRENT_OFF_COMPT(_x) ((_x) >> EFS_DIRENT_OFF_SHFT)
170 1.1 rumble #define EFS_DIRENT_OFF_VALID(_x) (((_x) & 0x1) == 0 && (_x) < \
171 1.1 rumble EFS_DIRBLK_SPACE_SIZE) /*if expanded*/
172 1.1 rumble
173 1.1 rumble #define EFS_DIRENT_NAMELEN_MAX 255
174 1.1 rumble
175 1.1 rumble #define EFS_DIRENT_SIZE_MIN (sizeof(struct efs_dirent))
176 1.1 rumble #define EFS_DIRENT_SIZE_MAX (EFS_DIRENT_SIZE_MIN+EFS_DIRENT_NAMELEN_MAX - 1)
177 1.1 rumble
178 1.1 rumble /*
179 1.1 rumble * Calculate the size of struct efs_dirent given the provided namelen. If our
180 1.1 rumble * namelen were even, then struct efs_dirent's size would be odd. In such a case
181 1.1 rumble * we must pad to ensure 16-bit alignment of the structure.
182 1.1 rumble */
183 1.1 rumble #define EFS_DIRENT_SIZE(_x) (EFS_DIRENT_SIZE_MIN + (_x) - ((_x) & 0x1))
184 1.1 rumble
185 1.1 rumble #endif /* !_FS_EFS_EFS_DIR_H_ */
186