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