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