]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/create_inode.c
Fix FreeBSD portability problem caused by it using character mode disk devices
[thirdparty/e2fsprogs.git] / misc / create_inode.c
CommitLineData
bbccc6f3
AD
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
76f13234
DW
12#define _FILE_OFFSET_BITS 64
13#define _LARGEFILE64_SOURCE 1
14#define _GNU_SOURCE 1
15
16#include "config.h"
8f8d8a57 17#include <time.h>
76f13234 18#include <sys/types.h>
8f8d8a57 19#include <unistd.h>
bbccc6f3 20#include <limits.h> /* for PATH_MAX */
c84da2ee
RB
21#ifdef HAVE_ATTR_XATTR_H
22#include <attr/xattr.h>
23#endif
76f13234 24#include <sys/ioctl.h>
3fb715b5
MF
25#ifdef HAVE_SYS_SYSMACROS_H
26#include <sys/sysmacros.h>
27#endif
28
76f13234
DW
29#include <ext2fs/ext2fs.h>
30#include <ext2fs/ext2_types.h>
31#include <ext2fs/fiemap.h>
8f8d8a57 32
0d4deba2 33#include "create_inode.h"
99ceb8ec 34#include "support/nls-enable.h"
0d4deba2 35
ce600dc4 36/* 64KiB is the minimium blksize to best minimize system call overhead. */
76f13234 37#define COPY_FILE_BUFLEN 65536
ce600dc4 38
a433db04
DW
39static 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
f84894bc 65/* Link an inode number to a directory */
a3111e80
DW
66static errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino,
67 ext2_ino_t ino, const char *name)
f84894bc
RY
68{
69 struct ext2_inode inode;
70 errcode_t retval;
71
a3111e80 72 retval = ext2fs_read_inode(fs, ino, &inode);
f84894bc 73 if (retval) {
b04af4fe 74 com_err(__func__, retval, _("while reading inode %u"), ino);
f84894bc
RY
75 return retval;
76 }
77
a433db04
DW
78 retval = ext2fs_link(fs, parent_ino, name, ino,
79 ext2_file_type(inode.i_mode));
f84894bc 80 if (retval == EXT2_ET_DIR_NO_SPACE) {
a3111e80 81 retval = ext2fs_expand_dir(fs, parent_ino);
f84894bc 82 if (retval) {
b04af4fe
DW
83 com_err(__func__, retval,
84 _("while expanding directory"));
f84894bc
RY
85 return retval;
86 }
a433db04
DW
87 retval = ext2fs_link(fs, parent_ino, name, ino,
88 ext2_file_type(inode.i_mode));
f84894bc
RY
89 }
90 if (retval) {
b04af4fe 91 com_err(__func__, retval, _("while linking \"%s\""), name);
f84894bc
RY
92 return retval;
93 }
94
95 inode.i_links_count++;
96
a3111e80 97 retval = ext2fs_write_inode(fs, ino, &inode);
f84894bc 98 if (retval)
b04af4fe 99 com_err(__func__, retval, _("while writing inode %u"), ino);
f84894bc
RY
100
101 return retval;
102}
103
b6960654 104/* Set the uid, gid, mode and time for the inode */
25f291c9
TT
105static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t ino,
106 struct stat *st)
b6960654
RY
107{
108 errcode_t retval;
109 struct ext2_inode inode;
110
a3111e80 111 retval = ext2fs_read_inode(fs, ino, &inode);
b6960654 112 if (retval) {
b04af4fe 113 com_err(__func__, retval, _("while reading inode %u"), ino);
b6960654
RY
114 return retval;
115 }
116
b04af4fe
DW
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;
b6960654 123
a3111e80 124 retval = ext2fs_write_inode(fs, ino, &inode);
3aec816f 125 if (retval)
b04af4fe 126 com_err(__func__, retval, _("while writing inode %u"), ino);
3aec816f 127 return retval;
b6960654
RY
128}
129
25f291c9 130#ifdef HAVE_LLISTXATTR
b04af4fe
DW
131static errcode_t set_inode_xattr(ext2_filsys fs, ext2_ino_t ino,
132 const char *filename)
c84da2ee 133{
c84da2ee 134 errcode_t retval, close_retval;
c84da2ee
RB
135 struct ext2_xattr_handle *handle;
136 ssize_t size, value_size;
76f13234 137 char *list = NULL;
c84da2ee
RB
138 int i;
139
140 size = llistxattr(filename, NULL, 0);
141 if (size == -1) {
b04af4fe
DW
142 retval = errno;
143 com_err(__func__, retval, _("while listing attributes of \"%s\""),
144 filename);
145 return retval;
c84da2ee
RB
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;
b04af4fe 154 com_err(__func__, retval, _("while opening inode %u"), ino);
c84da2ee
RB
155 return retval;
156 }
157
158 retval = ext2fs_get_mem(size, &list);
159 if (retval) {
b04af4fe 160 com_err(__func__, retval, _("while allocating memory"));
c84da2ee
RB
161 goto out;
162 }
163
164 size = llistxattr(filename, list, size);
165 if (size == -1) {
c84da2ee 166 retval = errno;
b04af4fe
DW
167 com_err(__func__, retval, _("while listing attributes of \"%s\""),
168 filename);
c84da2ee
RB
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
344c043b 176 value_size = lgetxattr(filename, name, NULL, 0);
c84da2ee 177 if (value_size == -1) {
c84da2ee 178 retval = errno;
b04af4fe
DW
179 com_err(__func__, retval,
180 _("while reading attribute \"%s\" of \"%s\""),
181 name, filename);
c84da2ee
RB
182 break;
183 }
184
185 retval = ext2fs_get_mem(value_size, &value);
186 if (retval) {
b04af4fe 187 com_err(__func__, retval, _("while allocating memory"));
c84da2ee
RB
188 break;
189 }
190
344c043b 191 value_size = lgetxattr(filename, name, value, value_size);
c84da2ee
RB
192 if (value_size == -1) {
193 ext2fs_free_mem(&value);
c84da2ee 194 retval = errno;
b04af4fe
DW
195 com_err(__func__, retval,
196 _("while reading attribute \"%s\" of \"%s\""),
197 name, filename);
c84da2ee
RB
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,
b04af4fe
DW
205 _("while writing attribute \"%s\" to inode %u"),
206 name, ino);
c84da2ee
RB
207 break;
208 }
209
210 }
211 out:
212 ext2fs_free_mem(&list);
213 close_retval = ext2fs_xattrs_close(&handle);
214 if (close_retval) {
b04af4fe 215 com_err(__func__, retval, _("while closing inode %u"), ino);
c84da2ee
RB
216 retval = retval ? retval : close_retval;
217 }
218 return retval;
25f291c9
TT
219 return 0;
220}
c84da2ee 221#else /* HAVE_LLISTXATTR */
25f291c9
TT
222static 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{
c84da2ee 226 return 0;
c84da2ee 227}
25f291c9 228#endif /* HAVE_LLISTXATTR */
c84da2ee 229
c61e0068 230/* Make a special files (block and character devices), fifo's, and sockets */
a3111e80
DW
231errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
232 struct stat *st)
0d4deba2 233{
b2346118 234 ext2_ino_t ino;
3cbb51f9 235 errcode_t retval;
b2346118 236 struct ext2_inode inode;
3cbb51f9 237 unsigned long devmajor, devminor, mode;
b2346118
RY
238 int filetype;
239
240 switch(st->st_mode & S_IFMT) {
9c891f7e
DW
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;
c61e0068
DW
253 case S_IFSOCK:
254 mode = LINUX_S_IFSOCK;
255 filetype = EXT2_FT_SOCK;
ec3a42b1 256 break;
3aec816f 257 default:
ec3a42b1 258 return EXT2_ET_INVALID_ARGUMENT;
b2346118
RY
259 }
260
a3111e80 261 retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino);
b2346118 262 if (retval) {
b04af4fe
DW
263 com_err(__func__, retval, _("while allocating inode \"%s\""),
264 name);
b2346118
RY
265 return retval;
266 }
267
268#ifdef DEBUGFS
269 printf("Allocated inode: %u\n", ino);
270#endif
a3111e80 271 retval = ext2fs_link(fs, cwd, name, ino, filetype);
b2346118 272 if (retval == EXT2_ET_DIR_NO_SPACE) {
a3111e80 273 retval = ext2fs_expand_dir(fs, cwd);
b2346118 274 if (retval) {
b04af4fe
DW
275 com_err(__func__, retval,
276 _("while expanding directory"));
b2346118
RY
277 return retval;
278 }
a3111e80 279 retval = ext2fs_link(fs, cwd, name, ino, filetype);
b2346118
RY
280 }
281 if (retval) {
b04af4fe 282 com_err(name, retval, _("while creating inode \"%s\""), name);
ec3a42b1 283 return retval;
b2346118 284 }
a3111e80 285 if (ext2fs_test_inode_bitmap2(fs->inode_map, ino))
b2346118 286 com_err(__func__, 0, "Warning: inode already set");
a3111e80 287 ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
b2346118
RY
288 memset(&inode, 0, sizeof(inode));
289 inode.i_mode = mode;
290 inode.i_atime = inode.i_ctime = inode.i_mtime =
a3111e80 291 fs->now ? fs->now : time(0);
b2346118 292
3cbb51f9
AD
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 }
b2346118
RY
305 }
306 inode.i_links_count = 1;
307
a3111e80 308 retval = ext2fs_write_new_inode(fs, ino, &inode);
b2346118 309 if (retval)
b04af4fe 310 com_err(__func__, retval, _("while writing inode %u"), ino);
b2346118
RY
311
312 return retval;
0d4deba2
RY
313}
314
315/* Make a symlink name -> target */
a3111e80
DW
316errcode_t do_symlink_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
317 char *target, ext2_ino_t root)
0d4deba2 318{
b25ddc5e
RY
319 char *cp;
320 ext2_ino_t parent_ino;
321 errcode_t retval;
b25ddc5e
RY
322
323 cp = strrchr(name, '/');
324 if (cp) {
325 *cp = 0;
a3111e80 326 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
8f8d8a57 327 if (retval) {
b25ddc5e
RY
328 com_err(name, retval, 0);
329 return retval;
330 }
331 name = cp+1;
332 } else
333 parent_ino = cwd;
334
a3111e80 335 retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
b25ddc5e 336 if (retval == EXT2_ET_DIR_NO_SPACE) {
a3111e80 337 retval = ext2fs_expand_dir(fs, parent_ino);
b25ddc5e 338 if (retval) {
9c891f7e 339 com_err("do_symlink_internal", retval,
b04af4fe 340 _("while expanding directory"));
b25ddc5e
RY
341 return retval;
342 }
b04af4fe 343 retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
b25ddc5e 344 }
3aec816f 345 if (retval)
b04af4fe
DW
346 com_err("ext2fs_symlink", retval,
347 _("while creating symlink \"%s\""), name);
3aec816f 348 return retval;
0d4deba2
RY
349}
350
351/* Make a directory in the fs */
a3111e80 352errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
25f291c9 353 ext2_ino_t root)
0d4deba2 354{
3068f03f 355 char *cp;
c4c9bc59 356 ext2_ino_t parent_ino;
3068f03f 357 errcode_t retval;
3068f03f
RY
358
359
360 cp = strrchr(name, '/');
361 if (cp) {
362 *cp = 0;
a3111e80 363 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
8f8d8a57 364 if (retval) {
b04af4fe
DW
365 com_err(name, retval, _("while looking up \"%s\""),
366 name);
3068f03f
RY
367 return retval;
368 }
369 name = cp+1;
370 } else
371 parent_ino = cwd;
372
a3111e80 373 retval = ext2fs_mkdir(fs, parent_ino, 0, name);
3068f03f 374 if (retval == EXT2_ET_DIR_NO_SPACE) {
a3111e80 375 retval = ext2fs_expand_dir(fs, parent_ino);
3068f03f 376 if (retval) {
b04af4fe
DW
377 com_err(__func__, retval,
378 _("while expanding directory"));
3068f03f
RY
379 return retval;
380 }
b04af4fe 381 retval = ext2fs_mkdir(fs, parent_ino, 0, name);
3068f03f 382 }
3aec816f 383 if (retval)
b04af4fe
DW
384 com_err("ext2fs_mkdir", retval,
385 _("while creating directory \"%s\""), name);
3aec816f 386 return retval;
0d4deba2
RY
387}
388
76f13234 389#if !defined HAVE_PREAD64 && !defined HAVE_PREAD
f7134a9e 390static ssize_t my_pread(int fd, void *buf, size_t count, off_t offset)
ce600dc4 391{
76f13234
DW
392 if (lseek(fd, offset, SEEK_SET) < 0)
393 return 0;
ce600dc4 394
76f13234
DW
395 return read(fd, buf, count);
396}
397#endif /* !defined HAVE_PREAD64 && !defined HAVE_PREAD */
ce600dc4 398
76f13234
DW
399static 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
ce600dc4 417 if (got < 0) {
76f13234 418 err = errno;
ce600dc4
RY
419 goto fail;
420 }
76f13234
DW
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;
ce600dc4 440 goto fail;
76f13234
DW
441 }
442 blen -= written;
443 ptr += written;
ce600dc4
RY
444 }
445 }
76f13234
DW
446 }
447fail:
448 return err;
449}
ce600dc4 450
478360f5 451#if defined(SEEK_DATA) && defined(SEEK_HOLE)
76f13234
DW
452static errcode_t try_lseek_copy(ext2_filsys fs, int fd, struct stat *statbuf,
453 ext2_file_t e2_file, char *buf, char *zerobuf)
454{
76f13234
DW
455 off_t data = 0, hole;
456 off_t data_blk, hole_blk;
c2c5c585 457 errcode_t err = 0;
76f13234
DW
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;
ce600dc4 466 }
76f13234
DW
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;
ce600dc4 479 }
ce600dc4 480
76f13234 481 return err;
76f13234 482}
478360f5 483#endif /* SEEK_DATA and SEEK_HOLE */
76f13234 484
478360f5 485#if defined(FS_IOC_FIEMAP)
76f13234
DW
486static errcode_t try_fiemap_copy(ext2_filsys fs, int fd, ext2_file_t e2_file,
487 char *buf, char *zerobuf)
488{
76f13234
DW
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;
75a79045 517 } else if (err < 0) {
76f13234
DW
518 err = errno;
519 goto out;
75a79045
AB
520 } else if (fiemap_buf->fm_mapped_extents == 0)
521 goto out;
76f13234
DW
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));
540out:
541 ext2fs_free_mem(&fiemap_buf);
542 return err;
76f13234 543}
478360f5 544#endif /* FS_IOC_FIEMAP */
76f13234
DW
545
546static 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
478360f5 565#if defined(SEEK_DATA) && defined(SEEK_HOLE)
76f13234
DW
566 err = try_lseek_copy(fs, fd, statbuf, e2_file, buf, zerobuf);
567 if (err != EXT2_ET_UNIMPLEMENTED)
568 goto out;
478360f5 569#endif
76f13234 570
478360f5 571#if defined(FS_IOC_FIEMAP)
76f13234
DW
572 err = try_fiemap_copy(fs, fd, e2_file, buf, zerobuf);
573 if (err != EXT2_ET_UNIMPLEMENTED)
574 goto out;
478360f5 575#endif
76f13234
DW
576
577 err = copy_file_range(fs, fd, e2_file, 0, statbuf->st_size, buf,
578 zerobuf);
579out:
580 ext2fs_free_mem(&zerobuf);
6bb88459 581 ext2fs_free_mem(&buf);
76f13234
DW
582 close_err = ext2fs_file_close(e2_file);
583 if (err == 0)
584 err = close_err;
585 return err;
ce600dc4
RY
586}
587
b99888a0 588static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino)
f84894bc
RY
589{
590 int i;
591
b99888a0
DW
592 for (i = 0; i < hdlinks->count; i++) {
593 if (hdlinks->hdl[i].src_dev == dev &&
594 hdlinks->hdl[i].src_ino == ino)
f84894bc
RY
595 return i;
596 }
597 return -1;
598}
599
0d4deba2 600/* Copy the native file to the fs */
a3111e80
DW
601errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
602 const char *dest, ext2_ino_t root)
0d4deba2 603{
ce600dc4
RY
604 int fd;
605 struct stat statbuf;
606 ext2_ino_t newfile;
607 errcode_t retval;
608 struct ext2_inode inode;
ce600dc4 609
ec3a42b1 610 fd = ext2fs_open_file(src, O_RDONLY, 0);
ce600dc4 611 if (fd < 0) {
b04af4fe
DW
612 retval = errno;
613 com_err(__func__, retval, _("while opening \"%s\" to copy"),
614 src);
615 return retval;
ce600dc4
RY
616 }
617 if (fstat(fd, &statbuf) < 0) {
b04af4fe
DW
618 retval = errno;
619 goto out;
ce600dc4
RY
620 }
621
a3111e80 622 retval = ext2fs_namei(fs, root, cwd, dest, &newfile);
ce600dc4 623 if (retval == 0) {
b04af4fe
DW
624 retval = EXT2_ET_FILE_EXISTS;
625 goto out;
ce600dc4
RY
626 }
627
a3111e80 628 retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
b04af4fe
DW
629 if (retval)
630 goto out;
ce600dc4
RY
631#ifdef DEBUGFS
632 printf("Allocated inode: %u\n", newfile);
633#endif
a3111e80 634 retval = ext2fs_link(fs, cwd, dest, newfile,
ce600dc4
RY
635 EXT2_FT_REG_FILE);
636 if (retval == EXT2_ET_DIR_NO_SPACE) {
a3111e80 637 retval = ext2fs_expand_dir(fs, cwd);
b04af4fe
DW
638 if (retval)
639 goto out;
a3111e80 640 retval = ext2fs_link(fs, cwd, dest, newfile,
ce600dc4
RY
641 EXT2_FT_REG_FILE);
642 }
b04af4fe
DW
643 if (retval)
644 goto out;
a3111e80 645 if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
ce600dc4 646 com_err(__func__, 0, "Warning: inode already set");
a3111e80 647 ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
ce600dc4
RY
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 =
a3111e80 651 fs->now ? fs->now : time(0);
ce600dc4 652 inode.i_links_count = 1;
22302aa3 653 retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size);
b04af4fe
DW
654 if (retval)
655 goto out;
7889640d 656 if (ext2fs_has_feature_inline_data(fs->super)) {
d23b1965 657 inode.i_flags |= EXT4_INLINE_DATA_FL;
7889640d 658 } else if (ext2fs_has_feature_extents(fs->super)) {
3548bb64
DW
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)
b04af4fe 664 goto out;
3548bb64 665 ext2fs_extent_free(handle);
ce600dc4
RY
666 }
667
a3111e80 668 retval = ext2fs_write_new_inode(fs, newfile, &inode);
b04af4fe
DW
669 if (retval)
670 goto out;
d23b1965 671 if (inode.i_flags & EXT4_INLINE_DATA_FL) {
a3111e80 672 retval = ext2fs_inline_data_init(fs, newfile);
b04af4fe
DW
673 if (retval)
674 goto out;
d23b1965 675 }
ce600dc4 676 if (LINUX_S_ISREG(inode.i_mode)) {
76f13234 677 retval = copy_file(fs, fd, &statbuf, newfile);
ce600dc4 678 if (retval)
b04af4fe 679 goto out;
ce600dc4 680 }
b04af4fe 681out:
ce600dc4 682 close(fd);
117b54c3 683 return retval;
0d4deba2
RY
684}
685
686/* Copy files from source_dir to fs */
b99888a0
DW
687static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
688 const char *source_dir, ext2_ino_t root,
689 struct hdlinks_s *hdlinks)
0d4deba2 690{
052064b9
RY
691 const char *name;
692 DIR *dh;
693 struct dirent *dent;
694 struct stat st;
d0e855fa 695 char *ln_target = NULL;
f84894bc 696 unsigned int save_inode;
052064b9 697 ext2_ino_t ino;
1bad6f46 698 errcode_t retval = 0;
052064b9 699 int read_cnt;
f84894bc 700 int hdlink;
052064b9 701
052064b9 702 if (chdir(source_dir) < 0) {
b04af4fe
DW
703 retval = errno;
704 com_err(__func__, retval,
9c891f7e
DW
705 _("while changing working directory to \"%s\""),
706 source_dir);
b04af4fe 707 return retval;
052064b9
RY
708 }
709
710 if (!(dh = opendir("."))) {
b04af4fe
DW
711 retval = errno;
712 com_err(__func__, retval,
8f8d8a57 713 _("while opening directory \"%s\""), source_dir);
b04af4fe 714 return retval;
052064b9
RY
715 }
716
8f8d8a57 717 while ((dent = readdir(dh))) {
9c891f7e
DW
718 if ((!strcmp(dent->d_name, ".")) ||
719 (!strcmp(dent->d_name, "..")))
052064b9 720 continue;
1bad6f46 721 if (lstat(dent->d_name, &st)) {
b04af4fe
DW
722 retval = errno;
723 com_err(__func__, retval, _("while lstat \"%s\""),
1bad6f46
DW
724 dent->d_name);
725 goto out;
726 }
052064b9
RY
727 name = dent->d_name;
728
f84894bc
RY
729 /* Check for hardlinks */
730 save_inode = 0;
9c891f7e
DW
731 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
732 st.st_nlink > 1) {
b99888a0 733 hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino);
f84894bc 734 if (hdlink >= 0) {
a3111e80 735 retval = add_link(fs, parent_ino,
b99888a0 736 hdlinks->hdl[hdlink].dst_ino,
9c891f7e 737 name);
f84894bc 738 if (retval) {
9c891f7e
DW
739 com_err(__func__, retval,
740 "while linking %s", name);
1bad6f46 741 goto out;
f84894bc
RY
742 }
743 continue;
744 } else
745 save_inode = 1;
746 }
747
052064b9 748 switch(st.st_mode & S_IFMT) {
9c891f7e
DW
749 case S_IFCHR:
750 case S_IFBLK:
751 case S_IFIFO:
c61e0068 752 case S_IFSOCK:
a3111e80 753 retval = do_mknod_internal(fs, parent_ino, name, &st);
9c891f7e
DW
754 if (retval) {
755 com_err(__func__, retval,
756 _("while creating special file "
757 "\"%s\""), name);
1bad6f46 758 goto out;
9c891f7e
DW
759 }
760 break;
9c891f7e 761 case S_IFLNK:
d0e855fa
TT
762 ln_target = malloc(st.st_size + 1);
763 if (ln_target == NULL) {
764 com_err(__func__, retval,
765 _("malloc failed"));
766 goto out;
767 }
9c891f7e 768 read_cnt = readlink(name, ln_target,
d0e855fa 769 st.st_size + 1);
9c891f7e 770 if (read_cnt == -1) {
ec3a42b1 771 retval = errno;
b04af4fe
DW
772 com_err(__func__, retval,
773 _("while trying to read link \"%s\""),
774 name);
051fd4e0 775 free(ln_target);
ec3a42b1 776 goto out;
9c891f7e 777 }
d0e855fa
TT
778 if (read_cnt > st.st_size) {
779 com_err(__func__, retval,
780 _("symlink increased in size "
781 "between lstat() and readlink()"));
782 free(ln_target);
783 goto out;
784 }
9c891f7e 785 ln_target[read_cnt] = '\0';
a3111e80
DW
786 retval = do_symlink_internal(fs, parent_ino, name,
787 ln_target, root);
d0e855fa 788 free(ln_target);
9c891f7e
DW
789 if (retval) {
790 com_err(__func__, retval,
791 _("while writing symlink\"%s\""),
792 name);
1bad6f46 793 goto out;
9c891f7e
DW
794 }
795 break;
796 case S_IFREG:
a3111e80
DW
797 retval = do_write_internal(fs, parent_ino, name, name,
798 root);
9c891f7e
DW
799 if (retval) {
800 com_err(__func__, retval,
801 _("while writing file \"%s\""), name);
1bad6f46 802 goto out;
9c891f7e
DW
803 }
804 break;
805 case S_IFDIR:
b04af4fe
DW
806 /* Don't choke on /lost+found */
807 if (parent_ino == EXT2_ROOT_INO &&
808 strcmp(name, "lost+found") == 0)
809 goto find_lnf;
25f291c9 810 retval = do_mkdir_internal(fs, parent_ino, name,
a3111e80 811 root);
9c891f7e
DW
812 if (retval) {
813 com_err(__func__, retval,
814 _("while making dir \"%s\""), name);
1bad6f46 815 goto out;
9c891f7e 816 }
b04af4fe 817find_lnf:
a3111e80 818 retval = ext2fs_namei(fs, root, parent_ino,
9c891f7e
DW
819 name, &ino);
820 if (retval) {
821 com_err(name, retval, 0);
1bad6f46 822 goto out;
9c891f7e
DW
823 }
824 /* Populate the dir recursively*/
b99888a0 825 retval = __populate_fs(fs, ino, name, root, hdlinks);
b04af4fe 826 if (retval)
1bad6f46 827 goto out;
a3111e80 828 if (chdir("..")) {
1bad6f46 829 retval = errno;
b04af4fe
DW
830 com_err(__func__, retval,
831 _("while changing directory"));
1bad6f46 832 goto out;
a3111e80 833 }
9c891f7e
DW
834 break;
835 default:
836 com_err(__func__, 0,
837 _("ignoring entry \"%s\""), name);
052064b9 838 }
b6960654 839
a3111e80 840 retval = ext2fs_namei(fs, root, parent_ino, name, &ino);
8f8d8a57 841 if (retval) {
b04af4fe
DW
842 com_err(name, retval, _("while looking up \"%s\""),
843 name);
1bad6f46 844 goto out;
b6960654
RY
845 }
846
25f291c9 847 retval = set_inode_extra(fs, ino, &st);
8f8d8a57 848 if (retval) {
b6960654
RY
849 com_err(__func__, retval,
850 _("while setting inode for \"%s\""), name);
1bad6f46 851 goto out;
b6960654 852 }
f84894bc 853
c84da2ee
RB
854 retval = set_inode_xattr(fs, ino, name);
855 if (retval) {
856 com_err(__func__, retval,
857 _("while setting xattrs for \"%s\""), name);
858 goto out;
859 }
860
f84894bc
RY
861 /* Save the hardlink ino */
862 if (save_inode) {
863 /*
864 * Check whether need more memory, and we don't need
865 * free() since the lifespan will be over after the fs
866 * populated.
867 */
b99888a0
DW
868 if (hdlinks->count == hdlinks->size) {
869 void *p = realloc(hdlinks->hdl,
870 (hdlinks->size + HDLINK_CNT) *
871 sizeof(struct hdlink_s));
872 if (p == NULL) {
1bad6f46 873 retval = EXT2_ET_NO_MEMORY;
b04af4fe
DW
874 com_err(name, retval,
875 _("while saving inode data"));
1bad6f46 876 goto out;
f84894bc 877 }
b99888a0
DW
878 hdlinks->hdl = p;
879 hdlinks->size += HDLINK_CNT;
f84894bc 880 }
b99888a0
DW
881 hdlinks->hdl[hdlinks->count].src_dev = st.st_dev;
882 hdlinks->hdl[hdlinks->count].src_ino = st.st_ino;
883 hdlinks->hdl[hdlinks->count].dst_ino = ino;
884 hdlinks->count++;
f84894bc 885 }
052064b9 886 }
1bad6f46
DW
887
888out:
052064b9
RY
889 closedir(dh);
890 return retval;
0d4deba2 891}
b99888a0
DW
892
893errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
894 const char *source_dir, ext2_ino_t root)
895{
896 struct hdlinks_s hdlinks;
897 errcode_t retval;
898
b04af4fe
DW
899 if (!(fs->flags & EXT2_FLAG_RW)) {
900 com_err(__func__, 0, "Filesystem opened readonly");
901 return EROFS;
902 }
903
b99888a0
DW
904 hdlinks.count = 0;
905 hdlinks.size = HDLINK_CNT;
906 hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s));
907 if (hdlinks.hdl == NULL) {
b04af4fe
DW
908 retval = errno;
909 com_err(__func__, retval, _("while allocating memory"));
910 return retval;
b99888a0
DW
911 }
912
913 retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks);
914
915 free(hdlinks.hdl);
916 return retval;
917}