1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 #define _GNU_SOURCE
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <config.h>
8 #if defined(HAVE_SYS_SYSMACROS_H)
9 #include <sys/sysmacros.h>
10 #endif
11 #include "erofs/print.h"
12 #include "erofs/inode.h"
13 #include "erofs/rebuild.h"
14 #include "erofs/dir.h"
15 #include "erofs/xattr.h"
16 #include "erofs/blobchunk.h"
17 #include "erofs/internal.h"
18 #include "liberofs_uuid.h"
19
20 #ifdef HAVE_LINUX_AUFS_TYPE_H
21 #include <linux/aufs_type.h>
22 #else
23 #define AUFS_WH_PFX ".wh."
24 #define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq"
25 #define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME
26 #endif
27
erofs_rebuild_mkdir(struct erofs_inode * dir,const char * s)28 static struct erofs_dentry *erofs_rebuild_mkdir(struct erofs_inode *dir,
29 const char *s)
30 {
31 struct erofs_inode *inode;
32 struct erofs_dentry *d;
33
34 inode = erofs_new_inode(dir->sbi);
35 if (IS_ERR(inode))
36 return ERR_CAST(inode);
37
38 if (asprintf(&inode->i_srcpath, "%s/%s",
39 dir->i_srcpath ? : "", s) < 0) {
40 erofs_iput(inode);
41 return ERR_PTR(-ENOMEM);
42 }
43 inode->i_mode = S_IFDIR | 0755;
44 inode->i_parent = dir;
45 inode->i_uid = getuid();
46 inode->i_gid = getgid();
47 inode->i_mtime = inode->sbi->build_time;
48 inode->i_mtime_nsec = inode->sbi->build_time_nsec;
49 inode->dev = dir->dev;
50 erofs_init_empty_dir(inode);
51
52 d = erofs_d_alloc(dir, s);
53 if (IS_ERR(d)) {
54 erofs_iput(inode);
55 } else {
56 d->type = EROFS_FT_DIR;
57 d->inode = inode;
58 }
59 return d;
60 }
61
erofs_rebuild_get_dentry(struct erofs_inode * pwd,char * path,bool aufs,bool * whout,bool * opq,bool to_head)62 struct erofs_dentry *erofs_rebuild_get_dentry(struct erofs_inode *pwd,
63 char *path, bool aufs, bool *whout, bool *opq, bool to_head)
64 {
65 struct erofs_dentry *d = NULL;
66 unsigned int len = strlen(path);
67 char *s = path;
68
69 *whout = false;
70 *opq = false;
71
72 while (s < path + len) {
73 char *slash = memchr(s, '/', path + len - s);
74
75 if (slash) {
76 if (s == slash) {
77 while (*++s == '/'); /* skip '//...' */
78 continue;
79 }
80 *slash = '\0';
81 }
82
83 if (!memcmp(s, ".", 2)) {
84 /* null */
85 } else if (!memcmp(s, "..", 3)) {
86 pwd = pwd->i_parent;
87 } else {
88 struct erofs_inode *inode = NULL;
89
90 if (aufs && !slash) {
91 if (!memcmp(s, AUFS_WH_DIROPQ, sizeof(AUFS_WH_DIROPQ))) {
92 *opq = true;
93 break;
94 }
95 if (!memcmp(s, AUFS_WH_PFX, sizeof(AUFS_WH_PFX) - 1)) {
96 s += sizeof(AUFS_WH_PFX) - 1;
97 *whout = true;
98 }
99 }
100
101 list_for_each_entry(d, &pwd->i_subdirs, d_child) {
102 if (!strcmp(d->name, s)) {
103 if (d->type != EROFS_FT_DIR && slash)
104 return ERR_PTR(-EIO);
105 inode = d->inode;
106 break;
107 }
108 }
109
110 if (inode) {
111 if (to_head) {
112 list_del(&d->d_child);
113 list_add(&d->d_child, &pwd->i_subdirs);
114 }
115 pwd = inode;
116 } else if (!slash) {
117 d = erofs_d_alloc(pwd, s);
118 if (IS_ERR(d))
119 return d;
120 d->type = EROFS_FT_UNKNOWN;
121 d->inode = pwd;
122 } else {
123 d = erofs_rebuild_mkdir(pwd, s);
124 if (IS_ERR(d))
125 return d;
126 pwd = d->inode;
127 }
128 }
129 if (slash) {
130 *slash = '/';
131 s = slash + 1;
132 } else {
133 break;
134 }
135 }
136 return d;
137 }
138
erofs_rebuild_write_blob_index(struct erofs_sb_info * dst_sb,struct erofs_inode * inode)139 static int erofs_rebuild_write_blob_index(struct erofs_sb_info *dst_sb,
140 struct erofs_inode *inode)
141 {
142 int ret;
143 unsigned int count, unit, chunkbits, i;
144 struct erofs_inode_chunk_index *idx;
145 erofs_off_t chunksize;
146 erofs_blk_t blkaddr;
147
148 /* TODO: fill data map in other layouts */
149 if (inode->datalayout == EROFS_INODE_CHUNK_BASED) {
150 chunkbits = inode->u.chunkbits;
151 if (chunkbits < dst_sb->blkszbits) {
152 erofs_err("%s: chunk size %u is smaller than the target block size %u",
153 inode->i_srcpath, 1U << chunkbits,
154 1U << dst_sb->blkszbits);
155 return -EINVAL;
156 }
157 } else if (inode->datalayout == EROFS_INODE_FLAT_PLAIN) {
158 chunkbits = ilog2(inode->i_size - 1) + 1;
159 if (chunkbits < dst_sb->blkszbits)
160 chunkbits = dst_sb->blkszbits;
161 if (chunkbits - dst_sb->blkszbits > EROFS_CHUNK_FORMAT_BLKBITS_MASK)
162 chunkbits = EROFS_CHUNK_FORMAT_BLKBITS_MASK + dst_sb->blkszbits;
163 } else {
164 erofs_err("%s: unsupported datalayout %d ", inode->i_srcpath,
165 inode->datalayout);
166 return -EOPNOTSUPP;
167 }
168
169 chunksize = 1ULL << chunkbits;
170 count = DIV_ROUND_UP(inode->i_size, chunksize);
171
172 unit = sizeof(struct erofs_inode_chunk_index);
173 inode->extent_isize = count * unit;
174 idx = malloc(max(sizeof(*idx), sizeof(void *)));
175 if (!idx)
176 return -ENOMEM;
177 inode->chunkindexes = idx;
178
179 for (i = 0; i < count; i++) {
180 struct erofs_blobchunk *chunk;
181 struct erofs_map_blocks map = {
182 .index = UINT_MAX,
183 };
184
185 map.m_la = i << chunkbits;
186 ret = erofs_map_blocks(inode, &map, 0);
187 if (ret)
188 goto err;
189
190 blkaddr = erofs_blknr(dst_sb, map.m_pa);
191 chunk = erofs_get_unhashed_chunk(inode->dev, blkaddr, 0);
192 if (IS_ERR(chunk)) {
193 ret = PTR_ERR(chunk);
194 goto err;
195 }
196 *(void **)idx++ = chunk;
197
198 }
199 inode->datalayout = EROFS_INODE_CHUNK_BASED;
200 inode->u.chunkformat = EROFS_CHUNK_FORMAT_INDEXES;
201 inode->u.chunkformat |= chunkbits - dst_sb->blkszbits;
202 return 0;
203 err:
204 free(inode->chunkindexes);
205 inode->chunkindexes = NULL;
206 return ret;
207 }
208
erofs_rebuild_update_inode(struct erofs_sb_info * dst_sb,struct erofs_inode * inode,enum erofs_rebuild_datamode datamode)209 static int erofs_rebuild_update_inode(struct erofs_sb_info *dst_sb,
210 struct erofs_inode *inode,
211 enum erofs_rebuild_datamode datamode)
212 {
213 int err = 0;
214
215 switch (inode->i_mode & S_IFMT) {
216 case S_IFCHR:
217 if (erofs_inode_is_whiteout(inode))
218 inode->i_parent->whiteouts = true;
219 /* fallthrough */
220 case S_IFBLK:
221 case S_IFIFO:
222 case S_IFSOCK:
223 inode->i_size = 0;
224 erofs_dbg("\tdev: %d %d", major(inode->u.i_rdev),
225 minor(inode->u.i_rdev));
226 inode->u.i_rdev = erofs_new_encode_dev(inode->u.i_rdev);
227 break;
228 case S_IFDIR:
229 err = erofs_init_empty_dir(inode);
230 break;
231 case S_IFLNK:
232 inode->i_link = malloc(inode->i_size + 1);
233 if (!inode->i_link)
234 return -ENOMEM;
235 err = erofs_pread(inode, inode->i_link, inode->i_size, 0);
236 erofs_dbg("\tsymlink: %s -> %s", inode->i_srcpath, inode->i_link);
237 break;
238 case S_IFREG:
239 if (!inode->i_size) {
240 inode->u.i_blkaddr = NULL_ADDR;
241 break;
242 }
243 if (datamode == EROFS_REBUILD_DATA_BLOB_INDEX)
244 err = erofs_rebuild_write_blob_index(dst_sb, inode);
245 else if (datamode == EROFS_REBUILD_DATA_RESVSP)
246 inode->datasource = EROFS_INODE_DATA_SOURCE_RESVSP;
247 else
248 err = -EOPNOTSUPP;
249 break;
250 default:
251 return -EINVAL;
252 }
253 return err;
254 }
255
256 /*
257 * @mergedir: parent directory in the merged tree
258 * @ctx.dir: parent directory when itering erofs_iterate_dir()
259 * @datamode: indicate how to import inode data
260 */
261 struct erofs_rebuild_dir_context {
262 struct erofs_dir_context ctx;
263 struct erofs_inode *mergedir;
264 enum erofs_rebuild_datamode datamode;
265 };
266
erofs_rebuild_dirent_iter(struct erofs_dir_context * ctx)267 static int erofs_rebuild_dirent_iter(struct erofs_dir_context *ctx)
268 {
269 struct erofs_rebuild_dir_context *rctx = (void *)ctx;
270 struct erofs_inode *mergedir = rctx->mergedir;
271 struct erofs_inode *dir = ctx->dir;
272 struct erofs_inode *inode, *candidate;
273 struct erofs_inode src;
274 struct erofs_dentry *d;
275 char *path, *dname;
276 bool dumb;
277 int ret;
278
279 if (ctx->dot_dotdot)
280 return 0;
281
282 ret = asprintf(&path, "%s/%.*s", rctx->mergedir->i_srcpath,
283 ctx->de_namelen, ctx->dname);
284 if (ret < 0)
285 return ret;
286
287 erofs_dbg("parsing %s", path);
288 dname = path + strlen(mergedir->i_srcpath) + 1;
289
290 d = erofs_rebuild_get_dentry(mergedir, dname, false,
291 &dumb, &dumb, false);
292 if (IS_ERR(d)) {
293 ret = PTR_ERR(d);
294 goto out;
295 }
296
297 ret = 0;
298 if (d->type != EROFS_FT_UNKNOWN) {
299 /*
300 * bail out if the file exists in the upper layers. (Note that
301 * extended attributes won't be merged too even for dirs.)
302 */
303 if (!S_ISDIR(d->inode->i_mode) || d->inode->opaque)
304 goto out;
305
306 /* merge directory entries */
307 src = (struct erofs_inode) {
308 .sbi = dir->sbi,
309 .nid = ctx->de_nid
310 };
311 ret = erofs_read_inode_from_disk(&src);
312 if (ret || !S_ISDIR(src.i_mode))
313 goto out;
314 mergedir = d->inode;
315 inode = dir = &src;
316 } else {
317 u64 nid;
318
319 DBG_BUGON(mergedir != d->inode);
320 inode = erofs_new_inode(dir->sbi);
321 if (IS_ERR(inode)) {
322 ret = PTR_ERR(inode);
323 goto out;
324 }
325
326 /* reuse i_ino[0] to read nid in source fs */
327 nid = inode->i_ino[0];
328 inode->sbi = dir->sbi;
329 inode->nid = ctx->de_nid;
330 ret = erofs_read_inode_from_disk(inode);
331 if (ret)
332 goto out;
333
334 /* restore nid in new generated fs */
335 inode->i_ino[1] = inode->i_ino[0];
336 inode->i_ino[0] = nid;
337 inode->dev = inode->sbi->dev;
338
339 if (S_ISREG(inode->i_mode) && inode->i_nlink > 1 &&
340 (candidate = erofs_iget(inode->dev, ctx->de_nid))) {
341 /* hardlink file */
342 erofs_iput(inode);
343 inode = candidate;
344 if (S_ISDIR(inode->i_mode)) {
345 erofs_err("hardlink directory not supported");
346 ret = -EISDIR;
347 goto out;
348 }
349 inode->i_nlink++;
350 erofs_dbg("\thardlink: %s -> %s", path, inode->i_srcpath);
351 } else {
352 ret = erofs_read_xattrs_from_disk(inode);
353 if (ret) {
354 erofs_iput(inode);
355 goto out;
356 }
357
358 inode->i_parent = d->inode;
359 inode->i_srcpath = path;
360 path = NULL;
361 inode->i_ino[1] = inode->nid;
362 inode->i_nlink = 1;
363
364 ret = erofs_rebuild_update_inode(&g_sbi, inode,
365 rctx->datamode);
366 if (ret) {
367 erofs_iput(inode);
368 goto out;
369 }
370
371 erofs_insert_ihash(inode);
372 mergedir = dir = inode;
373 }
374
375 d->inode = inode;
376 d->type = erofs_mode_to_ftype(inode->i_mode);
377 }
378
379 if (S_ISDIR(inode->i_mode)) {
380 struct erofs_rebuild_dir_context nctx = *rctx;
381
382 nctx.mergedir = mergedir;
383 nctx.ctx.dir = dir;
384 ret = erofs_iterate_dir(&nctx.ctx, false);
385 if (ret)
386 goto out;
387 }
388
389 /* reset sbi, nid after subdirs are all loaded for the final dump */
390 inode->sbi = &g_sbi;
391 inode->nid = 0;
392 out:
393 free(path);
394 return ret;
395 }
396
erofs_rebuild_load_tree(struct erofs_inode * root,struct erofs_sb_info * sbi,enum erofs_rebuild_datamode mode)397 int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi,
398 enum erofs_rebuild_datamode mode)
399 {
400 struct erofs_inode inode = {};
401 struct erofs_rebuild_dir_context ctx;
402 char uuid_str[37];
403 char *fsid = sbi->devname;
404 int ret;
405
406 if (!fsid) {
407 erofs_uuid_unparse_lower(sbi->uuid, uuid_str);
408 fsid = uuid_str;
409 }
410 ret = erofs_read_superblock(sbi);
411 if (ret) {
412 erofs_err("failed to read superblock of %s", fsid);
413 return ret;
414 }
415
416 inode.nid = sbi->root_nid;
417 inode.sbi = sbi;
418 ret = erofs_read_inode_from_disk(&inode);
419 if (ret) {
420 erofs_err("failed to read root inode of %s", fsid);
421 return ret;
422 }
423 inode.i_srcpath = strdup("/");
424
425 ctx = (struct erofs_rebuild_dir_context) {
426 .ctx.dir = &inode,
427 .ctx.cb = erofs_rebuild_dirent_iter,
428 .mergedir = root,
429 .datamode = mode,
430 };
431 ret = erofs_iterate_dir(&ctx.ctx, false);
432 free(inode.i_srcpath);
433 return ret;
434 }
435
erofs_rebuild_basedir_dirent_iter(struct erofs_dir_context * ctx)436 static int erofs_rebuild_basedir_dirent_iter(struct erofs_dir_context *ctx)
437 {
438 struct erofs_rebuild_dir_context *rctx = (void *)ctx;
439 struct erofs_inode *dir = ctx->dir;
440 struct erofs_inode *mergedir = rctx->mergedir;
441 struct erofs_dentry *d;
442 char *dname;
443 bool dumb;
444 int ret;
445
446 if (ctx->dot_dotdot)
447 return 0;
448
449 dname = strndup(ctx->dname, ctx->de_namelen);
450 if (!dname)
451 return -ENOMEM;
452 d = erofs_rebuild_get_dentry(mergedir, dname, false,
453 &dumb, &dumb, false);
454 if (IS_ERR(d)) {
455 ret = PTR_ERR(d);
456 goto out;
457 }
458
459 if (d->type == EROFS_FT_UNKNOWN) {
460 d->nid = ctx->de_nid;
461 d->type = ctx->de_ftype;
462 d->validnid = true;
463 if (!mergedir->whiteouts && erofs_dentry_is_wht(dir->sbi, d))
464 mergedir->whiteouts = true;
465 } else {
466 struct erofs_inode *inode = d->inode;
467
468 /* update sub-directories only for recursively loading */
469 if (S_ISDIR(inode->i_mode) &&
470 (ctx->de_ftype == EROFS_FT_DIR ||
471 ctx->de_ftype == EROFS_FT_UNKNOWN)) {
472 list_del(&inode->i_hash);
473 inode->dev = dir->sbi->dev;
474 inode->i_ino[1] = ctx->de_nid;
475 erofs_insert_ihash(inode);
476 }
477 }
478 ret = 0;
479 out:
480 free(dname);
481 return ret;
482 }
483
erofs_rebuild_load_basedir(struct erofs_inode * dir)484 int erofs_rebuild_load_basedir(struct erofs_inode *dir)
485 {
486 struct erofs_inode fakeinode = {
487 .sbi = dir->sbi,
488 .nid = dir->i_ino[1],
489 };
490 struct erofs_rebuild_dir_context ctx;
491 int ret;
492
493 ret = erofs_read_inode_from_disk(&fakeinode);
494 if (ret) {
495 erofs_err("failed to read inode @ %llu", fakeinode.nid);
496 return ret;
497 }
498
499 /* Inherit the maximum xattr size for the root directory */
500 if (__erofs_unlikely(IS_ROOT(dir)))
501 dir->xattr_isize = fakeinode.xattr_isize;
502
503 /*
504 * May be triggered if ftype == EROFS_FT_UNKNOWN, which is impossible
505 * with the current mkfs.
506 */
507 if (__erofs_unlikely(!S_ISDIR(fakeinode.i_mode))) {
508 DBG_BUGON(1);
509 return 0;
510 }
511
512 ctx = (struct erofs_rebuild_dir_context) {
513 .ctx.dir = &fakeinode,
514 .ctx.cb = erofs_rebuild_basedir_dirent_iter,
515 .mergedir = dir,
516 };
517 return erofs_iterate_dir(&ctx.ctx, false);
518 }
519