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