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