2 * Read a squashfs filesystem. This is a highly compressed read only
5 * Copyright (c) 2010, 2012, 2013, 2019
6 * Phillip Lougher <phillip@squashfs.org.uk>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2,
11 * or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 * Common xattr read code shared between mksquashfs and unsquashfs
35 #define __BYTE_ORDER BYTE_ORDER
36 #define __BIG_ENDIAN BIG_ENDIAN
37 #define __LITTLE_ENDIAN LITTLE_ENDIAN
42 #include "squashfs_fs.h"
43 #include "squashfs_swap.h"
49 extern int read_fs_bytes(int, long long, int, void *);
50 extern int read_block(int, long long, long long *, int, void *);
52 static struct hash_entry
{
55 struct hash_entry
*next
;
58 static struct squashfs_xattr_id
*xattr_ids
;
59 static void *xattrs
= NULL
;
60 static long long xattr_table_start
;
63 * Prefix lookup table, storing mapping to/from prefix string and prefix id
65 struct prefix prefix_table
[] = {
66 { "user.", SQUASHFS_XATTR_USER
},
67 { "trusted.", SQUASHFS_XATTR_TRUSTED
},
68 { "security.", SQUASHFS_XATTR_SECURITY
},
73 * store mapping from location of compressed block in fs ->
74 * location of uncompressed block in memory
76 static void save_xattr_block(long long start
, int offset
)
78 struct hash_entry
*hash_entry
= malloc(sizeof(*hash_entry
));
79 int hash
= start
& 0xffff;
81 TRACE("save_xattr_block: start %lld, offset %d\n", start
, offset
);
83 if(hash_entry
== NULL
)
86 hash_entry
->start
= start
;
87 hash_entry
->offset
= offset
;
88 hash_entry
->next
= hash_table
[hash
];
89 hash_table
[hash
] = hash_entry
;
94 * map from location of compressed block in fs ->
95 * location of uncompressed block in memory
97 static int get_xattr_block(long long start
)
99 int hash
= start
& 0xffff;
100 struct hash_entry
*hash_entry
= hash_table
[hash
];
102 for(; hash_entry
; hash_entry
= hash_entry
->next
)
103 if(hash_entry
->start
== start
)
106 TRACE("get_xattr_block: start %lld, offset %d\n", start
,
107 hash_entry
? hash_entry
->offset
: -1);
109 return hash_entry
? hash_entry
->offset
: -1;
114 * construct the xattr_list entry from the fs xattr, including
115 * mapping name and prefix into a full name
117 static int read_xattr_entry(struct xattr_list
*xattr
,
118 struct squashfs_xattr_entry
*entry
, void *name
)
120 int i
, len
, type
= entry
->type
& XATTR_PREFIX_MASK
;
122 for(i
= 0; prefix_table
[i
].type
!= -1; i
++)
123 if(prefix_table
[i
].type
== type
)
126 if(prefix_table
[i
].type
== -1) {
127 ERROR("read_xattr_entry: Unrecognised xattr type %d\n", type
);
131 len
= strlen(prefix_table
[i
].prefix
);
132 xattr
->full_name
= malloc(len
+ entry
->size
+ 1);
133 if(xattr
->full_name
== NULL
)
136 memcpy(xattr
->full_name
, prefix_table
[i
].prefix
, len
);
137 memcpy(xattr
->full_name
+ len
, name
, entry
->size
);
138 xattr
->full_name
[len
+ entry
->size
] = '\0';
139 xattr
->name
= xattr
->full_name
+ len
;
140 xattr
->size
= entry
->size
;
148 * Read and decompress the xattr id table and the xattr metadata.
149 * This is cached in memory for later use by get_xattr()
151 int read_xattrs_from_disk(int fd
, struct squashfs_super_block
*sBlk
, int flag
, long long *table_start
)
154 * Note on overflow limits:
155 * Size of ids (id_table.xattr_ids) is 2^32 (unsigned int)
156 * Max size of bytes is 2^32*16 or 2^36
157 * Max indexes is (2^32*16)/8K or 2^23
158 * Max index_bytes is ((2^32*16)/8K)*8 or 2^26 or 64M
160 int res
, i
, indexes
, index_bytes
;
163 long long *index
, start
, end
;
164 struct squashfs_xattr_table id_table
;
166 TRACE("read_xattrs_from_disk\n");
168 if(sBlk
->xattr_id_table_start
== SQUASHFS_INVALID_BLK
)
169 return SQUASHFS_INVALID_BLK
;
172 * Read xattr id table, containing start of xattr metadata and the
173 * number of xattrs in the file system
175 res
= read_fs_bytes(fd
, sBlk
->xattr_id_table_start
, sizeof(id_table
),
180 SQUASHFS_INSWAP_XATTR_TABLE(&id_table
);
183 * Compute index table values
185 ids
= id_table
.xattr_ids
;
186 xattr_table_start
= id_table
.xattr_table_start
;
187 index_bytes
= SQUASHFS_XATTR_BLOCK_BYTES((long long) ids
);
188 indexes
= SQUASHFS_XATTR_BLOCKS((long long) ids
);
191 * The size of the index table (index_bytes) should match the
192 * table start and end points
194 if(index_bytes
!= (sBlk
->bytes_used
- (sBlk
->xattr_id_table_start
+ sizeof(id_table
)))) {
195 ERROR("read_xattrs_from_disk: Bad xattr_ids count in super block\n");
200 * id_table.xattr_table_start stores the start of the compressed xattr
201 * metadata blocks. This by definition is also the end of the previous
202 * filesystem table - the id lookup table.
204 if(table_start
!= NULL
)
205 *table_start
= id_table
.xattr_table_start
;
208 * If flag is set then return once we've read the above
209 * table_start. That value is necessary for sanity checking,
210 * but we don't actually want to extract the xattrs, and so
214 return id_table
.xattr_ids
;
217 * Allocate and read the index to the xattr id table metadata
220 index
= malloc(index_bytes
);
224 res
= read_fs_bytes(fd
, sBlk
->xattr_id_table_start
+ sizeof(id_table
),
229 SQUASHFS_INSWAP_LONG_LONGS(index
, indexes
);
232 * Allocate enough space for the uncompressed xattr id table, and
233 * read and decompress it
235 bytes
= SQUASHFS_XATTR_BYTES((long long) ids
);
236 xattr_ids
= malloc(bytes
);
237 if(xattr_ids
== NULL
)
240 for(i
= 0; i
< indexes
; i
++) {
241 int expected
= (i
+ 1) != indexes
? SQUASHFS_METADATA_SIZE
:
242 bytes
& (SQUASHFS_METADATA_SIZE
- 1);
243 int length
= read_block(fd
, index
[i
], NULL
, expected
,
244 ((unsigned char *) xattr_ids
) +
245 ((long long) i
* SQUASHFS_METADATA_SIZE
));
246 TRACE("Read xattr id table block %d, from 0x%llx, length "
247 "%d\n", i
, index
[i
], length
);
249 ERROR("Failed to read xattr id table block %d, "
250 "from 0x%llx, length %d\n", i
, index
[i
],
257 * Read and decompress the xattr metadata
259 * Note the first xattr id table metadata block is immediately after
260 * the last xattr metadata block, so we can use index[0] to work out
261 * the end of the xattr metadata
263 start
= xattr_table_start
;
265 for(i
= 0; start
< end
; i
++) {
267 xattrs
= realloc(xattrs
, (i
+ 1) * SQUASHFS_METADATA_SIZE
);
271 /* store mapping from location of compressed block in fs ->
272 * location of uncompressed block in memory */
273 save_xattr_block(start
, i
* SQUASHFS_METADATA_SIZE
);
275 length
= read_block(fd
, start
, &start
, 0,
276 ((unsigned char *) xattrs
) +
277 (i
* SQUASHFS_METADATA_SIZE
));
278 TRACE("Read xattr block %d, length %d\n", i
, length
);
280 ERROR("Failed to read xattr block %d\n", i
);
285 * If this is not the last metadata block in the xattr metadata
286 * then it should be SQUASHFS_METADATA_SIZE in size.
287 * Note, we can't use expected in read_block() above for this
288 * because we don't know if this is the last block until
291 if(start
!= end
&& length
!= SQUASHFS_METADATA_SIZE
) {
292 ERROR("Xattr block %d should be %d bytes in length, "
293 "it is %d bytes\n", i
, SQUASHFS_METADATA_SIZE
,
299 /* swap if necessary the xattr id entries */
300 for(i
= 0; i
< ids
; i
++)
301 SQUASHFS_INSWAP_XATTR_ID(&xattr_ids
[i
]);
318 void free_xattr(struct xattr_list
*xattr_list
, int count
)
322 for(i
= 0; i
< count
; i
++)
323 free(xattr_list
[i
].full_name
);
330 * Construct and return the list of xattr name:value pairs for the passed xattr
333 * There are two users for get_xattr(), Mksquashfs uses it to read the
334 * xattrs from the filesystem on appending, and Unsquashfs uses it
335 * to retrieve the xattrs for writing to disk.
337 * Unfortunately, the two users disagree on what to do with unknown
338 * xattr prefixes, Mksquashfs wants to treat this as fatal otherwise
339 * this will cause xattrs to be be lost on appending. Unsquashfs
340 * on the otherhand wants to retrieve the xattrs which are known and
341 * to ignore the rest, this allows Unsquashfs to cope more gracefully
342 * with future versions which may have unknown xattrs, as long as the
343 * general xattr structure is adhered to, Unsquashfs should be able
344 * to safely ignore unknown xattrs, and to write the ones it knows about,
345 * this is better than completely refusing to retrieve all the xattrs.
347 * So return an error flag if any unrecognised types were found.
349 struct xattr_list
*get_xattr(int i
, unsigned int *count
, int *failed
)
352 struct xattr_list
*xattr_list
= NULL
;
357 TRACE("get_xattr\n");
359 if(xattr_ids
[i
].count
== 0) {
360 ERROR("get_xattr: xattr count unexpectedly 0 - corrupt fs?\n");
367 start
= SQUASHFS_XATTR_BLK(xattr_ids
[i
].xattr
) + xattr_table_start
;
368 offset
= SQUASHFS_XATTR_OFFSET(xattr_ids
[i
].xattr
);
369 xptr
= xattrs
+ get_xattr_block(start
) + offset
;
371 TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i
,
372 xattr_ids
[i
].count
, start
, offset
);
374 for(j
= 0, n
= 0; n
< xattr_ids
[i
].count
; n
++) {
375 struct squashfs_xattr_entry entry
;
376 struct squashfs_xattr_val val
;
379 xattr_list
= realloc(xattr_list
, (j
+ 1) *
380 sizeof(struct xattr_list
));
381 if(xattr_list
== NULL
)
385 SQUASHFS_SWAP_XATTR_ENTRY(xptr
, &entry
);
386 xptr
+= sizeof(entry
);
388 res
= read_xattr_entry(&xattr_list
[j
], &entry
, xptr
);
390 /* unknown type, skip, and set error flag */
392 SQUASHFS_SWAP_XATTR_VAL(xptr
, &val
);
393 xptr
+= sizeof(val
) + val
.vsize
;
400 TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j
,
401 entry
.type
, entry
.size
, xattr_list
[j
].full_name
);
403 if(entry
.type
& SQUASHFS_XATTR_VALUE_OOL
) {
408 SQUASHFS_SWAP_LONG_LONGS(xptr
, &xattr
, 1);
409 xptr
+= sizeof(xattr
);
410 start
= SQUASHFS_XATTR_BLK(xattr
) + xattr_table_start
;
411 offset
= SQUASHFS_XATTR_OFFSET(xattr
);
412 ool_xptr
= xattrs
+ get_xattr_block(start
) + offset
;
413 SQUASHFS_SWAP_XATTR_VAL(ool_xptr
, &val
);
414 xattr_list
[j
].value
= ool_xptr
+ sizeof(val
);
416 SQUASHFS_SWAP_XATTR_VAL(xptr
, &val
);
417 xattr_list
[j
].value
= xptr
+ sizeof(val
);
418 xptr
+= sizeof(val
) + val
.vsize
;
421 TRACE("get_xattr: xattr %d, vsize %d\n", j
, val
.vsize
);
423 xattr_list
[j
++].vsize
= val
.vsize
;