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