1 /*
2 * Copyright (c) 2012 Dave Vasilevsky <dave@vasilevsky.ca>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25 #include "dir.h"
26
27 #include "fs.h"
28 #include "swap.h"
29
30 #include <string.h>
31 #include <sys/stat.h>
32
33 /* Read some directory metadata, updating the dir structure as necessary */
34 static sqfs_err sqfs_dir_md_read(sqfs *fs, sqfs_dir *dir, void *buf,
35 size_t size);
36
37 /* Fast forwards to a directory header. */
38 typedef sqfs_err sqfs_dir_header_f(sqfs *fs, sqfs_md_cursor *cur,
39 struct squashfs_dir_index *index, bool *stop, void *arg);
40 static sqfs_err sqfs_dir_ff_header(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir,
41 sqfs_dir_header_f func, void *arg);
42
43 /* Fast forward a directory to the given offset. Return error if it doesn't
44 exist. */
45 static sqfs_err sqfs_dir_ff_offset(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir,
46 sqfs_off_t offset);
47
48
sqfs_dir_md_read(sqfs * fs,sqfs_dir * dir,void * buf,size_t size)49 static sqfs_err sqfs_dir_md_read(sqfs *fs, sqfs_dir *dir, void *buf,
50 size_t size) {
51 dir->offset += size;
52 return sqfs_md_read(fs, &dir->cur, buf, size);
53 }
54
55
sqfs_dir_open(sqfs * fs,sqfs_inode * inode,sqfs_dir * dir,off_t offset)56 sqfs_err sqfs_dir_open(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir,
57 off_t offset) {
58 if (!S_ISDIR(inode->base.mode))
59 return SQFS_ERR;
60
61 memset(dir, 0, sizeof(*dir));
62 dir->cur.block = inode->xtra.dir.start_block +
63 fs->sb.directory_table_start;
64 dir->cur.offset = inode->xtra.dir.offset;
65 dir->offset = 0;
66 dir->total = inode->xtra.dir.dir_size - 3;
67
68 if (offset) {
69 /* Fast forward to the given offset */
70 sqfs_err err = sqfs_dir_ff_offset(fs, inode, dir, offset);
71 if (err)
72 return err;
73 }
74
75 return SQFS_OK;
76 }
77
78
sqfs_dentry_init(sqfs_dir_entry * entry,char * namebuf)79 void sqfs_dentry_init(sqfs_dir_entry *entry, char *namebuf) {
80 entry->name = namebuf;
81 }
82
sqfs_dentry_offset(sqfs_dir_entry * entry)83 sqfs_off_t sqfs_dentry_offset(sqfs_dir_entry *entry) {
84 return entry->offset;
85 }
86
sqfs_dentry_next_offset(sqfs_dir_entry * entry)87 sqfs_off_t sqfs_dentry_next_offset(sqfs_dir_entry *entry) {
88 return entry->next_offset;
89 }
90
sqfs_dentry_type(sqfs_dir_entry * entry)91 int sqfs_dentry_type(sqfs_dir_entry *entry) {
92 return entry->type;
93 }
sqfs_dentry_mode(sqfs_dir_entry * entry)94 sqfs_mode_t sqfs_dentry_mode(sqfs_dir_entry *entry) {
95 return sqfs_mode(sqfs_dentry_type(entry));
96 }
97
sqfs_dentry_inode(sqfs_dir_entry * entry)98 sqfs_inode_id sqfs_dentry_inode(sqfs_dir_entry *entry) {
99 return entry->inode;
100 }
101
sqfs_dentry_inode_num(sqfs_dir_entry * entry)102 sqfs_inode_num sqfs_dentry_inode_num(sqfs_dir_entry *entry) {
103 return entry->inode_number;
104 }
105
sqfs_dentry_name_size(sqfs_dir_entry * entry)106 size_t sqfs_dentry_name_size(sqfs_dir_entry *entry) {
107 return entry->name_size;
108 }
109
sqfs_dentry_name(sqfs_dir_entry * entry)110 const char *sqfs_dentry_name(sqfs_dir_entry *entry) {
111 if (!entry->name)
112 return NULL;
113
114 entry->name[sqfs_dentry_name_size(entry)] = '\0';
115 return entry->name;
116 }
117
sqfs_dentry_is_dir(sqfs_dir_entry * entry)118 bool sqfs_dentry_is_dir(sqfs_dir_entry *entry) {
119 return S_ISDIR(sqfs_dentry_mode(entry));
120 }
121
122
123
sqfs_dir_next(sqfs * fs,sqfs_dir * dir,sqfs_dir_entry * entry,sqfs_err * err)124 bool sqfs_dir_next(sqfs *fs, sqfs_dir *dir, sqfs_dir_entry *entry,
125 sqfs_err *err) {
126 struct squashfs_dir_entry e;
127
128 *err = SQFS_OK;
129 entry->offset = dir->offset;
130
131 while (dir->header.count == 0) {
132 if (dir->offset >= dir->total)
133 return false;
134
135 if ((*err = sqfs_dir_md_read(fs, dir, &dir->header, sizeof(dir->header))))
136 return false;
137 sqfs_swapin_dir_header(&dir->header);
138 ++(dir->header.count); /* biased by one */
139 }
140
141 if ((*err = sqfs_dir_md_read(fs, dir, &e, sizeof(e))))
142 return false;
143 sqfs_swapin_dir_entry(&e);
144 --(dir->header.count);
145
146 entry->type = e.type;
147 entry->name_size = e.size + 1;
148 entry->inode = ((uint64_t)dir->header.start_block << 16) + e.offset;
149 /* e.inode_number is signed */
150 entry->inode_number = dir->header.inode_number + (int16_t)e.inode_number;
151
152 *err = sqfs_dir_md_read(fs, dir, entry->name, sqfs_dentry_name_size(entry));
153 if (*err)
154 return false;
155
156 entry->next_offset = dir->offset;
157
158 return true;
159 }
160
161
sqfs_dir_ff_header(sqfs * fs,sqfs_inode * inode,sqfs_dir * dir,sqfs_dir_header_f func,void * arg)162 static sqfs_err sqfs_dir_ff_header(sqfs *fs, sqfs_inode *inode,
163 sqfs_dir *dir, sqfs_dir_header_f func, void *arg) {
164 struct squashfs_dir_index idx;
165 sqfs_md_cursor cur = inode->next;
166 size_t count = inode->xtra.dir.idx_count;
167
168 if (count == 0)
169 return SQFS_OK;
170
171 while (count--) {
172 sqfs_err err;
173 bool stop = 0;
174
175 if ((err = sqfs_md_read(fs, &cur, &idx, sizeof(idx))))
176 return err;
177 sqfs_swapin_dir_index(&idx);
178
179 if ((err = func(fs, &cur, &idx, &stop, arg)))
180 return err;
181 if (stop)
182 break;
183
184 dir->cur.block = idx.start_block + fs->sb.directory_table_start;
185 dir->offset = idx.index;
186 }
187
188 dir->cur.offset = (dir->cur.offset + dir->offset) % SQUASHFS_METADATA_SIZE;
189 return SQFS_OK;
190 }
191
192
193 /* Helper for sqfs_dir_ff_offset */
sqfs_dir_ff_offset_f(sqfs * fs,sqfs_md_cursor * cur,struct squashfs_dir_index * index,bool * stop,void * arg)194 static sqfs_err sqfs_dir_ff_offset_f(sqfs *fs, sqfs_md_cursor *cur,
195 struct squashfs_dir_index *index, bool *stop, void *arg) {
196 sqfs_off_t offset = *(sqfs_off_t*)arg;
197
198 if (index->index >= offset) {
199 *stop = true;
200 return SQFS_OK;
201 }
202
203 return sqfs_md_read(fs, cur, NULL, index->size + 1); /* skip name */
204 }
205
sqfs_dir_ff_offset(sqfs * fs,sqfs_inode * inode,sqfs_dir * dir,sqfs_off_t offset)206 static sqfs_err sqfs_dir_ff_offset(sqfs *fs, sqfs_inode *inode, sqfs_dir *dir,
207 sqfs_off_t offset) {
208 sqfs_err err;
209 sqfs_dir_entry entry;
210
211 err = sqfs_dir_ff_header(fs, inode, dir, sqfs_dir_ff_offset_f, &offset);
212 if (err)
213 return err;
214
215 sqfs_dentry_init(&entry, NULL);
216 while (dir->offset < offset && sqfs_dir_next(fs, dir, &entry, &err))
217 ; /* pass */
218
219 if (err)
220 return err;
221 return dir->offset == offset ? SQFS_OK : SQFS_ERR;
222 }
223
224
225 /* Helper for sqfs_dir_lookup */
226 typedef struct {
227 const char *cmp;
228 size_t cmplen;
229 char *name;
230 } sqfs_dir_ff_name_t;
231
sqfs_dir_ff_name_f(sqfs * fs,sqfs_md_cursor * cur,struct squashfs_dir_index * index,bool * stop,void * arg)232 static sqfs_err sqfs_dir_ff_name_f(sqfs *fs, sqfs_md_cursor *cur,
233 struct squashfs_dir_index *index, bool *stop, void *arg) {
234 sqfs_err err;
235 sqfs_dir_ff_name_t *args = (sqfs_dir_ff_name_t*)arg;
236 size_t name_size = index->size + 1;
237
238 if ((err = sqfs_md_read(fs, cur, args->name, name_size)))
239 return err;
240 args->name[name_size] = '\0';
241
242 int order = strncmp(args->name, args->cmp, args->cmplen);
243 if (order > 0 || (order == 0 && name_size > args->cmplen))
244 *stop = true;
245
246 return SQFS_OK;
247 }
248
sqfs_dir_lookup(sqfs * fs,sqfs_inode * inode,const char * name,size_t namelen,sqfs_dir_entry * entry,bool * found)249 sqfs_err sqfs_dir_lookup(sqfs *fs, sqfs_inode *inode,
250 const char *name, size_t namelen, sqfs_dir_entry *entry, bool *found) {
251 sqfs_err err;
252 sqfs_dir dir;
253 sqfs_dir_ff_name_t arg;
254
255 *found = false;
256
257 if ((err = sqfs_dir_open(fs, inode, &dir, 0)))
258 return err;
259
260 /* Fast forward to header */
261 arg.cmp = name;
262 arg.cmplen = namelen;
263 arg.name = entry->name;
264 if ((err = sqfs_dir_ff_header(fs, inode, &dir, sqfs_dir_ff_name_f, &arg)))
265 return err;
266
267 /* Iterate to find the right entry */
268 while (sqfs_dir_next(fs, &dir, entry, &err)) {
269 int order = strncmp(sqfs_dentry_name(entry), name, namelen);
270 if (order == 0 && sqfs_dentry_name_size(entry) == namelen)
271 *found = true;
272 if (order >= 0)
273 break;
274 }
275
276 return err;
277 }
278
279
sqfs_lookup_path(sqfs * fs,sqfs_inode * inode,const char * path,bool * found)280 sqfs_err sqfs_lookup_path(sqfs *fs, sqfs_inode *inode, const char *path,
281 bool *found) {
282 sqfs_err err;
283 sqfs_name buf;
284 sqfs_dir_entry entry;
285
286 *found = false;
287 sqfs_dentry_init(&entry, buf);
288
289 while (*path) {
290 const char *name;
291 size_t size;
292 bool dfound;
293
294 /* Find next path component */
295 while (*path == '/') /* skip leading slashes */
296 ++path;
297
298 name = path;
299 while (*path && *path != '/')
300 ++path;
301 size = path - name;
302 if (size == 0) /* we're done */
303 break;
304
305 if ((err = sqfs_dir_lookup(fs, inode, name, size, &entry, &dfound)))
306 return err;
307 if (!dfound)
308 return SQFS_OK; /* not found */
309
310 if ((err = sqfs_inode_get(fs, inode, sqfs_dentry_inode(&entry))))
311 return err;
312 }
313
314 *found = true;
315 return SQFS_OK;
316 }
317