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