]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - misc/create_inode.c
Fix typos in code comments and developer docs
[thirdparty/e2fsprogs.git] / misc / create_inode.c
1 /*
2 * create_inode.c --- create an inode
3 *
4 * Copyright (C) 2014 Robert Yang <liezhi.yang@windriver.com>
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU library
8 * General Public License, version 2.
9 * %End-Header%
10 */
11
12 #define _FILE_OFFSET_BITS 64
13 #define _LARGEFILE64_SOURCE 1
14 #define _GNU_SOURCE 1
15
16 #include "config.h"
17 #include <time.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <limits.h> /* for PATH_MAX */
21 #ifdef HAVE_ATTR_XATTR_H
22 #include <attr/xattr.h>
23 #endif
24 #include <sys/ioctl.h>
25 #ifdef HAVE_SYS_SYSMACROS_H
26 #include <sys/sysmacros.h>
27 #endif
28
29 #include <ext2fs/ext2fs.h>
30 #include <ext2fs/ext2_types.h>
31 #include <ext2fs/fiemap.h>
32
33 #include "create_inode.h"
34 #include "support/nls-enable.h"
35
36 /* 64KiB is the minimum blksize to best minimize system call overhead. */
37 #define COPY_FILE_BUFLEN 65536
38
39 static int ext2_file_type(unsigned int mode)
40 {
41 if (LINUX_S_ISREG(mode))
42 return EXT2_FT_REG_FILE;
43
44 if (LINUX_S_ISDIR(mode))
45 return EXT2_FT_DIR;
46
47 if (LINUX_S_ISCHR(mode))
48 return EXT2_FT_CHRDEV;
49
50 if (LINUX_S_ISBLK(mode))
51 return EXT2_FT_BLKDEV;
52
53 if (LINUX_S_ISLNK(mode))
54 return EXT2_FT_SYMLINK;
55
56 if (LINUX_S_ISFIFO(mode))
57 return EXT2_FT_FIFO;
58
59 if (LINUX_S_ISSOCK(mode))
60 return EXT2_FT_SOCK;
61
62 return 0;
63 }
64
65 /* Link an inode number to a directory */
66 static errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino,
67 ext2_ino_t ino, const char *name)
68 {
69 struct ext2_inode inode;
70 errcode_t retval;
71
72 retval = ext2fs_read_inode(fs, ino, &inode);
73 if (retval) {
74 com_err(__func__, retval, _("while reading inode %u"), ino);
75 return retval;
76 }
77
78 retval = ext2fs_link(fs, parent_ino, name, ino,
79 ext2_file_type(inode.i_mode));
80 if (retval == EXT2_ET_DIR_NO_SPACE) {
81 retval = ext2fs_expand_dir(fs, parent_ino);
82 if (retval) {
83 com_err(__func__, retval,
84 _("while expanding directory"));
85 return retval;
86 }
87 retval = ext2fs_link(fs, parent_ino, name, ino,
88 ext2_file_type(inode.i_mode));
89 }
90 if (retval) {
91 com_err(__func__, retval, _("while linking \"%s\""), name);
92 return retval;
93 }
94
95 inode.i_links_count++;
96
97 retval = ext2fs_write_inode(fs, ino, &inode);
98 if (retval)
99 com_err(__func__, retval, _("while writing inode %u"), ino);
100
101 return retval;
102 }
103
104 /* Set the uid, gid, mode and time for the inode */
105 static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t ino,
106 struct stat *st)
107 {
108 errcode_t retval;
109 struct ext2_inode inode;
110
111 retval = ext2fs_read_inode(fs, ino, &inode);
112 if (retval) {
113 com_err(__func__, retval, _("while reading inode %u"), ino);
114 return retval;
115 }
116
117 inode.i_uid = st->st_uid;
118 inode.i_gid = st->st_gid;
119 inode.i_mode |= st->st_mode;
120 inode.i_atime = st->st_atime;
121 inode.i_mtime = st->st_mtime;
122 inode.i_ctime = st->st_ctime;
123
124 retval = ext2fs_write_inode(fs, ino, &inode);
125 if (retval)
126 com_err(__func__, retval, _("while writing inode %u"), ino);
127 return retval;
128 }
129
130 #ifdef HAVE_LLISTXATTR
131 static errcode_t set_inode_xattr(ext2_filsys fs, ext2_ino_t ino,
132 const char *filename)
133 {
134 errcode_t retval, close_retval;
135 struct ext2_xattr_handle *handle;
136 ssize_t size, value_size;
137 char *list = NULL;
138 int i;
139
140 size = llistxattr(filename, NULL, 0);
141 if (size == -1) {
142 retval = errno;
143 com_err(__func__, retval, _("while listing attributes of \"%s\""),
144 filename);
145 return retval;
146 } else if (size == 0) {
147 return 0;
148 }
149
150 retval = ext2fs_xattrs_open(fs, ino, &handle);
151 if (retval) {
152 if (retval == EXT2_ET_MISSING_EA_FEATURE)
153 return 0;
154 com_err(__func__, retval, _("while opening inode %u"), ino);
155 return retval;
156 }
157
158 retval = ext2fs_get_mem(size, &list);
159 if (retval) {
160 com_err(__func__, retval, _("while allocating memory"));
161 goto out;
162 }
163
164 size = llistxattr(filename, list, size);
165 if (size == -1) {
166 retval = errno;
167 com_err(__func__, retval, _("while listing attributes of \"%s\""),
168 filename);
169 goto out;
170 }
171
172 for (i = 0; i < size; i += strlen(&list[i]) + 1) {
173 const char *name = &list[i];
174 char *value;
175
176 value_size = lgetxattr(filename, name, NULL, 0);
177 if (value_size == -1) {
178 retval = errno;
179 com_err(__func__, retval,
180 _("while reading attribute \"%s\" of \"%s\""),
181 name, filename);
182 break;
183 }
184
185 retval = ext2fs_get_mem(value_size, &value);
186 if (retval) {
187 com_err(__func__, retval, _("while allocating memory"));
188 break;
189 }
190
191 value_size = lgetxattr(filename, name, value, value_size);
192 if (value_size == -1) {
193 ext2fs_free_mem(&value);
194 retval = errno;
195 com_err(__func__, retval,
196 _("while reading attribute \"%s\" of \"%s\""),
197 name, filename);
198 break;
199 }
200
201 retval = ext2fs_xattr_set(handle, name, value, value_size);
202 ext2fs_free_mem(&value);
203 if (retval) {
204 com_err(__func__, retval,
205 _("while writing attribute \"%s\" to inode %u"),
206 name, ino);
207 break;
208 }
209
210 }
211 out:
212 ext2fs_free_mem(&list);
213 close_retval = ext2fs_xattrs_close(&handle);
214 if (close_retval) {
215 com_err(__func__, retval, _("while closing inode %u"), ino);
216 retval = retval ? retval : close_retval;
217 }
218 return retval;
219 return 0;
220 }
221 #else /* HAVE_LLISTXATTR */
222 static errcode_t set_inode_xattr(ext2_filsys fs EXT2FS_ATTR((unused)),
223 ext2_ino_t ino EXT2FS_ATTR((unused)),
224 const char *filename EXT2FS_ATTR((unused)))
225 {
226 return 0;
227 }
228 #endif /* HAVE_LLISTXATTR */
229
230 /* Make a special files (block and character devices), fifo's, and sockets */
231 errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
232 struct stat *st)
233 {
234 ext2_ino_t ino;
235 errcode_t retval;
236 struct ext2_inode inode;
237 unsigned long devmajor, devminor, mode;
238 int filetype;
239
240 switch(st->st_mode & S_IFMT) {
241 case S_IFCHR:
242 mode = LINUX_S_IFCHR;
243 filetype = EXT2_FT_CHRDEV;
244 break;
245 case S_IFBLK:
246 mode = LINUX_S_IFBLK;
247 filetype = EXT2_FT_BLKDEV;
248 break;
249 case S_IFIFO:
250 mode = LINUX_S_IFIFO;
251 filetype = EXT2_FT_FIFO;
252 break;
253 case S_IFSOCK:
254 mode = LINUX_S_IFSOCK;
255 filetype = EXT2_FT_SOCK;
256 break;
257 default:
258 return EXT2_ET_INVALID_ARGUMENT;
259 }
260
261 retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino);
262 if (retval) {
263 com_err(__func__, retval, _("while allocating inode \"%s\""),
264 name);
265 return retval;
266 }
267
268 #ifdef DEBUGFS
269 printf("Allocated inode: %u\n", ino);
270 #endif
271 retval = ext2fs_link(fs, cwd, name, ino, filetype);
272 if (retval == EXT2_ET_DIR_NO_SPACE) {
273 retval = ext2fs_expand_dir(fs, cwd);
274 if (retval) {
275 com_err(__func__, retval,
276 _("while expanding directory"));
277 return retval;
278 }
279 retval = ext2fs_link(fs, cwd, name, ino, filetype);
280 }
281 if (retval) {
282 com_err(name, retval, _("while creating inode \"%s\""), name);
283 return retval;
284 }
285 if (ext2fs_test_inode_bitmap2(fs->inode_map, ino))
286 com_err(__func__, 0, "Warning: inode already set");
287 ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
288 memset(&inode, 0, sizeof(inode));
289 inode.i_mode = mode;
290 inode.i_atime = inode.i_ctime = inode.i_mtime =
291 fs->now ? fs->now : time(0);
292
293 if (filetype != S_IFIFO) {
294 devmajor = major(st->st_rdev);
295 devminor = minor(st->st_rdev);
296
297 if ((devmajor < 256) && (devminor < 256)) {
298 inode.i_block[0] = devmajor * 256 + devminor;
299 inode.i_block[1] = 0;
300 } else {
301 inode.i_block[0] = 0;
302 inode.i_block[1] = (devminor & 0xff) | (devmajor << 8) |
303 ((devminor & ~0xff) << 12);
304 }
305 }
306 inode.i_links_count = 1;
307
308 retval = ext2fs_write_new_inode(fs, ino, &inode);
309 if (retval)
310 com_err(__func__, retval, _("while writing inode %u"), ino);
311
312 return retval;
313 }
314
315 /* Make a symlink name -> target */
316 errcode_t do_symlink_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
317 char *target, ext2_ino_t root)
318 {
319 char *cp;
320 ext2_ino_t parent_ino;
321 errcode_t retval;
322
323 cp = strrchr(name, '/');
324 if (cp) {
325 *cp = 0;
326 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
327 if (retval) {
328 com_err(name, retval, 0);
329 return retval;
330 }
331 name = cp+1;
332 } else
333 parent_ino = cwd;
334
335 retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
336 if (retval == EXT2_ET_DIR_NO_SPACE) {
337 retval = ext2fs_expand_dir(fs, parent_ino);
338 if (retval) {
339 com_err("do_symlink_internal", retval,
340 _("while expanding directory"));
341 return retval;
342 }
343 retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
344 }
345 if (retval)
346 com_err("ext2fs_symlink", retval,
347 _("while creating symlink \"%s\""), name);
348 return retval;
349 }
350
351 /* Make a directory in the fs */
352 errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
353 ext2_ino_t root)
354 {
355 char *cp;
356 ext2_ino_t parent_ino;
357 errcode_t retval;
358
359
360 cp = strrchr(name, '/');
361 if (cp) {
362 *cp = 0;
363 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
364 if (retval) {
365 com_err(name, retval, _("while looking up \"%s\""),
366 name);
367 return retval;
368 }
369 name = cp+1;
370 } else
371 parent_ino = cwd;
372
373 retval = ext2fs_mkdir(fs, parent_ino, 0, name);
374 if (retval == EXT2_ET_DIR_NO_SPACE) {
375 retval = ext2fs_expand_dir(fs, parent_ino);
376 if (retval) {
377 com_err(__func__, retval,
378 _("while expanding directory"));
379 return retval;
380 }
381 retval = ext2fs_mkdir(fs, parent_ino, 0, name);
382 }
383 if (retval)
384 com_err("ext2fs_mkdir", retval,
385 _("while creating directory \"%s\""), name);
386 return retval;
387 }
388
389 #if !defined HAVE_PREAD64 && !defined HAVE_PREAD
390 static ssize_t my_pread(int fd, void *buf, size_t count, off_t offset)
391 {
392 if (lseek(fd, offset, SEEK_SET) < 0)
393 return 0;
394
395 return read(fd, buf, count);
396 }
397 #endif /* !defined HAVE_PREAD64 && !defined HAVE_PREAD */
398
399 static errcode_t copy_file_range(ext2_filsys fs, int fd, ext2_file_t e2_file,
400 off_t start, off_t end, char *buf,
401 char *zerobuf)
402 {
403 off_t off, bpos;
404 ssize_t got, blen;
405 unsigned int written;
406 char *ptr;
407 errcode_t err = 0;
408
409 for (off = start; off < end; off += COPY_FILE_BUFLEN) {
410 #ifdef HAVE_PREAD64
411 got = pread64(fd, buf, COPY_FILE_BUFLEN, off);
412 #elif HAVE_PREAD
413 got = pread(fd, buf, COPY_FILE_BUFLEN, off);
414 #else
415 got = my_pread(fd, buf, COPY_FILE_BUFLEN, off);
416 #endif
417 if (got < 0) {
418 err = errno;
419 goto fail;
420 }
421 for (bpos = 0, ptr = buf; bpos < got; bpos += fs->blocksize) {
422 blen = fs->blocksize;
423 if (blen > got - bpos)
424 blen = got - bpos;
425 if (memcmp(ptr, zerobuf, blen) == 0) {
426 ptr += blen;
427 continue;
428 }
429 err = ext2fs_file_lseek(e2_file, off + bpos,
430 EXT2_SEEK_SET, NULL);
431 if (err)
432 goto fail;
433 while (blen > 0) {
434 err = ext2fs_file_write(e2_file, ptr, blen,
435 &written);
436 if (err)
437 goto fail;
438 if (written == 0) {
439 err = EIO;
440 goto fail;
441 }
442 blen -= written;
443 ptr += written;
444 }
445 }
446 }
447 fail:
448 return err;
449 }
450
451 #if defined(SEEK_DATA) && defined(SEEK_HOLE)
452 static errcode_t try_lseek_copy(ext2_filsys fs, int fd, struct stat *statbuf,
453 ext2_file_t e2_file, char *buf, char *zerobuf)
454 {
455 off_t data = 0, hole;
456 off_t data_blk, hole_blk;
457 errcode_t err = 0;
458
459 /* Try to use SEEK_DATA and SEEK_HOLE */
460 while (data < statbuf->st_size) {
461 data = lseek(fd, data, SEEK_DATA);
462 if (data < 0) {
463 if (errno == ENXIO)
464 break;
465 return EXT2_ET_UNIMPLEMENTED;
466 }
467 hole = lseek(fd, data, SEEK_HOLE);
468 if (hole < 0)
469 return EXT2_ET_UNIMPLEMENTED;
470
471 data_blk = data & ~(fs->blocksize - 1);
472 hole_blk = (hole + (fs->blocksize - 1)) & ~(fs->blocksize - 1);
473 err = copy_file_range(fs, fd, e2_file, data_blk, hole_blk, buf,
474 zerobuf);
475 if (err)
476 return err;
477
478 data = hole;
479 }
480
481 return err;
482 }
483 #endif /* SEEK_DATA and SEEK_HOLE */
484
485 #if defined(FS_IOC_FIEMAP)
486 static errcode_t try_fiemap_copy(ext2_filsys fs, int fd, ext2_file_t e2_file,
487 char *buf, char *zerobuf)
488 {
489 #define EXTENT_MAX_COUNT 512
490 struct fiemap *fiemap_buf;
491 struct fiemap_extent *ext_buf, *ext;
492 int ext_buf_size, fie_buf_size;
493 off_t pos = 0;
494 unsigned int i;
495 errcode_t err;
496
497 ext_buf_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent);
498 fie_buf_size = sizeof(struct fiemap) + ext_buf_size;
499
500 err = ext2fs_get_memzero(fie_buf_size, &fiemap_buf);
501 if (err)
502 return err;
503
504 ext_buf = fiemap_buf->fm_extents;
505 memset(fiemap_buf, 0, fie_buf_size);
506 fiemap_buf->fm_length = FIEMAP_MAX_OFFSET;
507 fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC;
508 fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT;
509
510 do {
511 fiemap_buf->fm_start = pos;
512 memset(ext_buf, 0, ext_buf_size);
513 err = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf);
514 if (err < 0 && (errno == EOPNOTSUPP || errno == ENOTTY)) {
515 err = EXT2_ET_UNIMPLEMENTED;
516 goto out;
517 } else if (err < 0) {
518 err = errno;
519 goto out;
520 } else if (fiemap_buf->fm_mapped_extents == 0)
521 goto out;
522 for (i = 0, ext = ext_buf; i < fiemap_buf->fm_mapped_extents;
523 i++, ext++) {
524 err = copy_file_range(fs, fd, e2_file, ext->fe_logical,
525 ext->fe_logical + ext->fe_length,
526 buf, zerobuf);
527 if (err)
528 goto out;
529 }
530
531 ext--;
532 /* Record file's logical offset this time */
533 pos = ext->fe_logical + ext->fe_length;
534 /*
535 * If fm_extents array has been filled and
536 * there are extents left, continue to cycle.
537 */
538 } while (fiemap_buf->fm_mapped_extents == EXTENT_MAX_COUNT &&
539 !(ext->fe_flags & FIEMAP_EXTENT_LAST));
540 out:
541 ext2fs_free_mem(&fiemap_buf);
542 return err;
543 }
544 #endif /* FS_IOC_FIEMAP */
545
546 static errcode_t copy_file(ext2_filsys fs, int fd, struct stat *statbuf,
547 ext2_ino_t ino)
548 {
549 ext2_file_t e2_file;
550 char *buf = NULL, *zerobuf = NULL;
551 errcode_t err, close_err;
552
553 err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
554 if (err)
555 return err;
556
557 err = ext2fs_get_mem(COPY_FILE_BUFLEN, &buf);
558 if (err)
559 goto out;
560
561 err = ext2fs_get_memzero(fs->blocksize, &zerobuf);
562 if (err)
563 goto out;
564
565 #if defined(SEEK_DATA) && defined(SEEK_HOLE)
566 err = try_lseek_copy(fs, fd, statbuf, e2_file, buf, zerobuf);
567 if (err != EXT2_ET_UNIMPLEMENTED)
568 goto out;
569 #endif
570
571 #if defined(FS_IOC_FIEMAP)
572 err = try_fiemap_copy(fs, fd, e2_file, buf, zerobuf);
573 if (err != EXT2_ET_UNIMPLEMENTED)
574 goto out;
575 #endif
576
577 err = copy_file_range(fs, fd, e2_file, 0, statbuf->st_size, buf,
578 zerobuf);
579 out:
580 ext2fs_free_mem(&zerobuf);
581 ext2fs_free_mem(&buf);
582 close_err = ext2fs_file_close(e2_file);
583 if (err == 0)
584 err = close_err;
585 return err;
586 }
587
588 static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino)
589 {
590 int i;
591
592 for (i = 0; i < hdlinks->count; i++) {
593 if (hdlinks->hdl[i].src_dev == dev &&
594 hdlinks->hdl[i].src_ino == ino)
595 return i;
596 }
597 return -1;
598 }
599
600 /* Copy the native file to the fs */
601 errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
602 const char *dest, ext2_ino_t root)
603 {
604 int fd;
605 struct stat statbuf;
606 ext2_ino_t newfile;
607 errcode_t retval;
608 struct ext2_inode inode;
609
610 fd = ext2fs_open_file(src, O_RDONLY, 0);
611 if (fd < 0) {
612 retval = errno;
613 com_err(__func__, retval, _("while opening \"%s\" to copy"),
614 src);
615 return retval;
616 }
617 if (fstat(fd, &statbuf) < 0) {
618 retval = errno;
619 goto out;
620 }
621
622 retval = ext2fs_namei(fs, root, cwd, dest, &newfile);
623 if (retval == 0) {
624 retval = EXT2_ET_FILE_EXISTS;
625 goto out;
626 }
627
628 retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
629 if (retval)
630 goto out;
631 #ifdef DEBUGFS
632 printf("Allocated inode: %u\n", newfile);
633 #endif
634 retval = ext2fs_link(fs, cwd, dest, newfile,
635 EXT2_FT_REG_FILE);
636 if (retval == EXT2_ET_DIR_NO_SPACE) {
637 retval = ext2fs_expand_dir(fs, cwd);
638 if (retval)
639 goto out;
640 retval = ext2fs_link(fs, cwd, dest, newfile,
641 EXT2_FT_REG_FILE);
642 }
643 if (retval)
644 goto out;
645 if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
646 com_err(__func__, 0, "Warning: inode already set");
647 ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
648 memset(&inode, 0, sizeof(inode));
649 inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
650 inode.i_atime = inode.i_ctime = inode.i_mtime =
651 fs->now ? fs->now : time(0);
652 inode.i_links_count = 1;
653 retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size);
654 if (retval)
655 goto out;
656 if (ext2fs_has_feature_inline_data(fs->super)) {
657 inode.i_flags |= EXT4_INLINE_DATA_FL;
658 } else if (ext2fs_has_feature_extents(fs->super)) {
659 ext2_extent_handle_t handle;
660
661 inode.i_flags &= ~EXT4_EXTENTS_FL;
662 retval = ext2fs_extent_open2(fs, newfile, &inode, &handle);
663 if (retval)
664 goto out;
665 ext2fs_extent_free(handle);
666 }
667
668 retval = ext2fs_write_new_inode(fs, newfile, &inode);
669 if (retval)
670 goto out;
671 if (inode.i_flags & EXT4_INLINE_DATA_FL) {
672 retval = ext2fs_inline_data_init(fs, newfile);
673 if (retval)
674 goto out;
675 }
676 if (LINUX_S_ISREG(inode.i_mode)) {
677 retval = copy_file(fs, fd, &statbuf, newfile);
678 if (retval)
679 goto out;
680 }
681 out:
682 close(fd);
683 return retval;
684 }
685
686 /* Copy files from source_dir to fs */
687 static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
688 const char *source_dir, ext2_ino_t root,
689 struct hdlinks_s *hdlinks)
690 {
691 const char *name;
692 DIR *dh;
693 struct dirent *dent;
694 struct stat st;
695 char *ln_target = NULL;
696 unsigned int save_inode;
697 ext2_ino_t ino;
698 errcode_t retval = 0;
699 int read_cnt;
700 int hdlink;
701
702 if (chdir(source_dir) < 0) {
703 retval = errno;
704 com_err(__func__, retval,
705 _("while changing working directory to \"%s\""),
706 source_dir);
707 return retval;
708 }
709
710 if (!(dh = opendir("."))) {
711 retval = errno;
712 com_err(__func__, retval,
713 _("while opening directory \"%s\""), source_dir);
714 return retval;
715 }
716
717 while ((dent = readdir(dh))) {
718 if ((!strcmp(dent->d_name, ".")) ||
719 (!strcmp(dent->d_name, "..")))
720 continue;
721 if (lstat(dent->d_name, &st)) {
722 retval = errno;
723 com_err(__func__, retval, _("while lstat \"%s\""),
724 dent->d_name);
725 goto out;
726 }
727 name = dent->d_name;
728
729 /* Check for hardlinks */
730 save_inode = 0;
731 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
732 st.st_nlink > 1) {
733 hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino);
734 if (hdlink >= 0) {
735 retval = add_link(fs, parent_ino,
736 hdlinks->hdl[hdlink].dst_ino,
737 name);
738 if (retval) {
739 com_err(__func__, retval,
740 "while linking %s", name);
741 goto out;
742 }
743 continue;
744 } else
745 save_inode = 1;
746 }
747
748 switch(st.st_mode & S_IFMT) {
749 case S_IFCHR:
750 case S_IFBLK:
751 case S_IFIFO:
752 case S_IFSOCK:
753 retval = do_mknod_internal(fs, parent_ino, name, &st);
754 if (retval) {
755 com_err(__func__, retval,
756 _("while creating special file "
757 "\"%s\""), name);
758 goto out;
759 }
760 break;
761 case S_IFLNK:
762 ln_target = malloc(st.st_size + 1);
763 if (ln_target == NULL) {
764 com_err(__func__, retval,
765 _("malloc failed"));
766 goto out;
767 }
768 read_cnt = readlink(name, ln_target,
769 st.st_size + 1);
770 if (read_cnt == -1) {
771 retval = errno;
772 com_err(__func__, retval,
773 _("while trying to read link \"%s\""),
774 name);
775 free(ln_target);
776 goto out;
777 }
778 if (read_cnt > st.st_size) {
779 com_err(__func__, retval,
780 _("symlink increased in size "
781 "between lstat() and readlink()"));
782 free(ln_target);
783 goto out;
784 }
785 ln_target[read_cnt] = '\0';
786 retval = do_symlink_internal(fs, parent_ino, name,
787 ln_target, root);
788 free(ln_target);
789 if (retval) {
790 com_err(__func__, retval,
791 _("while writing symlink\"%s\""),
792 name);
793 goto out;
794 }
795 break;
796 case S_IFREG:
797 retval = do_write_internal(fs, parent_ino, name, name,
798 root);
799 if (retval) {
800 com_err(__func__, retval,
801 _("while writing file \"%s\""), name);
802 goto out;
803 }
804 break;
805 case S_IFDIR:
806 /* Don't choke on /lost+found */
807 if (parent_ino == EXT2_ROOT_INO &&
808 strcmp(name, "lost+found") == 0)
809 goto find_lnf;
810 retval = do_mkdir_internal(fs, parent_ino, name,
811 root);
812 if (retval) {
813 com_err(__func__, retval,
814 _("while making dir \"%s\""), name);
815 goto out;
816 }
817 find_lnf:
818 retval = ext2fs_namei(fs, root, parent_ino,
819 name, &ino);
820 if (retval) {
821 com_err(name, retval, 0);
822 goto out;
823 }
824 /* Populate the dir recursively*/
825 retval = __populate_fs(fs, ino, name, root, hdlinks);
826 if (retval)
827 goto out;
828 if (chdir("..")) {
829 retval = errno;
830 com_err(__func__, retval,
831 _("while changing directory"));
832 goto out;
833 }
834 break;
835 default:
836 com_err(__func__, 0,
837 _("ignoring entry \"%s\""), name);
838 }
839
840 retval = ext2fs_namei(fs, root, parent_ino, name, &ino);
841 if (retval) {
842 com_err(name, retval, _("while looking up \"%s\""),
843 name);
844 goto out;
845 }
846
847 retval = set_inode_extra(fs, ino, &st);
848 if (retval) {
849 com_err(__func__, retval,
850 _("while setting inode for \"%s\""), name);
851 goto out;
852 }
853
854 retval = set_inode_xattr(fs, ino, name);
855 if (retval) {
856 com_err(__func__, retval,
857 _("while setting xattrs for \"%s\""), name);
858 goto out;
859 }
860
861 /* Save the hardlink ino */
862 if (save_inode) {
863 /*
864 * Check whether need more memory, and we don't need
865 * free() since the lifespan will be over after the fs
866 * populated.
867 */
868 if (hdlinks->count == hdlinks->size) {
869 void *p = realloc(hdlinks->hdl,
870 (hdlinks->size + HDLINK_CNT) *
871 sizeof(struct hdlink_s));
872 if (p == NULL) {
873 retval = EXT2_ET_NO_MEMORY;
874 com_err(name, retval,
875 _("while saving inode data"));
876 goto out;
877 }
878 hdlinks->hdl = p;
879 hdlinks->size += HDLINK_CNT;
880 }
881 hdlinks->hdl[hdlinks->count].src_dev = st.st_dev;
882 hdlinks->hdl[hdlinks->count].src_ino = st.st_ino;
883 hdlinks->hdl[hdlinks->count].dst_ino = ino;
884 hdlinks->count++;
885 }
886 }
887
888 out:
889 closedir(dh);
890 return retval;
891 }
892
893 errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
894 const char *source_dir, ext2_ino_t root)
895 {
896 struct hdlinks_s hdlinks;
897 errcode_t retval;
898
899 if (!(fs->flags & EXT2_FLAG_RW)) {
900 com_err(__func__, 0, "Filesystem opened readonly");
901 return EROFS;
902 }
903
904 hdlinks.count = 0;
905 hdlinks.size = HDLINK_CNT;
906 hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s));
907 if (hdlinks.hdl == NULL) {
908 retval = errno;
909 com_err(__func__, retval, _("while allocating memory"));
910 return retval;
911 }
912
913 retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks);
914
915 free(hdlinks.hdl);
916 return retval;
917 }