MOS Source Code
Loading...
Searching...
No Matches
cpio.c
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
5#include "mos/mm/mm.h"
7#include "mos/mm/slab.h"
10
13#include <mos/filesystem/vfs.h>
16#include <mos/misc/setup.h>
17#include <mos/mos_global.h>
18#include <mos/syslog/printk.h>
19#include <mos_stdlib.h>
20#include <mos_string.h>
21
22#define CPIO_MODE_FILE_TYPE 0170000 // This masks the file type bits.
23#define CPIO_MODE_SOCKET 0140000 // File type value for sockets.
24#define CPIO_MODE_SYMLINK 0120000 // File type value for symbolic links. For symbolic links, the link body is stored as file data.
25#define CPIO_MODE_FILE 0100000 // File type value for regular files.
26#define CPIO_MODE_BLOCKDEV 0060000 // File type value for block special devices.
27#define CPIO_MODE_DIR 0040000 // File type value for directories.
28#define CPIO_MODE_CHARDEV 0020000 // File type value for character special devices.
29#define CPIO_MODE_FIFO 0010000 // File type value for named pipes or FIFOs.
30#define CPIO_MODE_SUID 0004000 // SUID bit.
31#define CPIO_MODE_SGID 0002000 // SGID bit.
32#define CPIO_MODE_STICKY 0001000 // Sticky bit.
33
34typedef struct
35{
36 char magic[6];
37 char ino[8];
38 char mode[8];
39 char uid[8];
40 char gid[8];
41 char nlink[8];
42 char mtime[8];
43
44 char filesize[8];
45 char devmajor[8];
46 char devminor[8];
47 char rdevmajor[8];
48 char rdevminor[8];
49
50 char namesize[8];
51 char check[8];
53
54MOS_STATIC_ASSERT(sizeof(cpio_newc_header_t) == 110, "cpio_newc_header has wrong size");
55
57
58typedef struct
59{
62 size_t name_offset, name_length;
66
72static const file_ops_t cpio_noop_file_ops = { 0 };
73
76
77static size_t initrd_read(void *buf, size_t size, size_t offset)
78{
80 mos_panic("initrd_read: out of bounds");
81 memcpy(buf, (void *) (pfn_va(platform_info->initrd_pfn) + offset), size);
82 return size;
83}
84
86{
88 switch (modebits & CPIO_MODE_FILE_TYPE)
89 {
90 case CPIO_MODE_FILE: type = FILE_TYPE_REGULAR; break;
91 case CPIO_MODE_DIR: type = FILE_TYPE_DIRECTORY; break;
92 case CPIO_MODE_SYMLINK: type = FILE_TYPE_SYMLINK; break;
93 case CPIO_MODE_CHARDEV: type = FILE_TYPE_CHAR_DEVICE; break;
95 case CPIO_MODE_FIFO: type = FILE_TYPE_NAMED_PIPE; break;
96 case CPIO_MODE_SOCKET: type = FILE_TYPE_SOCKET; break;
97 default: mos_warn("invalid cpio file mode"); break;
98 }
99
100 return type;
101}
102
103static bool cpio_read_metadata(const char *target, cpio_newc_header_t *header, size_t *header_offset, size_t *name_offset, size_t *name_length, size_t *data_offset,
104 size_t *data_length)
105{
106 if (unlikely(strcmp(target, "TRAILER!!!") == 0))
107 mos_panic("what the heck are you doing?");
108
109 size_t offset = 0;
110
111 while (true)
112 {
113 initrd_read(header, sizeof(cpio_newc_header_t), offset);
114
115 if (strncmp(header->magic, "07070", 5) != 0 || (header->magic[5] != '1' && header->magic[5] != '2'))
116 {
117 mos_warn("invalid cpio header magic, possibly corrupt archive");
118 return false;
119 }
120
121 offset += sizeof(cpio_newc_header_t);
122
123 const size_t filename_len = strntoll(header->namesize, NULL, 16, sizeof(header->namesize) / sizeof(char));
124
125 char filename[filename_len + 1]; // +1 for null terminator
126 initrd_read(filename, filename_len, offset);
127 filename[filename_len] = '\0';
128
129 bool found = strncmp(filename, target, filename_len) == 0;
130
131 if (found)
132 {
133 *header_offset = offset - sizeof(cpio_newc_header_t);
134 *name_offset = offset;
135 *name_length = filename_len;
136 }
137
138 if (unlikely(strcmp(filename, "TRAILER!!!") == 0))
139 return false;
140
141 offset += filename_len;
142 offset = ((offset + 3) & ~0x03); // align to 4 bytes
143
144 size_t data_len = strntoll(header->filesize, NULL, 16, sizeof(header->filesize) / sizeof(char));
145 if (found)
146 {
147 *data_offset = offset;
148 *data_length = data_len;
149 }
150
151 offset += data_len;
152 offset = ((offset + 3) & ~0x03); // align to 4 bytes (again)
153
154 if (found)
155 return true;
156 }
157
159}
160
162{
163 return container_of(inode, cpio_inode_t, inode);
164}
165
166// ============================================================================================================
167
168static cpio_inode_t *cpio_inode_trycreate(const char *path, superblock_t *sb)
169{
170 cpio_newc_header_t header = { 0 };
171 size_t header_offset = 0;
172 size_t name_offset = 0, name_length = 0;
173 size_t data_offset = 0, data_length = 0;
174 const bool found = cpio_read_metadata(path, &header, &header_offset, &name_offset, &name_length, &data_offset, &data_length);
175 if (!found)
176 return NULL;
177
178 cpio_inode_t *cpio_inode = kmalloc(cpio_inode_cache);
179 cpio_inode->header = header;
180 cpio_inode->header_offset = header_offset;
181 cpio_inode->name_offset = name_offset;
182 cpio_inode->name_length = name_length;
183 cpio_inode->data_offset = data_offset;
184
185 const u32 modebits = strntoll(cpio_inode->header.mode, NULL, 16, sizeof(cpio_inode->header.mode) / sizeof(char));
186 const u64 ino = strntoll(cpio_inode->header.ino, NULL, 16, sizeof(cpio_inode->header.ino) / sizeof(char));
187 const file_type_t file_type = cpio_modebits_to_filetype(modebits & CPIO_MODE_FILE_TYPE);
188
189 inode_t *const inode = &cpio_inode->inode;
190 inode_init(inode, sb, ino, file_type);
191
192 // 0000777 - The lower 9 bits specify read/write/execute permissions for world, group, and user following standard POSIX conventions.
193 inode->perm = modebits & PERM_MASK;
194 inode->size = data_length;
195 inode->uid = strntoll(cpio_inode->header.uid, NULL, 16, sizeof(cpio_inode->header.uid) / sizeof(char));
196 inode->gid = strntoll(cpio_inode->header.gid, NULL, 16, sizeof(cpio_inode->header.gid) / sizeof(char));
197 inode->sticky = modebits & CPIO_MODE_STICKY;
198 inode->suid = modebits & CPIO_MODE_SUID;
199 inode->sgid = modebits & CPIO_MODE_SGID;
200 inode->nlinks = strntoll(cpio_inode->header.nlink, NULL, 16, sizeof(cpio_inode->header.nlink) / sizeof(char));
203 inode->cache.ops = &cpio_icache_ops;
204
205 return cpio_inode;
206}
207
208// ============================================================================================================
209
210static dentry_t *cpio_mount(filesystem_t *fs, const char *dev_name, const char *mount_options)
211{
212 MOS_ASSERT(fs == &fs_cpiofs);
213
214 if (unlikely(mount_options) && strlen(mount_options) > 0)
215 mos_warn("cpio: mount options are not supported");
216
217 if (dev_name && strcmp(dev_name, "none") != 0)
218 pr_warn("cpio: mount: dev_name is not supported");
219
220 superblock_t *sb = kmalloc(superblock_cache);
221 sb->ops = &cpio_sb_ops;
222
224 if (!i)
225 {
226 kfree(sb);
227 return NULL; // not found
228 }
229
230 pr_dinfo2(cpio, "cpio header: %.6s", i->header.magic);
231 sb->fs = fs;
233 dentry_attach(sb->root, &i->inode);
234 sb->root->superblock = i->inode.superblock = sb;
235 return sb->root;
236}
237
238static bool cpio_i_lookup(inode_t *parent_dir, dentry_t *dentry)
239{
240 // keep prepending the path with the parent path, until we reach the root
241 char pathbuf[MOS_PATH_MAX_LENGTH] = { 0 };
242 dentry_path(dentry, parent_dir->superblock->root, pathbuf, sizeof(pathbuf));
243 const char *path = pathbuf + 1; // skip the first slash
244
245 cpio_inode_t *inode = cpio_inode_trycreate(path, parent_dir->superblock);
246 if (!inode)
247 return false; // not found
248
249 dentry_attach(dentry, &inode->inode);
250 return true;
251}
252
254{
255 dentry_t *d_parent = dentry_parent(dentry);
256 if (d_parent == NULL)
257 d_parent = root_dentry;
258
259 MOS_ASSERT(d_parent->inode != NULL);
260 MOS_ASSERT(dentry->inode);
261
262 add_record(state, dentry->inode->ino, ".", 1, FILE_TYPE_DIRECTORY);
263 add_record(state, d_parent->inode->ino, "..", 2, FILE_TYPE_DIRECTORY);
264
265 cpio_inode_t *inode = CPIO_INODE(dentry->inode);
266
267 char path_prefix[inode->name_length + 1]; // +1 for null terminator
268 initrd_read(path_prefix, inode->name_length, inode->name_offset);
269 path_prefix[inode->name_length] = '\0';
270 size_t prefix_len = strlen(path_prefix); // +1 for the slash
271
272 if (strcmp(path_prefix, ".") == 0)
273 path_prefix[0] = '\0', prefix_len = 0; // root directory
274
275 // find all children of this directory, that starts with 'path' and doesn't have any more slashes
276 cpio_newc_header_t header;
277 size_t offset = 0;
278
279 while (true)
280 {
281 initrd_read(&header, sizeof(cpio_newc_header_t), offset);
282 offset += sizeof(cpio_newc_header_t);
283
284 if (strncmp(header.magic, "07070", 5) != 0 || (header.magic[5] != '1' && header.magic[5] != '2'))
285 mos_panic("invalid cpio header magic, possibly corrupt archive");
286
287 const size_t filename_len = strntoll(header.namesize, NULL, 16, sizeof(header.namesize) / sizeof(char));
288
289 char filename[filename_len + 1]; // +1 for null terminator
290 initrd_read(filename, filename_len, offset);
291 filename[filename_len] = '\0';
292
293 const bool found = strncmp(path_prefix, filename, prefix_len) == 0 // make sure the path starts with the parent path
294 && (prefix_len == 0 || filename[prefix_len] == '/') // make sure it's a child, not a sibling (/path/to vs /path/toooo)
295 && strchr(filename + prefix_len + 1, '/') == NULL; // make sure it's only one level deep
296
297 const bool is_TRAILER = strcmp(filename, "TRAILER!!!") == 0;
298 const bool is_root_dot = strcmp(filename, ".") == 0;
299
300 if (found && !is_TRAILER && !is_root_dot)
301 {
302 pr_dinfo2(cpio, "prefix '%s' filename '%s'", path_prefix, filename);
303
304 const u32 modebits = strntoll(header.mode, NULL, 16, sizeof(header.mode) / sizeof(char));
306 const s64 ino = strntoll(header.ino, NULL, 16, sizeof(header.ino) / sizeof(char));
307
308 const char *name = filename + prefix_len + (prefix_len == 0 ? 0 : 1); // +1 for the slash if it's not the root
309 const size_t name_len = filename_len - prefix_len - (prefix_len == 0 ? 0 : 1); // -1 for the slash if it's not the root
310
311 add_record(state, ino, name, name_len, type);
312 }
313
314 if (unlikely(is_TRAILER))
315 break;
316
317 offset += filename_len;
318 offset = ALIGN_UP(offset, 4); // align to 4 bytes
319
320 const size_t data_len = strntoll(header.filesize, NULL, 16, sizeof(header.filesize) / sizeof(char));
321 offset += data_len;
322 offset = ALIGN_UP(offset, 4); // align to 4 bytes (again)
323 }
324}
325
326static size_t cpio_i_readlink(dentry_t *dentry, char *buffer, size_t buflen)
327{
328 cpio_inode_t *inode = CPIO_INODE(dentry->inode);
329 return initrd_read(buffer, MIN(buflen, inode->inode.size), inode->data_offset);
330}
331
332static bool cpio_sb_drop_inode(inode_t *inode)
333{
334 kfree(CPIO_INODE(inode));
335 return true;
336}
337
338static const superblock_ops_t cpio_sb_ops = {
339 .drop_inode = cpio_sb_drop_inode,
340};
341
342static const inode_ops_t cpio_dir_inode_ops = {
343 .lookup = cpio_i_lookup,
344 .iterate_dir = cpio_i_iterate_dir,
345};
346
347static const inode_ops_t cpio_file_inode_ops = {
348 .readlink = cpio_i_readlink,
349};
350
351static const file_ops_t cpio_file_ops = {
352 .read = vfs_generic_read,
353};
354
356{
357 inode_t *i = cache->owner;
358 cpio_inode_t *cpio_i = CPIO_INODE(i);
359
361 if (!page)
362 return NULL;
363 pmm_ref_one(page);
364
365 if ((size_t) pgoff * MOS_PAGE_SIZE >= i->size)
366 return page; // EOF, no need to read anything
367
368 const size_t bytes_to_read = MIN((size_t) MOS_PAGE_SIZE, i->size - pgoff * MOS_PAGE_SIZE);
369 const size_t read = initrd_read((char *) phyframe_va(page), bytes_to_read, cpio_i->data_offset + pgoff * MOS_PAGE_SIZE);
370 MOS_ASSERT(read == bytes_to_read);
371 return page;
372}
373
374static const inode_cache_ops_t cpio_icache_ops = {
375 .fill_cache = cpio_fill_cache,
376};
377
#define MOS_ASSERT(cond)
Definition assert.h:14
#define MOS_UNREACHABLE()
Definition assert.h:11
#define mos_warn(fmt,...)
Definition assert.h:23
#define MOS_PATH_MAX_LENGTH
Definition autoconf.h:27
#define MOS_PAGE_SIZE
Definition autoconf.h:6
#define CPIO_MODE_SOCKET
Definition cpio.c:23
#define CPIO_MODE_CHARDEV
Definition cpio.c:28
static phyframe_t * cpio_fill_cache(inode_cache_t *cache, off_t pgoff)
Definition cpio.c:355
static file_type_t cpio_modebits_to_filetype(u32 modebits)
Definition cpio.c:85
static cpio_inode_t * cpio_inode_trycreate(const char *path, superblock_t *sb)
Definition cpio.c:168
#define CPIO_MODE_FILE_TYPE
Definition cpio.c:22
static const inode_ops_t cpio_file_inode_ops
Definition cpio.c:68
static filesystem_t fs_cpiofs
Definition cpio.c:56
#define CPIO_MODE_FIFO
Definition cpio.c:29
#define CPIO_MODE_DIR
Definition cpio.c:27
static size_t cpio_i_readlink(dentry_t *dentry, char *buffer, size_t buflen)
Definition cpio.c:326
static const file_ops_t cpio_noop_file_ops
Definition cpio.c:72
#define CPIO_MODE_SYMLINK
Definition cpio.c:24
#define CPIO_MODE_SGID
Definition cpio.c:31
static bool cpio_sb_drop_inode(inode_t *inode)
Definition cpio.c:332
static void cpio_i_iterate_dir(dentry_t *dentry, vfs_listdir_state_t *state, dentry_iterator_op add_record)
Definition cpio.c:253
static const inode_ops_t cpio_dir_inode_ops
Definition cpio.c:67
should_inline cpio_inode_t * CPIO_INODE(inode_t *inode)
Definition cpio.c:161
static const superblock_ops_t cpio_sb_ops
Definition cpio.c:71
static bool cpio_read_metadata(const char *target, cpio_newc_header_t *header, size_t *header_offset, size_t *name_offset, size_t *name_length, size_t *data_offset, size_t *data_length)
Definition cpio.c:103
#define CPIO_MODE_STICKY
Definition cpio.c:32
static size_t initrd_read(void *buf, size_t size, size_t offset)
Definition cpio.c:77
#define CPIO_MODE_SUID
Definition cpio.c:30
static const inode_cache_ops_t cpio_icache_ops
Definition cpio.c:70
static const file_ops_t cpio_file_ops
Definition cpio.c:69
static bool cpio_i_lookup(inode_t *parent_dir, dentry_t *dentry)
Definition cpio.c:238
#define CPIO_MODE_BLOCKDEV
Definition cpio.c:26
static slab_t * cpio_inode_cache
Definition cpio.c:74
static dentry_t * cpio_mount(filesystem_t *fs, const char *dev_name, const char *mount_options)
Definition cpio.c:210
#define CPIO_MODE_FILE
Definition cpio.c:25
#define PERM_MASK
Definition fs_types.h:61
file_type_t
Definition fs_types.h:14
@ FILE_TYPE_UNKNOWN
Definition fs_types.h:22
@ FILE_TYPE_CHAR_DEVICE
Definition fs_types.h:18
@ FILE_TYPE_NAMED_PIPE
Definition fs_types.h:20
@ FILE_TYPE_REGULAR
Definition fs_types.h:15
@ FILE_TYPE_BLOCK_DEVICE
Definition fs_types.h:19
@ FILE_TYPE_SYMLINK
Definition fs_types.h:17
@ FILE_TYPE_DIRECTORY
Definition fs_types.h:16
@ FILE_TYPE_SOCKET
Definition fs_types.h:21
void dentry_attach(dentry_t *d, inode_t *inode)
Attach an inode to a dentry.
Definition dentry.c:305
ssize_t dentry_path(dentry_t *dentry, dentry_t *root, char *buf, size_t size)
Get the path of a dentry.
should_inline dentry_t * dentry_parent(const dentry_t *dentry)
Definition dentry.h:65
MOSAPI char * strchr(const char *s, int c)
Definition mos_string.c:236
MOSAPI s32 strncmp(const char *str1, const char *str2, size_t n)
Definition mos_string.c:32
MOSAPI s32 strcmp(const char *str1, const char *str2)
Definition mos_string.c:24
#define MIN(a, b)
Definition mos_stdlib.h:30
MOSAPI s64 strntoll(const char *str, char **endptr, int base, size_t n)
Definition mos_stdlib.c:73
__BEGIN_DECLS phyframe_t * mm_get_free_page(void)
Definition mm.c:46
#define phyframe_va(frame)
Definition mm.h:79
#define pfn_va(pfn)
Definition mm.h:76
#define pmm_ref_one(thing)
Definition pmm.h:146
dentry_t * root_dentry
Definition vfs.c:36
void inode_init(inode_t *inode, superblock_t *sb, u64 ino, file_type_t type)
Definition inode.c:49
#define should_inline
Definition mos_global.h:37
#define unlikely(x)
Definition mos_global.h:40
#define MOS_STATIC_ASSERT
Definition mos_global.h:14
#define ALIGN_UP(addr, size)
Definition mos_global.h:73
#define container_of(ptr, type, member)
Definition mos_global.h:50
#define mos_panic(fmt,...)
Definition panic.h:55
static void * memcpy(void *s1, const void *s2, size_t n)
Definition pb_syshdr.h:90
#define NULL
Definition pb_syshdr.h:46
static size_t strlen(const char *s)
Definition pb_syshdr.h:80
#define pr_warn(fmt,...)
Definition printk.h:38
#define pr_dinfo2(feat, fmt,...)
Definition printk.h:27
mos_platform_info_t *const platform_info
size_t size
Definition slab.c:30
const char * name
Definition slab.c:31
#define SLAB_AUTOINIT(name, var, type)
size_t name_length
Definition cpio.c:62
cpio_newc_header_t header
Definition cpio.c:64
size_t data_offset
Definition cpio.c:63
size_t name_offset
Definition cpio.c:62
inode_t inode
Definition cpio.c:60
size_t header_offset
Definition cpio.c:61
char nlink[8]
Definition cpio.c:41
char magic[6]
Definition cpio.c:36
char ino[8]
Definition cpio.c:37
char mode[8]
Definition cpio.c:38
char namesize[8]
Definition cpio.c:50
char filesize[8]
Definition cpio.c:44
char uid[8]
Definition cpio.c:39
char gid[8]
Definition cpio.c:40
superblock_t * superblock
Definition vfs_types.h:119
inode_t * inode
Definition vfs_types.h:117
inode_t * owner
Definition vfs_types.h:150
const inode_cache_ops_t * ops
Definition vfs_types.h:152
superblock_t * superblock
Definition vfs_types.h:171
uid_t uid
Definition vfs_types.h:161
file_perm_t perm
Definition vfs_types.h:159
const inode_ops_t * ops
Definition vfs_types.h:172
bool sgid
Definition vfs_types.h:165
size_t size
Definition vfs_types.h:160
const file_ops_t * file_ops
Definition vfs_types.h:173
bool suid
Definition vfs_types.h:164
inode_cache_t cache
Definition vfs_types.h:175
bool sticky
Definition vfs_types.h:163
gid_t gid
Definition vfs_types.h:162
u64 ino
Definition vfs_types.h:157
ssize_t nlinks
Definition vfs_types.h:166
Definition slab.h:45
dentry_t * root
Definition vfs_types.h:107
const superblock_ops_t * ops
Definition vfs_types.h:109
filesystem_t * fs
Definition vfs_types.h:108
static char buffer[2048]
Definition test_printf.c:7
unsigned int u32
Definition types.h:21
ssize_t off_t
Definition types.h:84
signed long long int s64
Definition types.h:17
unsigned long long u64
Definition types.h:23
slab_t * superblock_cache
Definition vfs.c:38
void dentry_iterator_op(vfs_listdir_state_t *state, u64 ino, const char *name, size_t name_len, file_type_t type)
Definition vfs_types.h:60
#define FILESYSTEM_DEFINE(var, fsname, mountfn, unmountfn)
Definition vfs_types.h:21
#define FILESYSTEM_AUTOREGISTER(fs)
Definition vfs_types.h:29
dentry_t * dentry_get_from_parent(superblock_t *sb, dentry_t *parent, const char *name)
Create a new dentry with the given name and parent.
Definition vfs_utils.c:40
ssize_t vfs_generic_read(const file_t *file, void *buf, size_t size, off_t offset)
Definition vfs_utils.c:95