Whamcloud - gitweb
Fix Coverity unintentional integer overflow warnings
[tools/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/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
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 */
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 */
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
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 */
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  */
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 */
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 */
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
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
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)
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)
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
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
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 */
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
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
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
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 */
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
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
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 }