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