]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/create_inode.c
mke2fs.conf: enable metadata_csum by default
[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
9d25d462
AS
686struct file_info {
687 char *path;
688 size_t path_len;
689 size_t path_max_len;
690};
691
692static errcode_t path_append(struct file_info *target, const char *file)
693{
694 if (strlen(file) + target->path_len + 1 > target->path_max_len) {
695 target->path_max_len *= 2;
696 target->path = realloc(target->path, target->path_max_len);
697 if (!target->path)
698 return EXT2_ET_NO_MEMORY;
699 }
700 target->path_len += sprintf(target->path + target->path_len, "/%s",
701 file);
702 return 0;
703}
704
0d4deba2 705/* Copy files from source_dir to fs */
b99888a0
DW
706static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
707 const char *source_dir, ext2_ino_t root,
9d25d462
AS
708 struct hdlinks_s *hdlinks,
709 struct file_info *target,
710 struct fs_ops_callbacks *fs_callbacks)
0d4deba2 711{
052064b9
RY
712 const char *name;
713 DIR *dh;
714 struct dirent *dent;
715 struct stat st;
d0e855fa 716 char *ln_target = NULL;
f84894bc 717 unsigned int save_inode;
052064b9 718 ext2_ino_t ino;
1bad6f46 719 errcode_t retval = 0;
052064b9 720 int read_cnt;
f84894bc 721 int hdlink;
9d25d462 722 size_t cur_dir_path_len;
052064b9 723
052064b9 724 if (chdir(source_dir) < 0) {
b04af4fe
DW
725 retval = errno;
726 com_err(__func__, retval,
9c891f7e
DW
727 _("while changing working directory to \"%s\""),
728 source_dir);
b04af4fe 729 return retval;
052064b9
RY
730 }
731
732 if (!(dh = opendir("."))) {
b04af4fe
DW
733 retval = errno;
734 com_err(__func__, retval,
8f8d8a57 735 _("while opening directory \"%s\""), source_dir);
b04af4fe 736 return retval;
052064b9
RY
737 }
738
8f8d8a57 739 while ((dent = readdir(dh))) {
9c891f7e
DW
740 if ((!strcmp(dent->d_name, ".")) ||
741 (!strcmp(dent->d_name, "..")))
052064b9 742 continue;
1bad6f46 743 if (lstat(dent->d_name, &st)) {
b04af4fe
DW
744 retval = errno;
745 com_err(__func__, retval, _("while lstat \"%s\""),
1bad6f46
DW
746 dent->d_name);
747 goto out;
748 }
052064b9
RY
749 name = dent->d_name;
750
f84894bc
RY
751 /* Check for hardlinks */
752 save_inode = 0;
9c891f7e
DW
753 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
754 st.st_nlink > 1) {
b99888a0 755 hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino);
f84894bc 756 if (hdlink >= 0) {
a3111e80 757 retval = add_link(fs, parent_ino,
b99888a0 758 hdlinks->hdl[hdlink].dst_ino,
9c891f7e 759 name);
f84894bc 760 if (retval) {
9c891f7e
DW
761 com_err(__func__, retval,
762 "while linking %s", name);
1bad6f46 763 goto out;
f84894bc
RY
764 }
765 continue;
766 } else
767 save_inode = 1;
768 }
769
9d25d462
AS
770 cur_dir_path_len = target->path_len;
771 retval = path_append(target, name);
772 if (retval)
773 return retval;
774
775 if (fs_callbacks && fs_callbacks->create_new_inode) {
776 retval = fs_callbacks->create_new_inode(fs,
777 target->path, name, parent_ino, root,
778 st.st_mode & S_IFMT);
779 if (retval)
780 goto out;
781 }
782
052064b9 783 switch(st.st_mode & S_IFMT) {
9c891f7e
DW
784 case S_IFCHR:
785 case S_IFBLK:
786 case S_IFIFO:
c61e0068 787 case S_IFSOCK:
a3111e80 788 retval = do_mknod_internal(fs, parent_ino, name, &st);
9c891f7e
DW
789 if (retval) {
790 com_err(__func__, retval,
791 _("while creating special file "
792 "\"%s\""), name);
1bad6f46 793 goto out;
9c891f7e
DW
794 }
795 break;
9c891f7e 796 case S_IFLNK:
d0e855fa
TT
797 ln_target = malloc(st.st_size + 1);
798 if (ln_target == NULL) {
799 com_err(__func__, retval,
800 _("malloc failed"));
801 goto out;
802 }
9c891f7e 803 read_cnt = readlink(name, ln_target,
d0e855fa 804 st.st_size + 1);
9c891f7e 805 if (read_cnt == -1) {
ec3a42b1 806 retval = errno;
b04af4fe
DW
807 com_err(__func__, retval,
808 _("while trying to read link \"%s\""),
809 name);
051fd4e0 810 free(ln_target);
ec3a42b1 811 goto out;
9c891f7e 812 }
d0e855fa
TT
813 if (read_cnt > st.st_size) {
814 com_err(__func__, retval,
815 _("symlink increased in size "
816 "between lstat() and readlink()"));
817 free(ln_target);
818 goto out;
819 }
9c891f7e 820 ln_target[read_cnt] = '\0';
a3111e80
DW
821 retval = do_symlink_internal(fs, parent_ino, name,
822 ln_target, root);
d0e855fa 823 free(ln_target);
9c891f7e
DW
824 if (retval) {
825 com_err(__func__, retval,
826 _("while writing symlink\"%s\""),
827 name);
1bad6f46 828 goto out;
9c891f7e
DW
829 }
830 break;
831 case S_IFREG:
a3111e80
DW
832 retval = do_write_internal(fs, parent_ino, name, name,
833 root);
9c891f7e
DW
834 if (retval) {
835 com_err(__func__, retval,
836 _("while writing file \"%s\""), name);
1bad6f46 837 goto out;
9c891f7e
DW
838 }
839 break;
840 case S_IFDIR:
b04af4fe
DW
841 /* Don't choke on /lost+found */
842 if (parent_ino == EXT2_ROOT_INO &&
843 strcmp(name, "lost+found") == 0)
844 goto find_lnf;
25f291c9 845 retval = do_mkdir_internal(fs, parent_ino, name,
a3111e80 846 root);
9c891f7e
DW
847 if (retval) {
848 com_err(__func__, retval,
849 _("while making dir \"%s\""), name);
1bad6f46 850 goto out;
9c891f7e 851 }
b04af4fe 852find_lnf:
a3111e80 853 retval = ext2fs_namei(fs, root, parent_ino,
9c891f7e
DW
854 name, &ino);
855 if (retval) {
856 com_err(name, retval, 0);
1bad6f46 857 goto out;
9c891f7e
DW
858 }
859 /* Populate the dir recursively*/
9d25d462
AS
860 retval = __populate_fs(fs, ino, name, root, hdlinks,
861 target, fs_callbacks);
b04af4fe 862 if (retval)
1bad6f46 863 goto out;
a3111e80 864 if (chdir("..")) {
1bad6f46 865 retval = errno;
b04af4fe
DW
866 com_err(__func__, retval,
867 _("while changing directory"));
1bad6f46 868 goto out;
a3111e80 869 }
9c891f7e
DW
870 break;
871 default:
872 com_err(__func__, 0,
873 _("ignoring entry \"%s\""), name);
052064b9 874 }
b6960654 875
a3111e80 876 retval = ext2fs_namei(fs, root, parent_ino, name, &ino);
8f8d8a57 877 if (retval) {
b04af4fe
DW
878 com_err(name, retval, _("while looking up \"%s\""),
879 name);
1bad6f46 880 goto out;
b6960654
RY
881 }
882
25f291c9 883 retval = set_inode_extra(fs, ino, &st);
8f8d8a57 884 if (retval) {
b6960654
RY
885 com_err(__func__, retval,
886 _("while setting inode for \"%s\""), name);
1bad6f46 887 goto out;
b6960654 888 }
f84894bc 889
c84da2ee
RB
890 retval = set_inode_xattr(fs, ino, name);
891 if (retval) {
892 com_err(__func__, retval,
893 _("while setting xattrs for \"%s\""), name);
894 goto out;
895 }
896
9d25d462
AS
897 if (fs_callbacks && fs_callbacks->end_create_new_inode) {
898 retval = fs_callbacks->end_create_new_inode(fs,
899 target->path, name, parent_ino, root,
900 st.st_mode & S_IFMT);
901 if (retval)
902 goto out;
903 }
904
f84894bc
RY
905 /* Save the hardlink ino */
906 if (save_inode) {
907 /*
908 * Check whether need more memory, and we don't need
909 * free() since the lifespan will be over after the fs
910 * populated.
911 */
b99888a0
DW
912 if (hdlinks->count == hdlinks->size) {
913 void *p = realloc(hdlinks->hdl,
914 (hdlinks->size + HDLINK_CNT) *
915 sizeof(struct hdlink_s));
916 if (p == NULL) {
1bad6f46 917 retval = EXT2_ET_NO_MEMORY;
b04af4fe
DW
918 com_err(name, retval,
919 _("while saving inode data"));
1bad6f46 920 goto out;
f84894bc 921 }
b99888a0
DW
922 hdlinks->hdl = p;
923 hdlinks->size += HDLINK_CNT;
f84894bc 924 }
b99888a0
DW
925 hdlinks->hdl[hdlinks->count].src_dev = st.st_dev;
926 hdlinks->hdl[hdlinks->count].src_ino = st.st_ino;
927 hdlinks->hdl[hdlinks->count].dst_ino = ino;
928 hdlinks->count++;
f84894bc 929 }
9d25d462
AS
930 target->path_len = cur_dir_path_len;
931 target->path[target->path_len] = 0;
052064b9 932 }
1bad6f46
DW
933
934out:
052064b9
RY
935 closedir(dh);
936 return retval;
0d4deba2 937}
b99888a0 938
9d25d462
AS
939errcode_t populate_fs2(ext2_filsys fs, ext2_ino_t parent_ino,
940 const char *source_dir, ext2_ino_t root,
941 struct fs_ops_callbacks *fs_callbacks)
b99888a0 942{
9d25d462 943 struct file_info file_info;
b99888a0
DW
944 struct hdlinks_s hdlinks;
945 errcode_t retval;
946
b04af4fe
DW
947 if (!(fs->flags & EXT2_FLAG_RW)) {
948 com_err(__func__, 0, "Filesystem opened readonly");
949 return EROFS;
950 }
951
b99888a0
DW
952 hdlinks.count = 0;
953 hdlinks.size = HDLINK_CNT;
954 hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s));
955 if (hdlinks.hdl == NULL) {
b04af4fe
DW
956 retval = errno;
957 com_err(__func__, retval, _("while allocating memory"));
958 return retval;
b99888a0
DW
959 }
960
9d25d462
AS
961 file_info.path_len = 0;
962 file_info.path_max_len = 255;
963 file_info.path = calloc(file_info.path_max_len, 1);
b99888a0 964
9d25d462
AS
965 retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks,
966 &file_info, fs_callbacks);
967
968 free(file_info.path);
b99888a0
DW
969 free(hdlinks.hdl);
970 return retval;
971}
9d25d462
AS
972
973errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
974 const char *source_dir, ext2_ino_t root)
975{
976 return populate_fs2(fs, parent_ino, source_dir, root, NULL);
977}