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