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 "xattr.h"
26 
27 #include "fs.h"
28 #include "nonstd.h"
29 #include "swap.h"
30 
31 #include <string.h>
32 #include <stdlib.h>
33 
34 
35 #define SQFS_XATTR_PREFIX_MAX SQUASHFS_XATTR_SECURITY
36 
37 typedef struct {
38 	const char *pref;
39 	size_t len;
40 } sqfs_prefix;
41 
42 sqfs_prefix sqfs_xattr_prefixes[] = {
43 	{"user.", 5},
44 	{"security.", 9},
45 	{"trusted.", 8},
46 };
47 
48 
49 typedef enum {
50 	CURS_VSIZE = 1,
51 	CURS_VAL = 2,
52 	CURS_NEXT = 4
53 } sqfs_xattr_curs;
54 
sqfs_xattr_init(sqfs * fs)55 sqfs_err sqfs_xattr_init(sqfs *fs) {
56 	sqfs_off_t start = fs->sb.xattr_id_table_start;
57 	size_t bread;
58 	if (start == SQUASHFS_INVALID_BLK)
59 		return SQFS_OK;
60 
61 	bread = sqfs_pread(fs->fd, &fs->xattr_info, sizeof(fs->xattr_info),
62 		start + fs->offset);
63 	if (bread != sizeof(fs->xattr_info))
64 		return SQFS_ERR;
65 	sqfs_swapin_xattr_id_table(&fs->xattr_info);
66 
67 	return sqfs_table_init(&fs->xattr_table, fs->fd,
68 		start + sizeof(fs->xattr_info) + fs->offset, sizeof(struct squashfs_xattr_id),
69 		fs->xattr_info.xattr_ids);
70 }
71 
sqfs_xattr_open(sqfs * fs,sqfs_inode * inode,sqfs_xattr * x)72 sqfs_err sqfs_xattr_open(sqfs *fs, sqfs_inode *inode, sqfs_xattr *x) {
73 	sqfs_err err;
74 
75 	x->remain = 0; /* assume none exist */
76 	if (fs->xattr_info.xattr_ids == 0 || inode->xattr == SQUASHFS_INVALID_XATTR)
77 		return SQFS_OK;
78 
79 	err = sqfs_table_get(&fs->xattr_table, fs, inode->xattr,
80 		&x->info);
81 	if (err)
82 		return SQFS_ERR;
83 	sqfs_swapin_xattr_id(&x->info);
84 
85 	sqfs_md_cursor_inode(&x->c_next, x->info.xattr,
86 		fs->xattr_info.xattr_table_start);
87 
88 	x->fs = fs;
89 	x->remain = x->info.count;
90 	x->cursors = CURS_NEXT;
91 	return SQFS_OK;
92 }
93 
sqfs_xattr_read(sqfs_xattr * x)94 sqfs_err sqfs_xattr_read(sqfs_xattr *x) {
95 	sqfs_err err;
96 
97 	if (x->remain == 0)
98 		return SQFS_ERR;
99 
100 	if (!(x->cursors & CURS_NEXT)) {
101 		x->ool = false; /* force inline */
102 		if ((err = sqfs_xattr_value(x, NULL)))
103 			return err;
104 	}
105 
106 	x->c_name = x->c_next;
107 	if ((err = sqfs_md_read(x->fs, &x->c_name, &x->entry, sizeof(x->entry))))
108 		return err;
109 	sqfs_swapin_xattr_entry(&x->entry);
110 
111 	x->type = x->entry.type & SQUASHFS_XATTR_PREFIX_MASK;
112 	x->ool = x->entry.type & SQUASHFS_XATTR_VALUE_OOL;
113 	if (x->type > SQFS_XATTR_PREFIX_MAX)
114 		return SQFS_ERR;
115 
116 	--(x->remain);
117 	x->cursors = 0;
118 	return err;
119 }
120 
sqfs_xattr_name_size(sqfs_xattr * x)121 size_t sqfs_xattr_name_size(sqfs_xattr *x) {
122 	return x->entry.size + sqfs_xattr_prefixes[x->type].len;
123 }
124 
sqfs_xattr_name(sqfs_xattr * x,char * name,bool prefix)125 sqfs_err sqfs_xattr_name(sqfs_xattr *x, char *name, bool prefix) {
126 	sqfs_err err;
127 
128 	if (name && prefix) {
129 		sqfs_prefix *p = &sqfs_xattr_prefixes[x->type];
130 		memcpy(name, p->pref, p->len);
131 		name += p->len;
132 	}
133 
134 	x->c_vsize = x->c_name;
135 	err = sqfs_md_read(x->fs, &x->c_vsize, name, x->entry.size);
136 	if (err)
137 		return err;
138 
139 	x->cursors |= CURS_VSIZE;
140 	return err;
141 }
142 
sqfs_xattr_value_size(sqfs_xattr * x,size_t * size)143 sqfs_err sqfs_xattr_value_size(sqfs_xattr *x, size_t *size) {
144 	sqfs_err err;
145 	if (!(x->cursors & CURS_VSIZE))
146 		if ((err = sqfs_xattr_name(x, NULL, false)))
147 			return err;
148 
149 	x->c_val = x->c_vsize;
150 	if ((err = sqfs_md_read(x->fs, &x->c_val, &x->val, sizeof(x->val))))
151 		return err;
152 	sqfs_swapin_xattr_val(&x->val);
153 
154 	if (x->ool) {
155 		uint64_t pos;
156 		x->c_next = x->c_val;
157 		if ((err = sqfs_md_read(x->fs, &x->c_next, &pos, sizeof(pos))))
158 			return err;
159 		sqfs_swapin64(&pos);
160 		x->cursors |= CURS_NEXT;
161 
162 		sqfs_md_cursor_inode(&x->c_val, pos,
163 			x->fs->xattr_info.xattr_table_start);
164 		if ((err = sqfs_md_read(x->fs, &x->c_val, &x->val, sizeof(x->val))))
165 			return err;
166 		sqfs_swapin_xattr_val(&x->val);
167 	}
168 
169 	if (size)
170 		*size = x->val.vsize;
171 	x->cursors |= CURS_VAL;
172 	return err;
173 }
174 
sqfs_xattr_value(sqfs_xattr * x,void * buf)175 sqfs_err sqfs_xattr_value(sqfs_xattr *x, void *buf) {
176 	sqfs_err err;
177 	sqfs_md_cursor c;
178 
179 	if (!(x->cursors & CURS_VAL))
180 		if ((err = sqfs_xattr_value_size(x, NULL)))
181 			return err;
182 
183 	c = x->c_val;
184 	if ((err = sqfs_md_read(x->fs, &c, buf, x->val.vsize)))
185 		return err;
186 
187 	if (!x->ool) {
188 		x->c_next = c;
189 		x->cursors |= CURS_NEXT;
190 	}
191 	return err;
192 }
193 
sqfs_xattr_find_prefix(const char * name,uint16_t * type)194 static sqfs_err sqfs_xattr_find_prefix(const char *name, uint16_t *type) {
195 	int i;
196 	for (i = 0; i <= SQFS_XATTR_PREFIX_MAX; ++i) {
197 		sqfs_prefix *p = &sqfs_xattr_prefixes[i];
198 		if (strncmp(name, p->pref, p->len) == 0) {
199 			*type = i;
200 			return SQFS_OK;
201 		}
202 	}
203 	return SQFS_ERR;
204 }
205 
206 /* FIXME: Indicate EINVAL, ENOMEM? */
sqfs_xattr_find(sqfs_xattr * x,const char * name,bool * found)207 sqfs_err sqfs_xattr_find(sqfs_xattr *x, const char *name, bool *found) {
208 	sqfs_err err;
209 	char *cmp = NULL;
210 	uint16_t type;
211 	size_t len;
212 
213 	if ((err = sqfs_xattr_find_prefix(name, &type))) {
214 		/* Consider an invalid prefix to just be not found, or OS X
215 		 * Finder complains. */
216 		*found = false;
217 		return SQFS_OK;
218 	}
219 
220 	name += sqfs_xattr_prefixes[type].len;
221 	len = strlen(name);
222 	if (!(cmp = malloc(len)))
223 		return SQFS_ERR;
224 
225 	while (x->remain) {
226 		if ((err = sqfs_xattr_read(x)))
227 			goto done;
228 		if (x->type != type && x->entry.size != len)
229 			continue;
230 		if ((err = sqfs_xattr_name(x, cmp, false)))
231 			goto done;
232 		if (strncmp(name, cmp, len) == 0) {
233 			*found = true;
234 			goto done;
235 		}
236 	}
237 
238 	*found = false;
239 
240 done:
241 	free(cmp);
242 	return err;
243 }
244 
sqfs_xattr_lookup(sqfs * fs,sqfs_inode * inode,const char * name,void * buf,size_t * size)245 sqfs_err sqfs_xattr_lookup(sqfs *fs, sqfs_inode *inode, const char *name,
246 		void *buf, size_t *size) {
247 	sqfs_err err = SQFS_OK;
248 
249 	sqfs_xattr xattr;
250 	if ((err = sqfs_xattr_open(fs, inode, &xattr)))
251 		return err;
252 
253 	bool found = false;
254 	if ((err = sqfs_xattr_find(&xattr, name, &found)))
255 		return err;
256 	if (!found) {
257 		*size = 0;
258 		return err;
259 	}
260 
261 	size_t real;
262 	if ((err = sqfs_xattr_value_size(&xattr, &real)))
263 		return err;
264 
265 	if (buf && *size >= real) {
266 		if ((err = sqfs_xattr_value(&xattr, buf)))
267 			return err;
268 	}
269 
270 	*size = real;
271 	return err;
272 }
273 
274