]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/fuse2fs.c
e2scrub: reorder exit status check after calling lvremove
[thirdparty/e2fsprogs.git] / misc / fuse2fs.c
CommitLineData
81cbf1ef
DW
1/*
2 * fuse2fs.c - FUSE server for e2fsprogs.
3 *
4 * Copyright (C) 2014 Oracle.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
4e222d9b 11#ifndef _GNU_SOURCE
81cbf1ef 12#define _GNU_SOURCE
4e222d9b 13#endif
81cbf1ef
DW
14#include "config.h"
15#include <pthread.h>
16#ifdef __linux__
17# include <linux/fs.h>
18# include <linux/falloc.h>
19# include <linux/xattr.h>
81cbf1ef 20#endif
5c7fec61
DW
21#ifdef HAVE_SYS_XATTR_H
22#include <sys/xattr.h>
23#endif
81cbf1ef
DW
24#include <sys/ioctl.h>
25#include <unistd.h>
13e365bb 26#include <ctype.h>
7dbac9cd 27#define FUSE_DARWIN_ENABLE_EXTENSIONS 0
fb702218
DW
28#ifdef __SET_FOB_FOR_FUSE
29# error Do not set magic value __SET_FOB_FOR_FUSE!!!!
30#endif
31#ifndef _FILE_OFFSET_BITS
32/*
33 * Old versions of libfuse (e.g. Debian 2.9.9 package) required that the build
34 * system set _FILE_OFFSET_BITS explicitly, even if doing so isn't required to
35 * get a 64-bit off_t. AC_SYS_LARGEFILE doesn't set any _FILE_OFFSET_BITS if
36 * it's not required (such as on aarch64), so we must inject it here.
37 */
38# define __SET_FOB_FOR_FUSE
39# define _FILE_OFFSET_BITS 64
40#endif /* _FILE_OFFSET_BITS */
81cbf1ef 41#include <fuse.h>
fb702218
DW
42#ifdef __SET_FOB_FOR_FUSE
43# undef _FILE_OFFSET_BITS
44#endif /* __SET_FOB_FOR_FUSE */
81cbf1ef
DW
45#include <inttypes.h>
46#include "ext2fs/ext2fs.h"
47#include "ext2fs/ext2_fs.h"
9f69dfc4 48#include "ext2fs/ext2fsP.h"
448a3f8b
DD
49#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
50# define FUSE_PLATFORM_OPTS ""
51#else
52# ifdef __linux__
53# define FUSE_PLATFORM_OPTS ",use_ino,big_writes"
54# else
55# define FUSE_PLATFORM_OPTS ",use_ino"
56# endif
57#endif
81cbf1ef 58
69df8496 59#include "../version.h"
667ea124 60#include "uuid/uuid.h"
51680537 61#include "e2p/e2p.h"
69df8496 62
81cbf1ef
DW
63#ifdef ENABLE_NLS
64#include <libintl.h>
65#include <locale.h>
66#define _(a) (gettext(a))
67#ifdef gettext_noop
68#define N_(a) gettext_noop(a)
69#else
70#define N_(a) (a)
71#endif
72#define P_(singular, plural, n) (ngettext(singular, plural, n))
73#ifndef NLS_CAT_NAME
74#define NLS_CAT_NAME "e2fsprogs"
75#endif
76#ifndef LOCALEDIR
77#define LOCALEDIR "/usr/share/locale"
78#endif
79#else
80#define _(a) (a)
81#define N_(a) a
82#define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural))
83#endif
84
cb115a87
DW
85#ifndef XATTR_NAME_POSIX_ACL_DEFAULT
86#define XATTR_NAME_POSIX_ACL_DEFAULT "posix_acl_default"
87#endif
88#ifndef XATTR_SECURITY_PREFIX
89#define XATTR_SECURITY_PREFIX "security."
90#define XATTR_SECURITY_PREFIX_LEN (sizeof (XATTR_SECURITY_PREFIX) - 1)
91#endif
92
9882047b
TT
93/*
94 * Linux and MacOS implement the setxattr(2) interface, which defines
95 * XATTR_CREATE and XATTR_REPLACE. However, FreeBSD uses
96 * extattr_set_file(2), which does not have a flags or options
97 * parameter, and does not define XATTR_CREATE and XATTR_REPLACE.
98 */
99#ifndef XATTR_CREATE
100#define XATTR_CREATE 0
101#endif
102
103#ifndef XATTR_REPLACE
104#define XATTR_REPLACE 0
105#endif
106
dca02019
DW
107#if !defined(EUCLEAN)
108#if !defined(EBADMSG)
109#define EUCLEAN EBADMSG
110#elif !defined(EPROTO)
111#define EUCLEAN EPROTO
112#else
113#define EUCLEAN EIO
114#endif
115#endif /* !defined(EUCLEAN) */
116
84edbfa2
TT
117#if !defined(ENODATA)
118#ifdef ENOATTR
119#define ENODATA ENOATTR
120#else
121#define ENODATA ENOENT
122#endif
123#endif /* !defined(ENODATA) */
124
81cbf1ef
DW
125static ext2_filsys global_fs; /* Try not to use this directly */
126
c4d34d7a
DW
127#define dbg_printf(fuse2fs, format, ...) \
128 while ((fuse2fs)->debug) { \
129 printf("FUSE2FS (%s): " format, (fuse2fs)->shortdev, ##__VA_ARGS__); \
130 fflush(stdout); \
131 break; \
132 }
81cbf1ef 133
cbbf7811
DW
134#define log_printf(fuse2fs, format, ...) \
135 do { \
136 printf("FUSE2FS (%s): " format, (fuse2fs)->shortdev, ##__VA_ARGS__); \
137 fflush(stdout); \
138 } while (0)
139
5cdebf3e
DW
140#define err_printf(fuse2fs, format, ...) \
141 do { \
142 fprintf(stderr, "FUSE2FS (%s): " format, (fuse2fs)->shortdev, ##__VA_ARGS__); \
143 fflush(stderr); \
144 } while (0)
145
81cbf1ef
DW
146#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
147# ifdef _IOR
148# ifdef _IOW
149# define SUPPORT_I_FLAGS
150# endif
151# endif
152#endif
153
154#ifdef FALLOC_FL_KEEP_SIZE
155# define FL_KEEP_SIZE_FLAG FALLOC_FL_KEEP_SIZE
156# define SUPPORT_FALLOCATE
157#else
158# define FL_KEEP_SIZE_FLAG (0)
159#endif
160
161#ifdef FALLOC_FL_PUNCH_HOLE
162# define FL_PUNCH_HOLE_FLAG FALLOC_FL_PUNCH_HOLE
163#else
164# define FL_PUNCH_HOLE_FLAG (0)
165#endif
166
44e024fd
DW
167#ifdef FALLOC_FL_ZERO_RANGE
168# define FL_ZERO_RANGE_FLAG FALLOC_FL_ZERO_RANGE
169#else
170# define FL_ZERO_RANGE_FLAG (0)
171#endif
172
81cbf1ef
DW
173errcode_t ext2fs_run_ext3_journal(ext2_filsys *fs);
174
1fc23b5e
TT
175#ifdef CONFIG_JBD_DEBUG /* Enabled by configure --enable-jbd-debug */
176int journal_enable_debug = -1;
177#endif
178
81cbf1ef
DW
179/*
180 * ext2_file_t contains a struct inode, so we can't leave files open.
181 * Use this as a proxy instead.
182 */
183#define FUSE2FS_FILE_MAGIC (0xEF53DEAFUL)
184struct fuse2fs_file_handle {
185 unsigned long magic;
186 ext2_ino_t ino;
187 int open_flags;
188};
189
190/* Main program context */
191#define FUSE2FS_MAGIC (0xEF53DEADUL)
192struct fuse2fs {
193 unsigned long magic;
194 ext2_filsys fs;
195 pthread_mutex_t bfl;
69df8496 196 char *device;
c4d34d7a 197 char *shortdev;
c7f26885
DW
198 uint8_t ro;
199 uint8_t debug;
200 uint8_t no_default_opts;
201 uint8_t panic_on_error;
202 uint8_t minixdf;
203 uint8_t fakeroot;
204 uint8_t alloc_all_blocks;
205 uint8_t norecovery;
206 uint8_t kernel;
207 uint8_t directio;
0a72f465 208 uint8_t acl;
241dae1b
DW
209
210 int blocklog;
211 unsigned int blockmask;
c4efea44 212 unsigned long offset;
81cbf1ef 213 unsigned int next_generation;
51680537 214 unsigned long long cache_size;
e83c0df0 215 char *lockfile;
81cbf1ef
DW
216};
217
218#define FUSE2FS_CHECK_MAGIC(fs, ptr, num) do {if ((ptr)->magic != (num)) \
4a59e57e 219 return translate_error((fs), 0, EXT2_ET_FILESYSTEM_CORRUPTED); \
81cbf1ef
DW
220} while (0)
221
222#define FUSE2FS_CHECK_CONTEXT(ptr) do {if ((ptr)->magic != FUSE2FS_MAGIC) \
4a59e57e 223 return translate_error(global_fs, 0, EXT2_ET_FILESYSTEM_CORRUPTED); \
81cbf1ef
DW
224} while (0)
225
bb2a4e6c 226static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err,
81cbf1ef 227 const char *file, int line);
bb2a4e6c 228#define translate_error(fs, ino, err) __translate_error((fs), (ino), (err), \
81cbf1ef
DW
229 __FILE__, __LINE__)
230
231/* for macosx */
232#ifndef W_OK
233# define W_OK 2
234#endif
235
236#ifndef R_OK
237# define R_OK 4
238#endif
239
241dae1b
DW
240static inline int u_log2(unsigned int arg)
241{
242 int l = 0;
243
244 arg >>= 1;
245 while (arg) {
246 l++;
247 arg >>= 1;
248 }
249 return l;
250}
251
252static inline blk64_t FUSE2FS_B_TO_FSBT(const struct fuse2fs *ff, off_t pos)
253{
254 return pos >> ff->blocklog;
255}
256
257static inline blk64_t FUSE2FS_B_TO_FSB(const struct fuse2fs *ff, off_t pos)
258{
259 return (pos + ff->blockmask) >> ff->blocklog;
260}
261
262static inline unsigned int FUSE2FS_OFF_IN_FSB(const struct fuse2fs *ff,
263 off_t pos)
264{
265 return pos & ff->blockmask;
266}
267
268static inline off_t FUSE2FS_FSB_TO_B(const struct fuse2fs *ff, blk64_t bno)
269{
270 return bno << ff->blocklog;
271}
272
81cbf1ef
DW
273#define EXT4_EPOCH_BITS 2
274#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
275#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS)
276
277/*
278 * Extended fields will fit into an inode if the filesystem was formatted
279 * with large inodes (-I 256 or larger) and there are not currently any EAs
280 * consuming all of the available space. For new inodes we always reserve
281 * enough space for the kernel's known extended fields, but for inodes
282 * created with an old kernel this might not have been the case. None of
283 * the extended inode fields is critical for correct filesystem operation.
284 * This macro checks if a certain field fits in the inode. Note that
285 * inode-size = GOOD_OLD_INODE_SIZE + i_extra_isize
286 */
287#define EXT4_FITS_IN_INODE(ext4_inode, field) \
288 ((offsetof(typeof(*ext4_inode), field) + \
289 sizeof((ext4_inode)->field)) \
4e222d9b 290 <= ((size_t) EXT2_GOOD_OLD_INODE_SIZE + \
81cbf1ef
DW
291 (ext4_inode)->i_extra_isize)) \
292
293static inline __u32 ext4_encode_extra_time(const struct timespec *time)
294{
295 __u32 extra = sizeof(time->tv_sec) > 4 ?
296 ((time->tv_sec - (__s32)time->tv_sec) >> 32) &
297 EXT4_EPOCH_MASK : 0;
298 return extra | (time->tv_nsec << EXT4_EPOCH_BITS);
299}
300
301static inline void ext4_decode_extra_time(struct timespec *time, __u32 extra)
302{
303 if (sizeof(time->tv_sec) > 4 && (extra & EXT4_EPOCH_MASK)) {
304 __u64 extra_bits = extra & EXT4_EPOCH_MASK;
305 /*
306 * Prior to kernel 3.14?, we had a broken decode function,
307 * wherein we effectively did this:
308 * if (extra_bits == 3)
309 * extra_bits = 0;
310 */
311 time->tv_sec += extra_bits << 32;
312 }
313 time->tv_nsec = ((extra) & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS;
314}
315
e1339587
DW
316#define EXT4_CLAMP_TIMESTAMP(xtime, timespec, raw_inode) \
317do { \
318 if ((timespec)->tv_sec < EXT4_TIMESTAMP_MIN) \
319 (timespec)->tv_sec = EXT4_TIMESTAMP_MIN; \
320 if ((timespec)->tv_sec < EXT4_TIMESTAMP_MIN) \
321 (timespec)->tv_sec = EXT4_TIMESTAMP_MIN; \
322 \
323 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) { \
324 if ((timespec)->tv_sec > EXT4_EXTRA_TIMESTAMP_MAX) \
325 (timespec)->tv_sec = EXT4_EXTRA_TIMESTAMP_MAX; \
326 } else { \
327 if ((timespec)->tv_sec > EXT4_NON_EXTRA_TIMESTAMP_MAX) \
328 (timespec)->tv_sec = EXT4_NON_EXTRA_TIMESTAMP_MAX; \
329 } \
330} while (0)
331
81cbf1ef
DW
332#define EXT4_INODE_SET_XTIME(xtime, timespec, raw_inode) \
333do { \
e1339587
DW
334 typeof(*(timespec)) _ts = *(timespec); \
335 \
336 EXT4_CLAMP_TIMESTAMP(xtime, &_ts, raw_inode); \
337 (raw_inode)->xtime = _ts.tv_sec; \
81cbf1ef
DW
338 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \
339 (raw_inode)->xtime ## _extra = \
e1339587 340 ext4_encode_extra_time(&_ts); \
81cbf1ef
DW
341} while (0)
342
343#define EXT4_EINODE_SET_XTIME(xtime, timespec, raw_inode) \
344do { \
e1339587
DW
345 typeof(*(timespec)) _ts = *(timespec); \
346 \
347 EXT4_CLAMP_TIMESTAMP(xtime, &_ts, raw_inode); \
81cbf1ef 348 if (EXT4_FITS_IN_INODE(raw_inode, xtime)) \
e1339587 349 (raw_inode)->xtime = _ts.tv_sec; \
81cbf1ef
DW
350 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \
351 (raw_inode)->xtime ## _extra = \
e1339587 352 ext4_encode_extra_time(&_ts); \
81cbf1ef
DW
353} while (0)
354
355#define EXT4_INODE_GET_XTIME(xtime, timespec, raw_inode) \
356do { \
357 (timespec)->tv_sec = (signed)((raw_inode)->xtime); \
358 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \
359 ext4_decode_extra_time((timespec), \
360 (raw_inode)->xtime ## _extra); \
361 else \
362 (timespec)->tv_nsec = 0; \
363} while (0)
364
365#define EXT4_EINODE_GET_XTIME(xtime, timespec, raw_inode) \
366do { \
367 if (EXT4_FITS_IN_INODE(raw_inode, xtime)) \
368 (timespec)->tv_sec = \
369 (signed)((raw_inode)->xtime); \
370 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \
371 ext4_decode_extra_time((timespec), \
372 raw_inode->xtime ## _extra); \
373 else \
374 (timespec)->tv_nsec = 0; \
375} while (0)
376
7607e348
DW
377static inline errcode_t fuse2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
378 struct ext2_inode_large *inode)
379{
380 memset(inode, 0, sizeof(*inode));
381 return ext2fs_read_inode_full(fs, ino, EXT2_INODE(inode),
382 sizeof(*inode));
383}
384
385static inline errcode_t fuse2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
386 struct ext2_inode_large *inode)
387{
388 return ext2fs_write_inode_full(fs, ino, EXT2_INODE(inode),
389 sizeof(*inode));
390}
391
81cbf1ef
DW
392static void get_now(struct timespec *now)
393{
394#ifdef CLOCK_REALTIME
395 if (!clock_gettime(CLOCK_REALTIME, now))
396 return;
397#endif
398
399 now->tv_sec = time(NULL);
400 now->tv_nsec = 0;
401}
402
403static void increment_version(struct ext2_inode_large *inode)
404{
405 __u64 ver;
406
407 ver = inode->osd1.linux1.l_i_version;
408 if (EXT4_FITS_IN_INODE(inode, i_version_hi))
409 ver |= (__u64)inode->i_version_hi << 32;
410 ver++;
411 inode->osd1.linux1.l_i_version = ver;
412 if (EXT4_FITS_IN_INODE(inode, i_version_hi))
413 inode->i_version_hi = ver >> 32;
414}
415
416static void init_times(struct ext2_inode_large *inode)
417{
418 struct timespec now;
419
420 get_now(&now);
421 EXT4_INODE_SET_XTIME(i_atime, &now, inode);
422 EXT4_INODE_SET_XTIME(i_ctime, &now, inode);
423 EXT4_INODE_SET_XTIME(i_mtime, &now, inode);
424 EXT4_EINODE_SET_XTIME(i_crtime, &now, inode);
425 increment_version(inode);
426}
427
428static int update_ctime(ext2_filsys fs, ext2_ino_t ino,
429 struct ext2_inode_large *pinode)
430{
431 errcode_t err;
432 struct timespec now;
433 struct ext2_inode_large inode;
434
435 get_now(&now);
436
437 /* If user already has a inode buffer, just update that */
438 if (pinode) {
439 increment_version(pinode);
440 EXT4_INODE_SET_XTIME(i_ctime, &now, pinode);
441 return 0;
442 }
443
444 /* Otherwise we have to read-modify-write the inode */
ca3f4e3a 445 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
446 if (err)
447 return translate_error(fs, ino, err);
448
449 increment_version(&inode);
450 EXT4_INODE_SET_XTIME(i_ctime, &now, &inode);
451
ca3f4e3a 452 err = fuse2fs_write_inode(fs, ino, &inode);
81cbf1ef
DW
453 if (err)
454 return translate_error(fs, ino, err);
455
456 return 0;
457}
458
459static int update_atime(ext2_filsys fs, ext2_ino_t ino)
460{
461 errcode_t err;
462 struct ext2_inode_large inode, *pinode;
463 struct timespec atime, mtime, now;
464
465 if (!(fs->flags & EXT2_FLAG_RW))
466 return 0;
ca3f4e3a 467 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
468 if (err)
469 return translate_error(fs, ino, err);
470
471 pinode = &inode;
472 EXT4_INODE_GET_XTIME(i_atime, &atime, pinode);
473 EXT4_INODE_GET_XTIME(i_mtime, &mtime, pinode);
474 get_now(&now);
475 /*
476 * If atime is newer than mtime and atime hasn't been updated in thirty
477 * seconds, skip the atime update. Same idea as Linux "relatime".
478 */
479 if (atime.tv_sec >= mtime.tv_sec && atime.tv_sec >= now.tv_sec - 30)
480 return 0;
481 EXT4_INODE_SET_XTIME(i_atime, &now, &inode);
482
ca3f4e3a 483 err = fuse2fs_write_inode(fs, ino, &inode);
81cbf1ef
DW
484 if (err)
485 return translate_error(fs, ino, err);
486
487 return 0;
488}
489
490static int update_mtime(ext2_filsys fs, ext2_ino_t ino,
491 struct ext2_inode_large *pinode)
492{
493 errcode_t err;
494 struct ext2_inode_large inode;
495 struct timespec now;
496
497 if (pinode) {
498 get_now(&now);
499 EXT4_INODE_SET_XTIME(i_mtime, &now, pinode);
500 EXT4_INODE_SET_XTIME(i_ctime, &now, pinode);
501 increment_version(pinode);
502 return 0;
503 }
504
ca3f4e3a 505 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
506 if (err)
507 return translate_error(fs, ino, err);
508
509 get_now(&now);
510 EXT4_INODE_SET_XTIME(i_mtime, &now, &inode);
511 EXT4_INODE_SET_XTIME(i_ctime, &now, &inode);
512 increment_version(&inode);
513
ca3f4e3a 514 err = fuse2fs_write_inode(fs, ino, &inode);
81cbf1ef
DW
515 if (err)
516 return translate_error(fs, ino, err);
517
518 return 0;
519}
520
521static int ext2_file_type(unsigned int mode)
522{
523 if (LINUX_S_ISREG(mode))
524 return EXT2_FT_REG_FILE;
525
526 if (LINUX_S_ISDIR(mode))
527 return EXT2_FT_DIR;
528
529 if (LINUX_S_ISCHR(mode))
530 return EXT2_FT_CHRDEV;
531
532 if (LINUX_S_ISBLK(mode))
533 return EXT2_FT_BLKDEV;
534
535 if (LINUX_S_ISLNK(mode))
536 return EXT2_FT_SYMLINK;
537
538 if (LINUX_S_ISFIFO(mode))
539 return EXT2_FT_FIFO;
540
541 if (LINUX_S_ISSOCK(mode))
542 return EXT2_FT_SOCK;
543
544 return 0;
545}
546
547static int fs_can_allocate(struct fuse2fs *ff, blk64_t num)
548{
549 ext2_filsys fs = ff->fs;
550 blk64_t reserved;
551
c4d34d7a 552 dbg_printf(ff, "%s: Asking for %llu; alloc_all=%d total=%llu free=%llu "
81cbf1ef
DW
553 "rsvd=%llu\n", __func__, num, ff->alloc_all_blocks,
554 ext2fs_blocks_count(fs->super),
555 ext2fs_free_blocks_count(fs->super),
556 ext2fs_r_blocks_count(fs->super));
557 if (num > ext2fs_blocks_count(fs->super))
558 return 0;
559
560 if (ff->alloc_all_blocks)
561 return 1;
562
563 /*
564 * Different meaning for r_blocks -- libext2fs has bugs where the FS
565 * can get corrupted if it totally runs out of blocks. Avoid this
566 * by refusing to allocate any of the reserve blocks to anybody.
567 */
568 reserved = ext2fs_r_blocks_count(fs->super);
569 if (reserved == 0)
570 reserved = ext2fs_blocks_count(fs->super) / 10;
571 return ext2fs_free_blocks_count(fs->super) > reserved + num;
572}
573
574static int fs_writeable(ext2_filsys fs)
575{
576 return (fs->flags & EXT2_FLAG_RW) && (fs->super->s_error_count == 0);
577}
578
36f691d3
DW
579static inline int is_superuser(struct fuse2fs *ff, struct fuse_context *ctxt)
580{
581 if (ff->fakeroot)
582 return 1;
583 return ctxt->uid == 0;
584}
585
bc76e0f7
DW
586static inline int want_check_owner(struct fuse2fs *ff,
587 struct fuse_context *ctxt)
588{
589 /*
590 * The kernel is responsible for access control, so we allow anything
591 * that the superuser can do.
592 */
593 if (ff->kernel)
594 return 0;
595 return !is_superuser(ff, ctxt);
596}
597
9f69dfc4
DW
598/* Test for append permission */
599#define A_OK 16
600
2f9b156c
DW
601static int check_iflags_access(struct fuse2fs *ff, ext2_ino_t ino,
602 const struct ext2_inode *inode, int mask)
603{
604 ext2_filsys fs = ff->fs;
605
9f69dfc4
DW
606 EXT2FS_BUILD_BUG_ON((A_OK & (R_OK | W_OK | X_OK | F_OK)) != 0);
607
608 /* no writing or metadata changes to read-only or broken fs */
609 if ((mask & (W_OK | A_OK)) && !fs_writeable(fs))
2f9b156c
DW
610 return -EROFS;
611
9f69dfc4 612 dbg_printf(ff, "access ino=%d mask=e%s%s%s%s iflags=0x%x\n",
2f9b156c
DW
613 ino,
614 (mask & R_OK ? "r" : ""),
615 (mask & W_OK ? "w" : ""),
616 (mask & X_OK ? "x" : ""),
9f69dfc4 617 (mask & A_OK ? "a" : ""),
2f9b156c
DW
618 inode->i_flags);
619
620 /* is immutable? */
621 if ((mask & W_OK) &&
622 (inode->i_flags & EXT2_IMMUTABLE_FL))
623 return -EPERM;
624
9f69dfc4
DW
625 /* is append-only? */
626 if ((inode->i_flags & EXT2_APPEND_FL) && (mask & W_OK) && !(mask & A_OK))
627 return -EPERM;
628
2f9b156c
DW
629 return 0;
630}
631
65ddfa48 632static int check_inum_access(struct fuse2fs *ff, ext2_ino_t ino, int mask)
81cbf1ef
DW
633{
634 struct fuse_context *ctxt = fuse_get_context();
65ddfa48 635 ext2_filsys fs = ff->fs;
81cbf1ef
DW
636 struct ext2_inode inode;
637 mode_t perms;
638 errcode_t err;
2f9b156c 639 int ret;
81cbf1ef
DW
640
641 /* no writing to read-only or broken fs */
9f69dfc4 642 if ((mask & (W_OK | A_OK)) && !fs_writeable(fs))
81cbf1ef
DW
643 return -EROFS;
644
645 err = ext2fs_read_inode(fs, ino, &inode);
646 if (err)
647 return translate_error(fs, ino, err);
648 perms = inode.i_mode & 0777;
649
9f69dfc4 650 dbg_printf(ff, "access ino=%d mask=e%s%s%s%s perms=0%o iflags=0x%x "
7f9166cd
DW
651 "fuid=%d fgid=%d uid=%d gid=%d\n", ino,
652 (mask & R_OK ? "r" : ""),
653 (mask & W_OK ? "w" : ""),
654 (mask & X_OK ? "x" : ""),
9f69dfc4 655 (mask & A_OK ? "a" : ""),
7f9166cd
DW
656 perms, inode.i_flags,
657 inode_uid(inode), inode_gid(inode),
658 ctxt->uid, ctxt->gid);
81cbf1ef
DW
659
660 /* existence check */
661 if (mask == 0)
662 return 0;
663
2f9b156c
DW
664 ret = check_iflags_access(ff, ino, &inode, mask);
665 if (ret)
666 return ret;
81cbf1ef 667
bc76e0f7
DW
668 /* If kernel is responsible for mode and acl checks, we're done. */
669 if (ff->kernel)
670 return 0;
671
81cbf1ef 672 /* Figure out what root's allowed to do */
36f691d3 673 if (is_superuser(ff, ctxt)) {
81cbf1ef
DW
674 /* Non-file access always ok */
675 if (!LINUX_S_ISREG(inode.i_mode))
676 return 0;
677
678 /* R/W access to a file always ok */
679 if (!(mask & X_OK))
680 return 0;
681
682 /* X access to a file ok if a user/group/other can X */
683 if (perms & 0111)
684 return 0;
685
686 /* Trying to execute a file that's not executable. BZZT! */
687 return -EACCES;
688 }
689
de86c43f
TT
690 /* Remove the O_APPEND flag before testing permissions */
691 mask &= ~A_OK;
692
81cbf1ef 693 /* allow owner, if perms match */
dae1ecc2 694 if (inode_uid(inode) == ctxt->uid) {
81cbf1ef
DW
695 if ((mask & (perms >> 6)) == mask)
696 return 0;
697 return -EACCES;
698 }
699
700 /* allow group, if perms match */
dae1ecc2 701 if (inode_gid(inode) == ctxt->gid) {
81cbf1ef
DW
702 if ((mask & (perms >> 3)) == mask)
703 return 0;
704 return -EACCES;
705 }
706
707 /* otherwise check other */
708 if ((mask & perms) == mask)
709 return 0;
710 return -EACCES;
711}
712
4e222d9b 713static void op_destroy(void *p EXT2FS_ATTR((unused)))
81cbf1ef
DW
714{
715 struct fuse_context *ctxt = fuse_get_context();
716 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
717 ext2_filsys fs;
718 errcode_t err;
719
720 if (ff->magic != FUSE2FS_MAGIC) {
721 translate_error(global_fs, 0, EXT2_ET_BAD_MAGIC);
722 return;
723 }
724 fs = ff->fs;
c4d34d7a 725 dbg_printf(ff, "%s: dev=%s\n", __func__, fs->device_name);
81cbf1ef
DW
726 if (fs->flags & EXT2_FLAG_RW) {
727 fs->super->s_state |= EXT2_VALID_FS;
728 if (fs->super->s_error_count)
729 fs->super->s_state |= EXT2_ERROR_FS;
730 ext2fs_mark_super_dirty(fs);
731 err = ext2fs_set_gdt_csum(fs);
732 if (err)
733 translate_error(fs, 0, err);
734
735 err = ext2fs_flush2(fs, 0);
736 if (err)
737 translate_error(fs, 0, err);
738 }
667ea124 739
2fc31e7f
DW
740 if (ff->debug && fs->io->manager->get_stats) {
741 io_stats stats = NULL;
742
743 fs->io->manager->get_stats(fs->io, &stats);
744 dbg_printf(ff, "read: %lluk\n", stats->bytes_read >> 10);
745 dbg_printf(ff, "write: %lluk\n", stats->bytes_written >> 10);
746 dbg_printf(ff, "hits: %llu\n", stats->cache_hits);
747 dbg_printf(ff, "misses: %llu\n", stats->cache_misses);
748 dbg_printf(ff, "hit_ratio: %.1f%%\n",
749 (100.0 * stats->cache_hits) /
750 (stats->cache_hits + stats->cache_misses));
751 }
752
667ea124
DW
753 if (ff->kernel) {
754 char uuid[UUID_STR_SIZE];
755
756 uuid_unparse(fs->super->s_uuid, uuid);
757 log_printf(ff, "%s %s.\n", _("unmounting filesystem"), uuid);
758 }
81cbf1ef
DW
759}
760
448a3f8b
DD
761static void *op_init(struct fuse_conn_info *conn
762#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
157b88c3 763 , struct fuse_config *cfg EXT2FS_ATTR((unused))
448a3f8b
DD
764#endif
765 )
81cbf1ef
DW
766{
767 struct fuse_context *ctxt = fuse_get_context();
768 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
769 ext2_filsys fs;
770 errcode_t err;
771
772 if (ff->magic != FUSE2FS_MAGIC) {
773 translate_error(global_fs, 0, EXT2_ET_BAD_MAGIC);
774 return NULL;
775 }
776 fs = ff->fs;
c4d34d7a 777 dbg_printf(ff, "%s: dev=%s\n", __func__, fs->device_name);
81cbf1ef
DW
778#ifdef FUSE_CAP_IOCTL_DIR
779 conn->want |= FUSE_CAP_IOCTL_DIR;
f2b51fa6 780#endif
0a72f465
DW
781#ifdef FUSE_CAP_POSIX_ACL
782 if (ff->acl)
783 conn->want |= FUSE_CAP_POSIX_ACL;
784#endif
f2b51fa6
DW
785#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
786 conn->time_gran = 1;
e135cae7 787 cfg->use_ino = 1;
015c3f4a
AK
788 if (ff->debug)
789 cfg->debug = 1;
81cbf1ef
DW
790#endif
791 if (fs->flags & EXT2_FLAG_RW) {
792 fs->super->s_mnt_count++;
ca8bc924 793 ext2fs_set_tstamp(fs->super, s_mtime, time(NULL));
81cbf1ef
DW
794 fs->super->s_state &= ~EXT2_VALID_FS;
795 ext2fs_mark_super_dirty(fs);
796 err = ext2fs_flush2(fs, 0);
797 if (err)
798 translate_error(fs, 0, err);
799 }
667ea124
DW
800
801 if (ff->kernel) {
802 char uuid[UUID_STR_SIZE];
803
804 uuid_unparse(fs->super->s_uuid, uuid);
805 log_printf(ff, "%s %s.\n", _("mounted filesystem"), uuid);
806 }
81cbf1ef
DW
807 return ff;
808}
809
81cbf1ef
DW
810static int stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *statbuf)
811{
812 struct ext2_inode_large inode;
813 dev_t fakedev = 0;
814 errcode_t err;
815 int ret = 0;
816 struct timespec tv;
817
ca3f4e3a 818 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
819 if (err)
820 return translate_error(fs, ino, err);
821
822 memcpy(&fakedev, fs->super->s_uuid, sizeof(fakedev));
823 statbuf->st_dev = fakedev;
824 statbuf->st_ino = ino;
825 statbuf->st_mode = inode.i_mode;
826 statbuf->st_nlink = inode.i_links_count;
dae1ecc2
TT
827 statbuf->st_uid = inode_uid(inode);
828 statbuf->st_gid = inode_gid(inode);
81cbf1ef
DW
829 statbuf->st_size = EXT2_I_SIZE(&inode);
830 statbuf->st_blksize = fs->blocksize;
3a15d85b 831 statbuf->st_blocks = ext2fs_get_stat_i_blocks(fs,
a6d88edd 832 EXT2_INODE(&inode));
81cbf1ef 833 EXT4_INODE_GET_XTIME(i_atime, &tv, &inode);
7dbac9cd
TT
834#if HAVE_STRUCT_STAT_ST_ATIM
835 statbuf->st_atim = tv;
836#else
81cbf1ef 837 statbuf->st_atime = tv.tv_sec;
7dbac9cd 838#endif
81cbf1ef 839 EXT4_INODE_GET_XTIME(i_mtime, &tv, &inode);
7dbac9cd
TT
840#if HAVE_STRUCT_STAT_ST_ATIM
841 statbuf->st_mtim = tv;
842#else
81cbf1ef 843 statbuf->st_mtime = tv.tv_sec;
7dbac9cd 844#endif
81cbf1ef 845 EXT4_INODE_GET_XTIME(i_ctime, &tv, &inode);
7dbac9cd
TT
846#if HAVE_STRUCT_STAT_ST_ATIM
847 statbuf->st_ctim = tv;
848#else
81cbf1ef 849 statbuf->st_ctime = tv.tv_sec;
7dbac9cd 850#endif
81cbf1ef
DW
851 if (LINUX_S_ISCHR(inode.i_mode) ||
852 LINUX_S_ISBLK(inode.i_mode)) {
853 if (inode.i_block[0])
854 statbuf->st_rdev = inode.i_block[0];
855 else
856 statbuf->st_rdev = inode.i_block[1];
857 }
858
859 return ret;
860}
861
448a3f8b
DD
862static int op_getattr(const char *path, struct stat *statbuf
863#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
157b88c3 864 , struct fuse_file_info *fi EXT2FS_ATTR((unused))
448a3f8b
DD
865#endif
866 )
81cbf1ef
DW
867{
868 struct fuse_context *ctxt = fuse_get_context();
869 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
870 ext2_filsys fs;
871 ext2_ino_t ino;
872 errcode_t err;
873 int ret = 0;
874
875 FUSE2FS_CHECK_CONTEXT(ff);
876 fs = ff->fs;
c4d34d7a 877 dbg_printf(ff, "%s: path=%s\n", __func__, path);
81cbf1ef
DW
878 pthread_mutex_lock(&ff->bfl);
879 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
880 if (err) {
881 ret = translate_error(fs, 0, err);
882 goto out;
883 }
884 ret = stat_inode(fs, ino, statbuf);
885out:
886 pthread_mutex_unlock(&ff->bfl);
887 return ret;
888}
889
890static int op_readlink(const char *path, char *buf, size_t len)
891{
892 struct fuse_context *ctxt = fuse_get_context();
893 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
894 ext2_filsys fs;
895 errcode_t err;
896 ext2_ino_t ino;
897 struct ext2_inode inode;
898 unsigned int got;
899 ext2_file_t file;
900 int ret = 0;
901
902 FUSE2FS_CHECK_CONTEXT(ff);
903 fs = ff->fs;
c4d34d7a 904 dbg_printf(ff, "%s: path=%s\n", __func__, path);
81cbf1ef
DW
905 pthread_mutex_lock(&ff->bfl);
906 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
907 if (err || ino == 0) {
908 ret = translate_error(fs, 0, err);
909 goto out;
910 }
911
912 err = ext2fs_read_inode(fs, ino, &inode);
913 if (err) {
914 ret = translate_error(fs, ino, err);
915 goto out;
916 }
917
918 if (!LINUX_S_ISLNK(inode.i_mode)) {
919 ret = -EINVAL;
920 goto out;
921 }
922
923 len--;
924 if (inode.i_size < len)
925 len = inode.i_size;
cf0be234
TE
926 if (ext2fs_is_fast_symlink(&inode))
927 memcpy(buf, (char *)inode.i_block, len);
928 else {
81cbf1ef
DW
929 /* big/inline symlink */
930
931 err = ext2fs_file_open(fs, ino, 0, &file);
932 if (err) {
933 ret = translate_error(fs, ino, err);
934 goto out;
935 }
936
937 err = ext2fs_file_read(file, buf, len, &got);
938 if (err || got != len) {
939 ext2fs_file_close(file);
940 ret = translate_error(fs, ino, err);
941 goto out2;
942 }
943
944out2:
945 err = ext2fs_file_close(file);
946 if (ret)
947 goto out;
948 if (err) {
949 ret = translate_error(fs, ino, err);
950 goto out;
951 }
cf0be234 952 }
81cbf1ef
DW
953 buf[len] = 0;
954
955 if (fs_writeable(fs)) {
956 ret = update_atime(fs, ino);
957 if (ret)
958 goto out;
959 }
960
961out:
962 pthread_mutex_unlock(&ff->bfl);
963 return ret;
964}
965
2c790038
DW
966static int __getxattr(struct fuse2fs *ff, ext2_ino_t ino, const char *name,
967 void **value, size_t *value_len)
968{
969 ext2_filsys fs = ff->fs;
970 struct ext2_xattr_handle *h;
971 errcode_t err;
972 int ret = 0;
973
974 err = ext2fs_xattrs_open(fs, ino, &h);
975 if (err)
976 return translate_error(fs, ino, err);
977
978 err = ext2fs_xattrs_read(h);
979 if (err) {
980 ret = translate_error(fs, ino, err);
981 goto out_close;
982 }
983
984 err = ext2fs_xattr_get(h, name, value, value_len);
985 if (err) {
986 ret = translate_error(fs, ino, err);
987 goto out_close;
988 }
989
990out_close:
991 err = ext2fs_xattrs_close(&h);
992 if (err && !ret)
993 ret = translate_error(fs, ino, err);
994 return ret;
995}
996
997static int __setxattr(struct fuse2fs *ff, ext2_ino_t ino, const char *name,
998 void *value, size_t valuelen)
999{
1000 ext2_filsys fs = ff->fs;
1001 struct ext2_xattr_handle *h;
1002 errcode_t err;
1003 int ret = 0;
1004
1005 err = ext2fs_xattrs_open(fs, ino, &h);
1006 if (err)
1007 return translate_error(fs, ino, err);
1008
1009 err = ext2fs_xattrs_read(h);
1010 if (err) {
1011 ret = translate_error(fs, ino, err);
1012 goto out_close;
1013 }
1014
1015 err = ext2fs_xattr_set(h, name, value, valuelen);
1016 if (err) {
1017 ret = translate_error(fs, ino, err);
1018 goto out_close;
1019 }
1020
1021out_close:
1022 err = ext2fs_xattrs_close(&h);
1023 if (err && !ret)
1024 ret = translate_error(fs, ino, err);
1025 return ret;
1026}
1027
1028static int propagate_default_acls(struct fuse2fs *ff, ext2_ino_t parent,
1029 ext2_ino_t child)
1030{
1031 void *def;
1032 size_t deflen;
1033 int ret;
1034
1035 if (!ff->acl)
1036 return 0;
1037
1038 ret = __getxattr(ff, parent, XATTR_NAME_POSIX_ACL_DEFAULT, &def,
1039 &deflen);
1040 switch (ret) {
1041 case -ENODATA:
1042 case -ENOENT:
1043 /* no default acl */
1044 return 0;
1045 case 0:
1046 break;
1047 default:
1048 return ret;
1049 }
1050
1051 ret = __setxattr(ff, child, XATTR_NAME_POSIX_ACL_DEFAULT, def, deflen);
1052 ext2fs_free_mem(&def);
1053 return ret;
1054}
1055
81cbf1ef
DW
1056static int op_mknod(const char *path, mode_t mode, dev_t dev)
1057{
1058 struct fuse_context *ctxt = fuse_get_context();
1059 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1060 ext2_filsys fs;
1061 ext2_ino_t parent, child;
da9d5b80 1062 char *temp_path;
81cbf1ef
DW
1063 errcode_t err;
1064 char *node_name, a;
1065 int filetype;
1066 struct ext2_inode_large inode;
1067 int ret = 0;
1068
1069 FUSE2FS_CHECK_CONTEXT(ff);
1070 fs = ff->fs;
c4d34d7a 1071 dbg_printf(ff, "%s: path=%s mode=0%o dev=0x%x\n", __func__, path, mode,
81cbf1ef 1072 (unsigned int)dev);
da9d5b80 1073 temp_path = strdup(path);
81cbf1ef
DW
1074 if (!temp_path) {
1075 ret = -ENOMEM;
1076 goto out;
1077 }
1078 node_name = strrchr(temp_path, '/');
1079 if (!node_name) {
1080 ret = -ENOMEM;
1081 goto out;
1082 }
1083 node_name++;
1084 a = *node_name;
1085 *node_name = 0;
1086
1087 pthread_mutex_lock(&ff->bfl);
1088 if (!fs_can_allocate(ff, 2)) {
1089 ret = -ENOSPC;
1090 goto out2;
1091 }
1092
1093 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1094 &parent);
1095 if (err) {
1096 ret = translate_error(fs, 0, err);
1097 goto out2;
1098 }
1099
9f69dfc4 1100 ret = check_inum_access(ff, parent, A_OK | W_OK);
81cbf1ef
DW
1101 if (ret)
1102 goto out2;
1103
1104 *node_name = a;
1105
1106 if (LINUX_S_ISCHR(mode))
1107 filetype = EXT2_FT_CHRDEV;
1108 else if (LINUX_S_ISBLK(mode))
1109 filetype = EXT2_FT_BLKDEV;
1110 else if (LINUX_S_ISFIFO(mode))
1111 filetype = EXT2_FT_FIFO;
1112 else if (LINUX_S_ISSOCK(mode))
1113 filetype = EXT2_FT_SOCK;
1114 else {
1115 ret = -EINVAL;
1116 goto out2;
1117 }
1118
1119 err = ext2fs_new_inode(fs, parent, mode, 0, &child);
1120 if (err) {
1121 ret = translate_error(fs, 0, err);
1122 goto out2;
1123 }
1124
c4d34d7a 1125 dbg_printf(ff, "%s: create ino=%d/name=%s in dir=%d\n", __func__, child,
81cbf1ef 1126 node_name, parent);
0a5dc78b
TT
1127 err = ext2fs_link(fs, parent, node_name, child,
1128 filetype | EXT2FS_LINK_EXPAND);
81cbf1ef
DW
1129 if (err) {
1130 ret = translate_error(fs, parent, err);
1131 goto out2;
1132 }
1133
1134 ret = update_mtime(fs, parent, NULL);
1135 if (ret)
1136 goto out2;
1137
1138 memset(&inode, 0, sizeof(inode));
1139 inode.i_mode = mode;
1140
1141 if (dev & ~0xFFFF)
1142 inode.i_block[1] = dev;
1143 else
1144 inode.i_block[0] = dev;
1145 inode.i_links_count = 1;
1146 inode.i_extra_isize = sizeof(struct ext2_inode_large) -
1147 EXT2_GOOD_OLD_INODE_SIZE;
f150bdec 1148 inode.i_uid = ctxt->uid;
dae1ecc2 1149 ext2fs_set_i_uid_high(inode, ctxt->uid >> 16);
f150bdec 1150 inode.i_gid = ctxt->gid;
dae1ecc2 1151 ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
81cbf1ef 1152
a6d88edd 1153 err = ext2fs_write_new_inode(fs, child, EXT2_INODE(&inode));
81cbf1ef
DW
1154 if (err) {
1155 ret = translate_error(fs, child, err);
1156 goto out2;
1157 }
1158
1159 inode.i_generation = ff->next_generation++;
1160 init_times(&inode);
ca3f4e3a 1161 err = fuse2fs_write_inode(fs, child, &inode);
81cbf1ef
DW
1162 if (err) {
1163 ret = translate_error(fs, child, err);
1164 goto out2;
1165 }
1166
1167 ext2fs_inode_alloc_stats2(fs, child, 1, 0);
1168
2c790038
DW
1169 ret = propagate_default_acls(ff, parent, child);
1170 if (ret)
1171 goto out2;
81cbf1ef
DW
1172out2:
1173 pthread_mutex_unlock(&ff->bfl);
1174out:
1175 free(temp_path);
1176 return ret;
1177}
1178
1179static int op_mkdir(const char *path, mode_t mode)
1180{
1181 struct fuse_context *ctxt = fuse_get_context();
1182 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1183 ext2_filsys fs;
1184 ext2_ino_t parent, child;
da9d5b80 1185 char *temp_path;
81cbf1ef
DW
1186 errcode_t err;
1187 char *node_name, a;
1188 struct ext2_inode_large inode;
1189 char *block;
1190 blk64_t blk;
1191 int ret = 0;
1192 mode_t parent_sgid;
1193
1194 FUSE2FS_CHECK_CONTEXT(ff);
1195 fs = ff->fs;
c4d34d7a 1196 dbg_printf(ff, "%s: path=%s mode=0%o\n", __func__, path, mode);
da9d5b80 1197 temp_path = strdup(path);
81cbf1ef
DW
1198 if (!temp_path) {
1199 ret = -ENOMEM;
1200 goto out;
1201 }
1202 node_name = strrchr(temp_path, '/');
1203 if (!node_name) {
1204 ret = -ENOMEM;
1205 goto out;
1206 }
1207 node_name++;
1208 a = *node_name;
1209 *node_name = 0;
1210
1211 pthread_mutex_lock(&ff->bfl);
1212 if (!fs_can_allocate(ff, 1)) {
1213 ret = -ENOSPC;
1214 goto out2;
1215 }
1216
1217 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1218 &parent);
1219 if (err) {
1220 ret = translate_error(fs, 0, err);
1221 goto out2;
1222 }
1223
9f69dfc4 1224 ret = check_inum_access(ff, parent, A_OK | W_OK);
81cbf1ef
DW
1225 if (ret)
1226 goto out2;
1227
1228 /* Is the parent dir sgid? */
ca3f4e3a 1229 err = fuse2fs_read_inode(fs, parent, &inode);
81cbf1ef
DW
1230 if (err) {
1231 ret = translate_error(fs, parent, err);
1232 goto out2;
1233 }
1234 parent_sgid = inode.i_mode & S_ISGID;
1235
1236 *node_name = a;
1237
b107c3a2
TT
1238 err = ext2fs_mkdir2(fs, parent, 0, 0, EXT2FS_LINK_EXPAND,
1239 node_name, NULL);
81cbf1ef
DW
1240 if (err) {
1241 ret = translate_error(fs, parent, err);
1242 goto out2;
1243 }
1244
1245 ret = update_mtime(fs, parent, NULL);
1246 if (ret)
1247 goto out2;
1248
1249 /* Still have to update the uid/gid of the dir */
1250 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1251 &child);
1252 if (err) {
1253 ret = translate_error(fs, 0, err);
1254 goto out2;
1255 }
c4d34d7a 1256 dbg_printf(ff, "%s: created ino=%d/path=%s in dir=%d\n", __func__, child,
81cbf1ef
DW
1257 node_name, parent);
1258
ca3f4e3a 1259 err = fuse2fs_read_inode(fs, child, &inode);
81cbf1ef
DW
1260 if (err) {
1261 ret = translate_error(fs, child, err);
1262 goto out2;
1263 }
1264
1265 inode.i_uid = ctxt->uid;
dae1ecc2 1266 ext2fs_set_i_uid_high(inode, ctxt->uid >> 16);
81cbf1ef 1267 inode.i_gid = ctxt->gid;
dae1ecc2 1268 ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
4cfa9f70 1269 inode.i_mode = LINUX_S_IFDIR | (mode & ~S_ISUID) |
81cbf1ef
DW
1270 parent_sgid;
1271 inode.i_generation = ff->next_generation++;
f73fbf8e 1272 init_times(&inode);
81cbf1ef 1273
ca3f4e3a 1274 err = fuse2fs_write_inode(fs, child, &inode);
81cbf1ef
DW
1275 if (err) {
1276 ret = translate_error(fs, child, err);
1277 goto out2;
1278 }
1279
1280 /* Rewrite the directory block checksum, having set i_generation */
1281 if ((inode.i_flags & EXT4_INLINE_DATA_FL) ||
7889640d 1282 !ext2fs_has_feature_metadata_csum(fs->super))
81cbf1ef
DW
1283 goto out2;
1284 err = ext2fs_new_dir_block(fs, child, parent, &block);
1285 if (err) {
1286 ret = translate_error(fs, child, err);
1287 goto out2;
1288 }
a6d88edd 1289 err = ext2fs_bmap2(fs, child, EXT2_INODE(&inode), NULL, 0, 0,
81cbf1ef
DW
1290 NULL, &blk);
1291 if (err) {
1292 ret = translate_error(fs, child, err);
1293 goto out3;
1294 }
1295 err = ext2fs_write_dir_block4(fs, blk, block, 0, child);
1296 if (err) {
1297 ret = translate_error(fs, child, err);
1298 goto out3;
1299 }
1300
2c790038
DW
1301 ret = propagate_default_acls(ff, parent, child);
1302 if (ret)
1303 goto out3;
1304
81cbf1ef
DW
1305out3:
1306 ext2fs_free_mem(&block);
1307out2:
1308 pthread_mutex_unlock(&ff->bfl);
1309out:
1310 free(temp_path);
1311 return ret;
1312}
1313
c4d34d7a 1314static int unlink_file_by_name(struct fuse2fs *ff, const char *path)
81cbf1ef 1315{
c4d34d7a 1316 ext2_filsys fs = ff->fs;
81cbf1ef
DW
1317 errcode_t err;
1318 ext2_ino_t dir;
1319 char *filename = strdup(path);
1320 char *base_name;
1321 int ret;
1322
1323 base_name = strrchr(filename, '/');
1324 if (base_name) {
1325 *base_name++ = '\0';
1326 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, filename,
1327 &dir);
1328 if (err) {
1329 free(filename);
1330 return translate_error(fs, 0, err);
1331 }
1332 } else {
1333 dir = EXT2_ROOT_INO;
1334 base_name = filename;
1335 }
1336
65ddfa48 1337 ret = check_inum_access(ff, dir, W_OK);
81cbf1ef
DW
1338 if (ret) {
1339 free(filename);
1340 return ret;
1341 }
1342
c4d34d7a 1343 dbg_printf(ff, "%s: unlinking name=%s from dir=%d\n", __func__,
81cbf1ef
DW
1344 base_name, dir);
1345 err = ext2fs_unlink(fs, dir, base_name, 0, 0);
1346 free(filename);
1347 if (err)
1348 return translate_error(fs, dir, err);
1349
1350 return update_mtime(fs, dir, NULL);
1351}
1352
3045aed6
DW
1353static errcode_t remove_ea_inodes(struct fuse2fs *ff, ext2_ino_t ino,
1354 struct ext2_inode_large *inode)
1355{
1356 ext2_filsys fs = ff->fs;
1357 struct ext2_xattr_handle *h;
1358 errcode_t err;
1359
1360 /*
1361 * The xattr handle maintains its own private copy of the inode, so
1362 * write ours to disk so that we can read it.
1363 */
1364 err = fuse2fs_write_inode(fs, ino, inode);
1365 if (err)
1366 return err;
1367
1368 err = ext2fs_xattrs_open(fs, ino, &h);
1369 if (err)
1370 return err;
1371
1372 err = ext2fs_xattrs_read(h);
1373 if (err)
1374 goto out_close;
1375
1376 err = ext2fs_xattr_remove_all(h);
1377 if (err)
1378 goto out_close;
1379
1380out_close:
1381 ext2fs_xattrs_close(&h);
1382
1383 /* Now read the inode back in. */
1384 return fuse2fs_read_inode(fs, ino, inode);
1385}
1386
81cbf1ef
DW
1387static int remove_inode(struct fuse2fs *ff, ext2_ino_t ino)
1388{
1389 ext2_filsys fs = ff->fs;
1390 errcode_t err;
1391 struct ext2_inode_large inode;
1392 int ret = 0;
1393
ca3f4e3a 1394 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
1395 if (err) {
1396 ret = translate_error(fs, ino, err);
1397 goto out;
1398 }
c4d34d7a 1399 dbg_printf(ff, "%s: put ino=%d links=%d\n", __func__, ino,
81cbf1ef
DW
1400 inode.i_links_count);
1401
1402 switch (inode.i_links_count) {
1403 case 0:
1404 return 0; /* XXX: already done? */
1405 case 1:
1406 inode.i_links_count--;
f590d714 1407 ext2fs_set_dtime(fs, EXT2_INODE(&inode));
81cbf1ef
DW
1408 break;
1409 default:
1410 inode.i_links_count--;
1411 }
1412
1413 ret = update_ctime(fs, ino, &inode);
1414 if (ret)
1415 goto out;
1416
1417 if (inode.i_links_count)
1418 goto write_out;
1419
3045aed6
DW
1420 if (ext2fs_has_feature_ea_inode(fs->super)) {
1421 err = remove_ea_inodes(ff, ino, &inode);
1422 if (err)
1423 goto write_out;
1424 }
1425
81cbf1ef
DW
1426 /* Nobody holds this file; free its blocks! */
1427 err = ext2fs_free_ext_attr(fs, ino, &inode);
1428 if (err)
1429 goto write_out;
1430
a6d88edd
DW
1431 if (ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(&inode))) {
1432 err = ext2fs_punch(fs, ino, EXT2_INODE(&inode), NULL,
81cbf1ef
DW
1433 0, ~0ULL);
1434 if (err) {
1435 ret = translate_error(fs, ino, err);
1436 goto write_out;
1437 }
1438 }
1439
1440 ext2fs_inode_alloc_stats2(fs, ino, -1,
1441 LINUX_S_ISDIR(inode.i_mode));
1442
1443write_out:
ca3f4e3a 1444 err = fuse2fs_write_inode(fs, ino, &inode);
81cbf1ef
DW
1445 if (err) {
1446 ret = translate_error(fs, ino, err);
1447 goto out;
1448 }
1449out:
1450 return ret;
1451}
1452
1453static int __op_unlink(struct fuse2fs *ff, const char *path)
1454{
1455 ext2_filsys fs = ff->fs;
1456 ext2_ino_t ino;
1457 errcode_t err;
1458 int ret = 0;
1459
1460 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
1461 if (err) {
1462 ret = translate_error(fs, 0, err);
1463 goto out;
1464 }
1465
2f9b156c
DW
1466 ret = check_inum_access(ff, ino, W_OK);
1467 if (ret)
1468 goto out;
1469
c4d34d7a 1470 ret = unlink_file_by_name(ff, path);
81cbf1ef
DW
1471 if (ret)
1472 goto out;
1473
1474 ret = remove_inode(ff, ino);
1475 if (ret)
1476 goto out;
1477out:
1478 return ret;
1479}
1480
1481static int op_unlink(const char *path)
1482{
1483 struct fuse_context *ctxt = fuse_get_context();
1484 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1485 int ret;
1486
1487 FUSE2FS_CHECK_CONTEXT(ff);
1488 pthread_mutex_lock(&ff->bfl);
1489 ret = __op_unlink(ff, path);
1490 pthread_mutex_unlock(&ff->bfl);
1491 return ret;
1492}
1493
1494struct rd_struct {
1495 ext2_ino_t parent;
1496 int empty;
1497};
1498
1499static int rmdir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
1500 int entry EXT2FS_ATTR((unused)),
1501 struct ext2_dir_entry *dirent,
1502 int offset EXT2FS_ATTR((unused)),
1503 int blocksize EXT2FS_ATTR((unused)),
1504 char *buf EXT2FS_ATTR((unused)),
1505 void *private)
1506{
1507 struct rd_struct *rds = (struct rd_struct *) private;
1508
1509 if (dirent->inode == 0)
1510 return 0;
1511 if (((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.'))
1512 return 0;
1513 if (((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') &&
1514 (dirent->name[1] == '.')) {
1515 rds->parent = dirent->inode;
1516 return 0;
1517 }
1518 rds->empty = 0;
1519 return 0;
1520}
1521
1522static int __op_rmdir(struct fuse2fs *ff, const char *path)
1523{
1524 ext2_filsys fs = ff->fs;
1525 ext2_ino_t child;
1526 errcode_t err;
1527 struct ext2_inode_large inode;
1528 struct rd_struct rds;
1529 int ret = 0;
1530
1531 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &child);
1532 if (err) {
1533 ret = translate_error(fs, 0, err);
1534 goto out;
1535 }
c4d34d7a 1536 dbg_printf(ff, "%s: rmdir path=%s ino=%d\n", __func__, path, child);
81cbf1ef 1537
2f9b156c
DW
1538 ret = check_inum_access(ff, child, W_OK);
1539 if (ret)
1540 goto out;
1541
81cbf1ef
DW
1542 rds.parent = 0;
1543 rds.empty = 1;
1544
1545 err = ext2fs_dir_iterate2(fs, child, 0, 0, rmdir_proc, &rds);
1546 if (err) {
1547 ret = translate_error(fs, child, err);
1548 goto out;
1549 }
1550
2f9b156c
DW
1551 /* the kernel checks parent permissions before emptiness */
1552 if (rds.parent == 0) {
1553 ret = translate_error(fs, child, EXT2_ET_FILESYSTEM_CORRUPTED);
1554 goto out;
1555 }
1556
1557 ret = check_inum_access(ff, rds.parent, W_OK);
1558 if (ret)
1559 goto out;
1560
81cbf1ef
DW
1561 if (rds.empty == 0) {
1562 ret = -ENOTEMPTY;
1563 goto out;
1564 }
1565
c4d34d7a 1566 ret = unlink_file_by_name(ff, path);
81cbf1ef
DW
1567 if (ret)
1568 goto out;
1569 /* Directories have to be "removed" twice. */
1570 ret = remove_inode(ff, child);
1571 if (ret)
1572 goto out;
1573 ret = remove_inode(ff, child);
1574 if (ret)
1575 goto out;
1576
1577 if (rds.parent) {
c4d34d7a 1578 dbg_printf(ff, "%s: decr dir=%d link count\n", __func__,
81cbf1ef 1579 rds.parent);
ca3f4e3a 1580 err = fuse2fs_read_inode(fs, rds.parent, &inode);
81cbf1ef
DW
1581 if (err) {
1582 ret = translate_error(fs, rds.parent, err);
1583 goto out;
1584 }
1585 if (inode.i_links_count > 1)
1586 inode.i_links_count--;
1587 ret = update_mtime(fs, rds.parent, &inode);
1588 if (ret)
1589 goto out;
ca3f4e3a 1590 err = fuse2fs_write_inode(fs, rds.parent, &inode);
81cbf1ef
DW
1591 if (err) {
1592 ret = translate_error(fs, rds.parent, err);
1593 goto out;
1594 }
1595 }
1596
1597out:
1598 return ret;
1599}
1600
1601static int op_rmdir(const char *path)
1602{
1603 struct fuse_context *ctxt = fuse_get_context();
1604 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1605 int ret;
1606
1607 FUSE2FS_CHECK_CONTEXT(ff);
1608 pthread_mutex_lock(&ff->bfl);
1609 ret = __op_rmdir(ff, path);
1610 pthread_mutex_unlock(&ff->bfl);
1611 return ret;
1612}
1613
1614static int op_symlink(const char *src, const char *dest)
1615{
1616 struct fuse_context *ctxt = fuse_get_context();
1617 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1618 ext2_filsys fs;
1619 ext2_ino_t parent, child;
da9d5b80 1620 char *temp_path;
81cbf1ef
DW
1621 errcode_t err;
1622 char *node_name, a;
1623 struct ext2_inode_large inode;
1624 int ret = 0;
1625
1626 FUSE2FS_CHECK_CONTEXT(ff);
1627 fs = ff->fs;
c4d34d7a 1628 dbg_printf(ff, "%s: symlink %s to %s\n", __func__, src, dest);
da9d5b80 1629 temp_path = strdup(dest);
81cbf1ef
DW
1630 if (!temp_path) {
1631 ret = -ENOMEM;
1632 goto out;
1633 }
1634 node_name = strrchr(temp_path, '/');
1635 if (!node_name) {
1636 ret = -ENOMEM;
1637 goto out;
1638 }
1639 node_name++;
1640 a = *node_name;
1641 *node_name = 0;
1642
1643 pthread_mutex_lock(&ff->bfl);
1644 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1645 &parent);
1646 *node_name = a;
1647 if (err) {
1648 ret = translate_error(fs, 0, err);
1649 goto out2;
1650 }
1651
9f69dfc4 1652 ret = check_inum_access(ff, parent, A_OK | W_OK);
81cbf1ef
DW
1653 if (ret)
1654 goto out2;
1655
1656
1657 /* Create symlink */
4e222d9b 1658 err = ext2fs_symlink(fs, parent, 0, node_name, src);
81cbf1ef
DW
1659 if (err == EXT2_ET_DIR_NO_SPACE) {
1660 err = ext2fs_expand_dir(fs, parent);
1661 if (err) {
1662 ret = translate_error(fs, parent, err);
1663 goto out2;
1664 }
1665
4e222d9b 1666 err = ext2fs_symlink(fs, parent, 0, node_name, src);
81cbf1ef
DW
1667 }
1668 if (err) {
1669 ret = translate_error(fs, parent, err);
1670 goto out2;
1671 }
1672
1673 /* Update parent dir's mtime */
1674 ret = update_mtime(fs, parent, NULL);
1675 if (ret)
1676 goto out2;
1677
1678 /* Still have to update the uid/gid of the symlink */
1679 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1680 &child);
1681 if (err) {
1682 ret = translate_error(fs, 0, err);
1683 goto out2;
1684 }
c4d34d7a 1685 dbg_printf(ff, "%s: symlinking ino=%d/name=%s to dir=%d\n", __func__,
81cbf1ef
DW
1686 child, node_name, parent);
1687
ca3f4e3a 1688 err = fuse2fs_read_inode(fs, child, &inode);
81cbf1ef
DW
1689 if (err) {
1690 ret = translate_error(fs, child, err);
1691 goto out2;
1692 }
1693
1694 inode.i_uid = ctxt->uid;
dae1ecc2 1695 ext2fs_set_i_uid_high(inode, ctxt->uid >> 16);
81cbf1ef 1696 inode.i_gid = ctxt->gid;
dae1ecc2 1697 ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
81cbf1ef 1698 inode.i_generation = ff->next_generation++;
f73fbf8e 1699 init_times(&inode);
81cbf1ef 1700
ca3f4e3a 1701 err = fuse2fs_write_inode(fs, child, &inode);
81cbf1ef
DW
1702 if (err) {
1703 ret = translate_error(fs, child, err);
1704 goto out2;
1705 }
1706out2:
1707 pthread_mutex_unlock(&ff->bfl);
1708out:
1709 free(temp_path);
1710 return ret;
1711}
1712
1713struct update_dotdot {
1714 ext2_ino_t new_dotdot;
1715};
1716
4e222d9b
TT
1717static int update_dotdot_helper(ext2_ino_t dir EXT2FS_ATTR((unused)),
1718 int entry EXT2FS_ATTR((unused)),
1719 struct ext2_dir_entry *dirent,
1720 int offset EXT2FS_ATTR((unused)),
1721 int blocksize EXT2FS_ATTR((unused)),
1722 char *buf EXT2FS_ATTR((unused)),
1723 void *priv_data)
81cbf1ef
DW
1724{
1725 struct update_dotdot *ud = priv_data;
1726
1727 if (ext2fs_dirent_name_len(dirent) == 2 &&
1728 dirent->name[0] == '.' && dirent->name[1] == '.') {
1729 dirent->inode = ud->new_dotdot;
1730 return DIRENT_CHANGED | DIRENT_ABORT;
1731 }
1732
1733 return 0;
1734}
1735
448a3f8b
DD
1736static int op_rename(const char *from, const char *to
1737#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
157b88c3 1738 , unsigned int flags EXT2FS_ATTR((unused))
448a3f8b
DD
1739#endif
1740 )
81cbf1ef
DW
1741{
1742 struct fuse_context *ctxt = fuse_get_context();
1743 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1744 ext2_filsys fs;
1745 errcode_t err;
1746 ext2_ino_t from_ino, to_ino, to_dir_ino, from_dir_ino;
1747 char *temp_to = NULL, *temp_from = NULL;
1748 char *cp, a;
1749 struct ext2_inode inode;
1750 struct update_dotdot ud;
1751 int ret = 0;
1752
b431abbc
DW
1753#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1754 /* renameat2 is not supported */
1755 if (flags)
1756 return -ENOSYS;
1757#endif
1758
81cbf1ef
DW
1759 FUSE2FS_CHECK_CONTEXT(ff);
1760 fs = ff->fs;
c4d34d7a 1761 dbg_printf(ff, "%s: renaming %s to %s\n", __func__, from, to);
81cbf1ef
DW
1762 pthread_mutex_lock(&ff->bfl);
1763 if (!fs_can_allocate(ff, 5)) {
1764 ret = -ENOSPC;
1765 goto out;
1766 }
1767
1768 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, from, &from_ino);
1769 if (err || from_ino == 0) {
1770 ret = translate_error(fs, 0, err);
1771 goto out;
1772 }
1773
1774 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, to, &to_ino);
1775 if (err && err != EXT2_ET_FILE_NOT_FOUND) {
1776 ret = translate_error(fs, 0, err);
1777 goto out;
1778 }
1779
1780 if (err == EXT2_ET_FILE_NOT_FOUND)
1781 to_ino = 0;
1782
1783 /* Already the same file? */
1784 if (to_ino != 0 && to_ino == from_ino) {
1785 ret = 0;
1786 goto out;
1787 }
1788
2f9b156c
DW
1789 ret = check_inum_access(ff, from_ino, W_OK);
1790 if (ret)
1791 goto out;
1792
1793 if (to_ino) {
1794 ret = check_inum_access(ff, to_ino, W_OK);
1795 if (ret)
1796 goto out;
1797 }
1798
81cbf1ef
DW
1799 temp_to = strdup(to);
1800 if (!temp_to) {
1801 ret = -ENOMEM;
1802 goto out;
1803 }
1804
1805 temp_from = strdup(from);
1806 if (!temp_from) {
1807 ret = -ENOMEM;
1808 goto out2;
1809 }
1810
1811 /* Find parent dir of the source and check write access */
1812 cp = strrchr(temp_from, '/');
1813 if (!cp) {
1814 ret = -EINVAL;
1815 goto out2;
1816 }
1817
1818 a = *(cp + 1);
1819 *(cp + 1) = 0;
1820 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_from,
1821 &from_dir_ino);
1822 *(cp + 1) = a;
1823 if (err) {
1824 ret = translate_error(fs, 0, err);
1825 goto out2;
1826 }
1827 if (from_dir_ino == 0) {
1828 ret = -ENOENT;
1829 goto out2;
1830 }
1831
65ddfa48 1832 ret = check_inum_access(ff, from_dir_ino, W_OK);
81cbf1ef
DW
1833 if (ret)
1834 goto out2;
1835
1836 /* Find parent dir of the destination and check write access */
1837 cp = strrchr(temp_to, '/');
1838 if (!cp) {
1839 ret = -EINVAL;
1840 goto out2;
1841 }
1842
1843 a = *(cp + 1);
1844 *(cp + 1) = 0;
1845 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_to,
1846 &to_dir_ino);
1847 *(cp + 1) = a;
1848 if (err) {
1849 ret = translate_error(fs, 0, err);
1850 goto out2;
1851 }
1852 if (to_dir_ino == 0) {
1853 ret = -ENOENT;
1854 goto out2;
1855 }
1856
65ddfa48 1857 ret = check_inum_access(ff, to_dir_ino, W_OK);
81cbf1ef
DW
1858 if (ret)
1859 goto out2;
1860
1861 /* If the target exists, unlink it first */
1862 if (to_ino != 0) {
1863 err = ext2fs_read_inode(fs, to_ino, &inode);
1864 if (err) {
1865 ret = translate_error(fs, to_ino, err);
1866 goto out2;
1867 }
1868
c4d34d7a 1869 dbg_printf(ff, "%s: unlinking %s ino=%d\n", __func__,
81cbf1ef
DW
1870 LINUX_S_ISDIR(inode.i_mode) ? "dir" : "file",
1871 to_ino);
1872 if (LINUX_S_ISDIR(inode.i_mode))
1873 ret = __op_rmdir(ff, to);
1874 else
1875 ret = __op_unlink(ff, to);
1876 if (ret)
1877 goto out2;
1878 }
1879
1880 /* Get ready to do the move */
1881 err = ext2fs_read_inode(fs, from_ino, &inode);
1882 if (err) {
1883 ret = translate_error(fs, from_ino, err);
1884 goto out2;
1885 }
1886
1887 /* Link in the new file */
c4d34d7a 1888 dbg_printf(ff, "%s: linking ino=%d/path=%s to dir=%d\n", __func__,
81cbf1ef
DW
1889 from_ino, cp + 1, to_dir_ino);
1890 err = ext2fs_link(fs, to_dir_ino, cp + 1, from_ino,
0a5dc78b 1891 ext2_file_type(inode.i_mode) | EXT2FS_LINK_EXPAND);
81cbf1ef
DW
1892 if (err) {
1893 ret = translate_error(fs, to_dir_ino, err);
1894 goto out2;
1895 }
1896
1897 /* Update '..' pointer if dir */
1898 err = ext2fs_read_inode(fs, from_ino, &inode);
1899 if (err) {
1900 ret = translate_error(fs, from_ino, err);
1901 goto out2;
1902 }
1903
1904 if (LINUX_S_ISDIR(inode.i_mode)) {
1905 ud.new_dotdot = to_dir_ino;
c4d34d7a 1906 dbg_printf(ff, "%s: updating .. entry for dir=%d\n", __func__,
81cbf1ef
DW
1907 to_dir_ino);
1908 err = ext2fs_dir_iterate2(fs, from_ino, 0, NULL,
1909 update_dotdot_helper, &ud);
1910 if (err) {
1911 ret = translate_error(fs, from_ino, err);
1912 goto out2;
1913 }
1914
1915 /* Decrease from_dir_ino's links_count */
c4d34d7a 1916 dbg_printf(ff, "%s: moving linkcount from dir=%d to dir=%d\n",
81cbf1ef
DW
1917 __func__, from_dir_ino, to_dir_ino);
1918 err = ext2fs_read_inode(fs, from_dir_ino, &inode);
1919 if (err) {
1920 ret = translate_error(fs, from_dir_ino, err);
1921 goto out2;
1922 }
1923 inode.i_links_count--;
1924 err = ext2fs_write_inode(fs, from_dir_ino, &inode);
1925 if (err) {
1926 ret = translate_error(fs, from_dir_ino, err);
1927 goto out2;
1928 }
1929
1930 /* Increase to_dir_ino's links_count */
1931 err = ext2fs_read_inode(fs, to_dir_ino, &inode);
1932 if (err) {
1933 ret = translate_error(fs, to_dir_ino, err);
1934 goto out2;
1935 }
1936 inode.i_links_count++;
1937 err = ext2fs_write_inode(fs, to_dir_ino, &inode);
1938 if (err) {
1939 ret = translate_error(fs, to_dir_ino, err);
1940 goto out2;
1941 }
1942 }
1943
1944 /* Update timestamps */
1945 ret = update_ctime(fs, from_ino, NULL);
1946 if (ret)
1947 goto out2;
1948
1949 ret = update_mtime(fs, to_dir_ino, NULL);
1950 if (ret)
1951 goto out2;
1952
1953 /* Remove the old file */
c4d34d7a 1954 ret = unlink_file_by_name(ff, from);
81cbf1ef
DW
1955 if (ret)
1956 goto out2;
1957
1958 /* Flush the whole mess out */
1959 err = ext2fs_flush2(fs, 0);
1960 if (err)
1961 ret = translate_error(fs, 0, err);
1962
1963out2:
1964 free(temp_from);
1965 free(temp_to);
1966out:
1967 pthread_mutex_unlock(&ff->bfl);
1968 return ret;
1969}
1970
1971static int op_link(const char *src, const char *dest)
1972{
1973 struct fuse_context *ctxt = fuse_get_context();
1974 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1975 ext2_filsys fs;
da9d5b80 1976 char *temp_path;
81cbf1ef
DW
1977 errcode_t err;
1978 char *node_name, a;
1979 ext2_ino_t parent, ino;
1980 struct ext2_inode_large inode;
1981 int ret = 0;
1982
1983 FUSE2FS_CHECK_CONTEXT(ff);
1984 fs = ff->fs;
c4d34d7a 1985 dbg_printf(ff, "%s: src=%s dest=%s\n", __func__, src, dest);
da9d5b80 1986 temp_path = strdup(dest);
81cbf1ef
DW
1987 if (!temp_path) {
1988 ret = -ENOMEM;
1989 goto out;
1990 }
1991 node_name = strrchr(temp_path, '/');
1992 if (!node_name) {
1993 ret = -ENOMEM;
1994 goto out;
1995 }
1996 node_name++;
1997 a = *node_name;
1998 *node_name = 0;
1999
2000 pthread_mutex_lock(&ff->bfl);
2001 if (!fs_can_allocate(ff, 2)) {
2002 ret = -ENOSPC;
2003 goto out2;
2004 }
2005
2006 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
2007 &parent);
2008 *node_name = a;
2009 if (err) {
2010 err = -ENOENT;
2011 goto out2;
2012 }
2013
9f69dfc4 2014 ret = check_inum_access(ff, parent, A_OK | W_OK);
81cbf1ef
DW
2015 if (ret)
2016 goto out2;
2017
81cbf1ef
DW
2018 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, src, &ino);
2019 if (err || ino == 0) {
2020 ret = translate_error(fs, 0, err);
2021 goto out2;
2022 }
2023
ca3f4e3a 2024 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
2025 if (err) {
2026 ret = translate_error(fs, ino, err);
2027 goto out2;
2028 }
2029
2f9b156c
DW
2030 ret = check_iflags_access(ff, ino, EXT2_INODE(&inode), W_OK);
2031 if (ret)
2032 goto out2;
2033
81cbf1ef
DW
2034 inode.i_links_count++;
2035 ret = update_ctime(fs, ino, &inode);
2036 if (ret)
2037 goto out2;
2038
ca3f4e3a 2039 err = fuse2fs_write_inode(fs, ino, &inode);
81cbf1ef
DW
2040 if (err) {
2041 ret = translate_error(fs, ino, err);
2042 goto out2;
2043 }
2044
c4d34d7a 2045 dbg_printf(ff, "%s: linking ino=%d/name=%s to dir=%d\n", __func__, ino,
81cbf1ef
DW
2046 node_name, parent);
2047 err = ext2fs_link(fs, parent, node_name, ino,
0a5dc78b 2048 ext2_file_type(inode.i_mode) | EXT2FS_LINK_EXPAND);
81cbf1ef
DW
2049 if (err) {
2050 ret = translate_error(fs, parent, err);
2051 goto out2;
2052 }
2053
2054 ret = update_mtime(fs, parent, NULL);
2055 if (ret)
2056 goto out2;
2057
2058out2:
2059 pthread_mutex_unlock(&ff->bfl);
2060out:
2061 free(temp_path);
2062 return ret;
2063}
2064
3469e6ff
DW
2065#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
2066/* Obtain group ids of the process that sent us a command(?) */
2067static int get_req_groups(struct fuse2fs *ff, gid_t **gids, size_t *nr_gids)
2068{
2069 ext2_filsys fs = ff->fs;
2070 errcode_t err;
2071 gid_t *array;
2072 int nr = 32; /* nobody has more than 32 groups right? */
2073 int ret;
2074
2075 do {
2076 err = ext2fs_get_array(nr, sizeof(gid_t), &array);
2077 if (err)
2078 return translate_error(fs, 0, err);
2079
2080 ret = fuse_getgroups(nr, array);
2081 if (ret < 0)
2082 return ret;
2083
2084 if (ret <= nr) {
2085 *gids = array;
2086 *nr_gids = ret;
2087 return 0;
2088 }
2089
2090 ext2fs_free_mem(&array);
2091 nr = ret;
2092 } while (0);
2093
2094 /* shut up gcc */
2095 return -ENOMEM;
2096}
2097
2098/*
2099 * Is this file's group id in the set of groups associated with the process
2100 * that initiated the fuse request? Returns 1 for yes, 0 for no, or a negative
2101 * errno.
2102 */
2103static int in_file_group(struct fuse_context *ctxt,
2104 const struct ext2_inode_large *inode)
2105{
2106 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2107 gid_t *gids = NULL;
2108 size_t i, nr_gids = 0;
2109 gid_t gid = inode_gid(*inode);
2110 int ret;
2111
2112 ret = get_req_groups(ff, &gids, &nr_gids);
2113 if (ret < 0)
2114 return ret;
2115
2116 for (i = 0; i < nr_gids; i++)
2117 if (gids[i] == gid)
2118 return 1;
2119 return 0;
2120}
2121#else
2122static int in_file_group(struct fuse_context *ctxt,
2123 const struct ext2_inode_large *inode)
2124{
2125 return ctxt->gid == inode_gid(*inode);
2126}
2127#endif
2128
448a3f8b
DD
2129static int op_chmod(const char *path, mode_t mode
2130#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
157b88c3 2131 , struct fuse_file_info *fi EXT2FS_ATTR((unused))
448a3f8b
DD
2132#endif
2133 )
81cbf1ef
DW
2134{
2135 struct fuse_context *ctxt = fuse_get_context();
2136 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2137 ext2_filsys fs;
2138 errcode_t err;
2139 ext2_ino_t ino;
2140 struct ext2_inode_large inode;
2141 int ret = 0;
2142
2143 FUSE2FS_CHECK_CONTEXT(ff);
2144 fs = ff->fs;
2145 pthread_mutex_lock(&ff->bfl);
2146 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2147 if (err) {
2148 ret = translate_error(fs, 0, err);
2149 goto out;
2150 }
c4d34d7a 2151 dbg_printf(ff, "%s: path=%s mode=0%o ino=%d\n", __func__, path, mode, ino);
81cbf1ef 2152
ca3f4e3a 2153 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
2154 if (err) {
2155 ret = translate_error(fs, ino, err);
2156 goto out;
2157 }
2158
2f9b156c
DW
2159 ret = check_iflags_access(ff, ino, EXT2_INODE(&inode), W_OK);
2160 if (ret)
2161 goto out;
2162
bc76e0f7 2163 if (want_check_owner(ff, ctxt) && ctxt->uid != inode_uid(inode)) {
81cbf1ef
DW
2164 ret = -EPERM;
2165 goto out;
2166 }
2167
2168 /*
2169 * XXX: We should really check that the inode gid is not in /any/
2170 * of the user's groups, but FUSE only tells us about the primary
2171 * group.
2172 */
3469e6ff
DW
2173 if (!is_superuser(ff, ctxt)) {
2174 ret = in_file_group(ctxt, &inode);
2175 if (ret < 0)
2176 goto out;
2177
2178 if (!ret)
2179 mode &= ~S_ISGID;
2180 }
81cbf1ef
DW
2181
2182 inode.i_mode &= ~0xFFF;
2183 inode.i_mode |= mode & 0xFFF;
3469e6ff
DW
2184
2185 dbg_printf(ff, "%s: path=%s new_mode=0%o ino=%d\n", __func__,
2186 path, inode.i_mode, ino);
2187
81cbf1ef
DW
2188 ret = update_ctime(fs, ino, &inode);
2189 if (ret)
2190 goto out;
2191
ca3f4e3a 2192 err = fuse2fs_write_inode(fs, ino, &inode);
81cbf1ef
DW
2193 if (err) {
2194 ret = translate_error(fs, ino, err);
2195 goto out;
2196 }
2197
2198out:
2199 pthread_mutex_unlock(&ff->bfl);
2200 return ret;
2201}
2202
448a3f8b
DD
2203static int op_chown(const char *path, uid_t owner, gid_t group
2204#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
157b88c3 2205 , struct fuse_file_info *fi EXT2FS_ATTR((unused))
448a3f8b
DD
2206#endif
2207 )
81cbf1ef
DW
2208{
2209 struct fuse_context *ctxt = fuse_get_context();
2210 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2211 ext2_filsys fs;
2212 errcode_t err;
2213 ext2_ino_t ino;
2214 struct ext2_inode_large inode;
2215 int ret = 0;
2216
2217 FUSE2FS_CHECK_CONTEXT(ff);
2218 fs = ff->fs;
2219 pthread_mutex_lock(&ff->bfl);
2220 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2221 if (err) {
2222 ret = translate_error(fs, 0, err);
2223 goto out;
2224 }
c4d34d7a 2225 dbg_printf(ff, "%s: path=%s owner=%d group=%d ino=%d\n", __func__,
81cbf1ef
DW
2226 path, owner, group, ino);
2227
ca3f4e3a 2228 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
2229 if (err) {
2230 ret = translate_error(fs, ino, err);
2231 goto out;
2232 }
2233
2f9b156c
DW
2234 ret = check_iflags_access(ff, ino, EXT2_INODE(&inode), W_OK);
2235 if (ret)
2236 goto out;
2237
81cbf1ef 2238 /* FUSE seems to feed us ~0 to mean "don't change" */
4e222d9b 2239 if (owner != (uid_t) ~0) {
81cbf1ef 2240 /* Only root gets to change UID. */
bc76e0f7 2241 if (want_check_owner(ff, ctxt) &&
dae1ecc2 2242 !(inode_uid(inode) == ctxt->uid && owner == ctxt->uid)) {
81cbf1ef
DW
2243 ret = -EPERM;
2244 goto out;
2245 }
2246 inode.i_uid = owner;
dae1ecc2 2247 ext2fs_set_i_uid_high(inode, owner >> 16);
81cbf1ef
DW
2248 }
2249
4e222d9b 2250 if (group != (gid_t) ~0) {
81cbf1ef 2251 /* Only root or the owner get to change GID. */
bc76e0f7 2252 if (want_check_owner(ff, ctxt) &&
dae1ecc2 2253 inode_uid(inode) != ctxt->uid) {
81cbf1ef
DW
2254 ret = -EPERM;
2255 goto out;
2256 }
2257
2258 /* XXX: We /should/ check group membership but FUSE */
2259 inode.i_gid = group;
dae1ecc2 2260 ext2fs_set_i_gid_high(inode, group >> 16);
81cbf1ef
DW
2261 }
2262
2263 ret = update_ctime(fs, ino, &inode);
2264 if (ret)
2265 goto out;
2266
ca3f4e3a 2267 err = fuse2fs_write_inode(fs, ino, &inode);
81cbf1ef
DW
2268 if (err) {
2269 ret = translate_error(fs, ino, err);
2270 goto out;
2271 }
2272
2273out:
2274 pthread_mutex_unlock(&ff->bfl);
2275 return ret;
2276}
2277
4581ac60 2278static int punch_posteof(struct fuse2fs *ff, ext2_ino_t ino, off_t new_size)
b022aca2 2279{
4581ac60
DW
2280 ext2_filsys fs = ff->fs;
2281 struct ext2_inode_large inode;
241dae1b 2282 blk64_t truncate_block = FUSE2FS_B_TO_FSB(ff, new_size);
4581ac60
DW
2283 errcode_t err;
2284
2285 err = fuse2fs_read_inode(fs, ino, &inode);
2286 if (err)
2287 return translate_error(fs, ino, err);
2288
2289 err = ext2fs_punch(fs, ino, EXT2_INODE(&inode), 0, truncate_block,
2290 ~0ULL);
2291 if (err)
2292 return translate_error(fs, ino, err);
2293
2294 return 0;
2295}
2296
2297static int truncate_helper(struct fuse2fs *ff, ext2_ino_t ino, off_t new_size)
2298{
2299 ext2_filsys fs = ff->fs;
b022aca2 2300 ext2_file_t file;
4581ac60 2301 __u64 old_isize;
b022aca2
DW
2302 errcode_t err;
2303 int ret = 0;
2304
2305 err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &file);
2306 if (err)
2307 return translate_error(fs, ino, err);
2308
4581ac60
DW
2309 err = ext2fs_file_get_lsize(file, &old_isize);
2310 if (err) {
2311 ret = translate_error(fs, ino, err);
2312 goto out_close;
2313 }
2314
2315 dbg_printf(ff, "%s: ino=%u isize=0x%llx new_size=0x%llx\n", __func__,
2316 ino,
2317 (unsigned long long)old_isize,
2318 (unsigned long long)new_size);
2319
b022aca2
DW
2320 err = ext2fs_file_set_size2(file, new_size);
2321 if (err)
2322 ret = translate_error(fs, ino, err);
2323
4581ac60 2324out_close:
b022aca2
DW
2325 err = ext2fs_file_close(file);
2326 if (ret)
2327 return ret;
2328 if (err)
2329 return translate_error(fs, ino, err);
2330
4581ac60
DW
2331 ret = update_mtime(fs, ino, NULL);
2332 if (ret)
2333 return ret;
2334
2335 /*
2336 * Truncating to the current size is usually understood to mean that
2337 * we should clear out post-EOF preallocations.
2338 */
2339 if (new_size == old_isize)
2340 return punch_posteof(ff, ino, new_size);
2341
2342 return 0;
b022aca2
DW
2343}
2344
448a3f8b
DD
2345static int op_truncate(const char *path, off_t len
2346#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
157b88c3 2347 , struct fuse_file_info *fi EXT2FS_ATTR((unused))
448a3f8b
DD
2348#endif
2349 )
81cbf1ef
DW
2350{
2351 struct fuse_context *ctxt = fuse_get_context();
2352 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2353 ext2_filsys fs;
81cbf1ef 2354 ext2_ino_t ino;
b022aca2 2355 errcode_t err;
81cbf1ef
DW
2356 int ret = 0;
2357
2358 FUSE2FS_CHECK_CONTEXT(ff);
2359 fs = ff->fs;
2360 pthread_mutex_lock(&ff->bfl);
2361 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
18c01ba6 2362 if (err) {
81cbf1ef
DW
2363 ret = translate_error(fs, 0, err);
2364 goto out;
2365 }
18c01ba6
DW
2366 if (!ino) {
2367 ret = -ESTALE;
2368 goto out;
2369 }
7dbac9cd 2370 dbg_printf(ff, "%s: ino=%d len=%jd\n", __func__, ino, (intmax_t) len);
81cbf1ef 2371
65ddfa48 2372 ret = check_inum_access(ff, ino, W_OK);
81cbf1ef
DW
2373 if (ret)
2374 goto out;
2375
4581ac60 2376 ret = truncate_helper(ff, ino, len);
81cbf1ef
DW
2377 if (ret)
2378 goto out;
81cbf1ef
DW
2379
2380out:
2381 pthread_mutex_unlock(&ff->bfl);
2382 return err;
2383}
2384
2385#ifdef __linux__
2386static void detect_linux_executable_open(int kernel_flags, int *access_check,
2387 int *e2fs_open_flags)
2388{
2389 /*
2390 * On Linux, execve will bleed __FMODE_EXEC into the file mode flags,
2391 * and FUSE is more than happy to let that slip through.
2392 */
2393 if (kernel_flags & 0x20) {
2394 *access_check = X_OK;
2395 *e2fs_open_flags &= ~EXT2_FILE_WRITE;
2396 }
2397}
2398#else
2399static void detect_linux_executable_open(int kernel_flags, int *access_check,
2400 int *e2fs_open_flags)
2401{
2402 /* empty */
2403}
2404#endif /* __linux__ */
2405
2406static int __op_open(struct fuse2fs *ff, const char *path,
2407 struct fuse_file_info *fp)
2408{
2409 ext2_filsys fs = ff->fs;
2410 errcode_t err;
2411 struct fuse2fs_file_handle *file;
2412 int check = 0, ret = 0;
2413
b022aca2 2414 dbg_printf(ff, "%s: path=%s oflags=0o%o\n", __func__, path, fp->flags);
81cbf1ef
DW
2415 err = ext2fs_get_mem(sizeof(*file), &file);
2416 if (err)
2417 return translate_error(fs, 0, err);
2418 file->magic = FUSE2FS_FILE_MAGIC;
2419
2420 file->open_flags = 0;
2421 switch (fp->flags & O_ACCMODE) {
2422 case O_RDONLY:
2423 check = R_OK;
2424 break;
2425 case O_WRONLY:
2426 check = W_OK;
2427 file->open_flags |= EXT2_FILE_WRITE;
2428 break;
2429 case O_RDWR:
2430 check = R_OK | W_OK;
2431 file->open_flags |= EXT2_FILE_WRITE;
2432 break;
2433 }
9f69dfc4
DW
2434 if (fp->flags & O_APPEND) {
2435 /* the kernel doesn't allow truncation of an append-only file */
2436 if (fp->flags & O_TRUNC) {
2437 ret = -EPERM;
2438 goto out;
2439 }
2440
2441 check |= A_OK;
2442 }
81cbf1ef
DW
2443
2444 detect_linux_executable_open(fp->flags, &check, &file->open_flags);
2445
2446 if (fp->flags & O_CREAT)
2447 file->open_flags |= EXT2_FILE_CREATE;
2448
2449 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &file->ino);
2450 if (err || file->ino == 0) {
2451 ret = translate_error(fs, 0, err);
2452 goto out;
2453 }
c4d34d7a 2454 dbg_printf(ff, "%s: ino=%d\n", __func__, file->ino);
81cbf1ef 2455
65ddfa48 2456 ret = check_inum_access(ff, file->ino, check);
81cbf1ef
DW
2457 if (ret) {
2458 /*
2459 * In a regular (Linux) fs driver, the kernel will open
2460 * binaries for reading if the user has --x privileges (i.e.
2461 * execute without read). Since the kernel doesn't have any
2462 * way to tell us if it's opening a file via execve, we'll
2463 * just assume that allowing access is ok if asking for ro mode
2464 * fails but asking for x mode succeeds. Of course we can
2465 * also employ undocumented hacks (see above).
2466 */
2467 if (check == R_OK) {
65ddfa48 2468 ret = check_inum_access(ff, file->ino, X_OK);
81cbf1ef
DW
2469 if (ret)
2470 goto out;
2471 } else
2472 goto out;
2473 }
b022aca2
DW
2474
2475 if (fp->flags & O_TRUNC) {
4581ac60 2476 ret = truncate_helper(ff, file->ino, 0);
b022aca2
DW
2477 if (ret)
2478 goto out;
2479 }
2480
377e3a96 2481 fp->fh = (uintptr_t)file;
81cbf1ef
DW
2482
2483out:
2484 if (ret)
2485 ext2fs_free_mem(&file);
2486 return ret;
2487}
2488
2489static int op_open(const char *path, struct fuse_file_info *fp)
2490{
2491 struct fuse_context *ctxt = fuse_get_context();
2492 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2493 int ret;
2494
2495 FUSE2FS_CHECK_CONTEXT(ff);
2496 pthread_mutex_lock(&ff->bfl);
2497 ret = __op_open(ff, path, fp);
2498 pthread_mutex_unlock(&ff->bfl);
2499 return ret;
2500}
2501
4e222d9b
TT
2502static int op_read(const char *path EXT2FS_ATTR((unused)), char *buf,
2503 size_t len, off_t offset,
2504 struct fuse_file_info *fp)
81cbf1ef
DW
2505{
2506 struct fuse_context *ctxt = fuse_get_context();
2507 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2508 struct fuse2fs_file_handle *fh =
2509 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2510 ext2_filsys fs;
2511 ext2_file_t efp;
2512 errcode_t err;
2513 unsigned int got = 0;
2514 int ret = 0;
2515
2516 FUSE2FS_CHECK_CONTEXT(ff);
2517 fs = ff->fs;
2518 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
7dbac9cd
TT
2519 dbg_printf(ff, "%s: ino=%d off=%jd len=%jd\n", __func__, fh->ino,
2520 (intmax_t) offset, len);
81cbf1ef
DW
2521 pthread_mutex_lock(&ff->bfl);
2522 err = ext2fs_file_open(fs, fh->ino, fh->open_flags, &efp);
2523 if (err) {
2524 ret = translate_error(fs, fh->ino, err);
2525 goto out;
2526 }
2527
2528 err = ext2fs_file_llseek(efp, offset, SEEK_SET, NULL);
2529 if (err) {
2530 ret = translate_error(fs, fh->ino, err);
2531 goto out2;
2532 }
2533
2534 err = ext2fs_file_read(efp, buf, len, &got);
2535 if (err) {
2536 ret = translate_error(fs, fh->ino, err);
2537 goto out2;
2538 }
2539
2540out2:
2541 err = ext2fs_file_close(efp);
2542 if (ret)
2543 goto out;
2544 if (err) {
2545 ret = translate_error(fs, fh->ino, err);
2546 goto out;
2547 }
2548
2549 if (fs_writeable(fs)) {
2550 ret = update_atime(fs, fh->ino);
2551 if (ret)
2552 goto out;
2553 }
2554out:
2555 pthread_mutex_unlock(&ff->bfl);
4e222d9b 2556 return got ? (int) got : ret;
81cbf1ef
DW
2557}
2558
4e222d9b
TT
2559static int op_write(const char *path EXT2FS_ATTR((unused)),
2560 const char *buf, size_t len, off_t offset,
2561 struct fuse_file_info *fp)
81cbf1ef
DW
2562{
2563 struct fuse_context *ctxt = fuse_get_context();
2564 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2565 struct fuse2fs_file_handle *fh =
2566 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2567 ext2_filsys fs;
2568 ext2_file_t efp;
2569 errcode_t err;
2570 unsigned int got = 0;
2571 int ret = 0;
2572
2573 FUSE2FS_CHECK_CONTEXT(ff);
2574 fs = ff->fs;
2575 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
7dbac9cd
TT
2576 dbg_printf(ff, "%s: ino=%d off=%jd len=%jd\n", __func__, fh->ino,
2577 (intmax_t) offset, (intmax_t) len);
81cbf1ef
DW
2578 pthread_mutex_lock(&ff->bfl);
2579 if (!fs_writeable(fs)) {
2580 ret = -EROFS;
2581 goto out;
2582 }
2583
241dae1b 2584 if (!fs_can_allocate(ff, FUSE2FS_B_TO_FSB(ff, len))) {
81cbf1ef
DW
2585 ret = -ENOSPC;
2586 goto out;
2587 }
2588
2589 err = ext2fs_file_open(fs, fh->ino, fh->open_flags, &efp);
2590 if (err) {
2591 ret = translate_error(fs, fh->ino, err);
2592 goto out;
2593 }
2594
2595 err = ext2fs_file_llseek(efp, offset, SEEK_SET, NULL);
2596 if (err) {
2597 ret = translate_error(fs, fh->ino, err);
2598 goto out2;
2599 }
2600
2601 err = ext2fs_file_write(efp, buf, len, &got);
2602 if (err) {
2603 ret = translate_error(fs, fh->ino, err);
2604 goto out2;
2605 }
2606
2607 err = ext2fs_file_flush(efp);
2608 if (err) {
2609 got = 0;
2610 ret = translate_error(fs, fh->ino, err);
2611 goto out2;
2612 }
2613
2614out2:
2615 err = ext2fs_file_close(efp);
2616 if (ret)
2617 goto out;
2618 if (err) {
2619 ret = translate_error(fs, fh->ino, err);
2620 goto out;
2621 }
2622
2623 ret = update_mtime(fs, fh->ino, NULL);
2624 if (ret)
2625 goto out;
2626
2627out:
2628 pthread_mutex_unlock(&ff->bfl);
4e222d9b 2629 return got ? (int) got : ret;
81cbf1ef
DW
2630}
2631
4e222d9b
TT
2632static int op_release(const char *path EXT2FS_ATTR((unused)),
2633 struct fuse_file_info *fp)
81cbf1ef
DW
2634{
2635 struct fuse_context *ctxt = fuse_get_context();
2636 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2637 struct fuse2fs_file_handle *fh =
2638 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2639 ext2_filsys fs;
2640 errcode_t err;
2641 int ret = 0;
2642
2643 FUSE2FS_CHECK_CONTEXT(ff);
2644 fs = ff->fs;
2645 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
c4d34d7a 2646 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
81cbf1ef
DW
2647 pthread_mutex_lock(&ff->bfl);
2648 if (fs_writeable(fs) && fh->open_flags & EXT2_FILE_WRITE) {
2649 err = ext2fs_flush2(fs, EXT2_FLAG_FLUSH_NO_SYNC);
2650 if (err)
2651 ret = translate_error(fs, fh->ino, err);
2652 }
2653 fp->fh = 0;
2654 pthread_mutex_unlock(&ff->bfl);
2655
2656 ext2fs_free_mem(&fh);
2657
2658 return ret;
2659}
2660
4e222d9b
TT
2661static int op_fsync(const char *path EXT2FS_ATTR((unused)),
2662 int datasync EXT2FS_ATTR((unused)),
2663 struct fuse_file_info *fp)
81cbf1ef
DW
2664{
2665 struct fuse_context *ctxt = fuse_get_context();
2666 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2667 struct fuse2fs_file_handle *fh =
2668 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2669 ext2_filsys fs;
2670 errcode_t err;
2671 int ret = 0;
2672
2673 FUSE2FS_CHECK_CONTEXT(ff);
2674 fs = ff->fs;
2675 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
c4d34d7a 2676 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
81cbf1ef
DW
2677 /* For now, flush everything, even if it's slow */
2678 pthread_mutex_lock(&ff->bfl);
2679 if (fs_writeable(fs) && fh->open_flags & EXT2_FILE_WRITE) {
2680 err = ext2fs_flush2(fs, 0);
2681 if (err)
2682 ret = translate_error(fs, fh->ino, err);
2683 }
2684 pthread_mutex_unlock(&ff->bfl);
2685
2686 return ret;
2687}
2688
4e222d9b
TT
2689static int op_statfs(const char *path EXT2FS_ATTR((unused)),
2690 struct statvfs *buf)
81cbf1ef
DW
2691{
2692 struct fuse_context *ctxt = fuse_get_context();
2693 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2694 ext2_filsys fs;
2695 uint64_t fsid, *f;
2696 blk64_t overhead, reserved, free;
2697
2698 FUSE2FS_CHECK_CONTEXT(ff);
2699 fs = ff->fs;
c4d34d7a 2700 dbg_printf(ff, "%s: path=%s\n", __func__, path);
81cbf1ef
DW
2701 buf->f_bsize = fs->blocksize;
2702 buf->f_frsize = 0;
2703
2704 if (ff->minixdf)
2705 overhead = 0;
2706 else
2707 overhead = fs->desc_blocks +
102993ec 2708 (blk64_t)fs->group_desc_count *
81cbf1ef
DW
2709 (fs->inode_blocks_per_group + 2);
2710 reserved = ext2fs_r_blocks_count(fs->super);
2711 if (!reserved)
2712 reserved = ext2fs_blocks_count(fs->super) / 10;
2713 free = ext2fs_free_blocks_count(fs->super);
2714
2715 buf->f_blocks = ext2fs_blocks_count(fs->super) - overhead;
2716 buf->f_bfree = free;
2717 if (free < reserved)
2718 buf->f_bavail = 0;
2719 else
2720 buf->f_bavail = free - reserved;
2721 buf->f_files = fs->super->s_inodes_count;
2722 buf->f_ffree = fs->super->s_free_inodes_count;
2723 buf->f_favail = fs->super->s_free_inodes_count;
2724 f = (uint64_t *)fs->super->s_uuid;
2725 fsid = *f;
2726 f++;
2727 fsid ^= *f;
2728 buf->f_fsid = fsid;
2729 buf->f_flag = 0;
2730 if (fs->flags & EXT2_FLAG_RW)
2731 buf->f_flag |= ST_RDONLY;
2732 buf->f_namemax = EXT2_NAME_LEN;
2733
2734 return 0;
2735}
2736
010a73cd
DW
2737static const char *valid_xattr_prefixes[] = {
2738 "user.",
2739 "trusted.",
2740 "security.",
2741 "gnu.",
2742 "system.",
2743};
2744
2745static int validate_xattr_name(const char *name)
2746{
2747 int i;
2748
2749 for (i = 0; i < ARRAY_SIZE(valid_xattr_prefixes); i++) {
2750 if (!strncmp(name, valid_xattr_prefixes[i],
2751 strlen(valid_xattr_prefixes[i])))
2752 return 1;
2753 }
2754
2755 return 0;
2756}
2757
81cbf1ef
DW
2758static int op_getxattr(const char *path, const char *key, char *value,
2759 size_t len)
2760{
2761 struct fuse_context *ctxt = fuse_get_context();
2762 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2763 ext2_filsys fs;
0111bdb7
DW
2764 void *ptr;
2765 size_t plen;
81cbf1ef
DW
2766 ext2_ino_t ino;
2767 errcode_t err;
2768 int ret = 0;
2769
010a73cd
DW
2770 if (!validate_xattr_name(key))
2771 return -ENODATA;
2772
81cbf1ef
DW
2773 FUSE2FS_CHECK_CONTEXT(ff);
2774 fs = ff->fs;
2775 pthread_mutex_lock(&ff->bfl);
7889640d 2776 if (!ext2fs_has_feature_xattr(fs->super)) {
81cbf1ef
DW
2777 ret = -ENOTSUP;
2778 goto out;
2779 }
2780
2781 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2782 if (err || ino == 0) {
2783 ret = translate_error(fs, 0, err);
2784 goto out;
2785 }
1686713e 2786 dbg_printf(ff, "%s: ino=%d name=%s\n", __func__, ino, key);
81cbf1ef 2787
65ddfa48 2788 ret = check_inum_access(ff, ino, R_OK);
81cbf1ef
DW
2789 if (ret)
2790 goto out;
2791
2c790038
DW
2792 ret = __getxattr(ff, ino, key, &ptr, &plen);
2793 if (ret)
81cbf1ef 2794 goto out;
81cbf1ef 2795
81cbf1ef
DW
2796 if (!len) {
2797 ret = plen;
2798 } else if (len < plen) {
2799 ret = -ERANGE;
2800 } else {
2801 memcpy(value, ptr, plen);
2802 ret = plen;
2803 }
2804
81cbf1ef 2805 ext2fs_free_mem(&ptr);
81cbf1ef
DW
2806out:
2807 pthread_mutex_unlock(&ff->bfl);
2808
2809 return ret;
2810}
2811
4e222d9b
TT
2812static int count_buffer_space(char *name, char *value EXT2FS_ATTR((unused)),
2813 size_t value_len EXT2FS_ATTR((unused)),
81cbf1ef
DW
2814 void *data)
2815{
2816 unsigned int *x = data;
2817
2818 *x = *x + strlen(name) + 1;
2819 return 0;
2820}
2821
4e222d9b
TT
2822static int copy_names(char *name, char *value EXT2FS_ATTR((unused)),
2823 size_t value_len EXT2FS_ATTR((unused)), void *data)
81cbf1ef
DW
2824{
2825 char **b = data;
44ee1fb8 2826 size_t name_len = strlen(name);
81cbf1ef 2827
44ee1fb8
EB
2828 memcpy(*b, name, name_len + 1);
2829 *b = *b + name_len + 1;
81cbf1ef
DW
2830
2831 return 0;
2832}
2833
2834static int op_listxattr(const char *path, char *names, size_t len)
2835{
2836 struct fuse_context *ctxt = fuse_get_context();
2837 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2838 ext2_filsys fs;
2839 struct ext2_xattr_handle *h;
2840 unsigned int bufsz;
2841 ext2_ino_t ino;
2842 errcode_t err;
2843 int ret = 0;
2844
2845 FUSE2FS_CHECK_CONTEXT(ff);
2846 fs = ff->fs;
2847 pthread_mutex_lock(&ff->bfl);
7889640d 2848 if (!ext2fs_has_feature_xattr(fs->super)) {
81cbf1ef
DW
2849 ret = -ENOTSUP;
2850 goto out;
2851 }
2852
2853 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2854 if (err || ino == 0) {
2855 ret = translate_error(fs, ino, err);
2856 goto out;
2857 }
c4d34d7a 2858 dbg_printf(ff, "%s: ino=%d\n", __func__, ino);
81cbf1ef 2859
65ddfa48 2860 ret = check_inum_access(ff, ino, R_OK);
81cbf1ef 2861 if (ret)
da9d5b80 2862 goto out;
81cbf1ef
DW
2863
2864 err = ext2fs_xattrs_open(fs, ino, &h);
2865 if (err) {
2866 ret = translate_error(fs, ino, err);
2867 goto out;
2868 }
2869
2870 err = ext2fs_xattrs_read(h);
2871 if (err) {
2872 ret = translate_error(fs, ino, err);
2873 goto out2;
2874 }
2875
2876 /* Count buffer space needed for names */
2877 bufsz = 0;
2878 err = ext2fs_xattrs_iterate(h, count_buffer_space, &bufsz);
2879 if (err) {
2880 ret = translate_error(fs, ino, err);
2881 goto out2;
2882 }
2883
2884 if (len == 0) {
2885 ret = bufsz;
2886 goto out2;
2887 } else if (len < bufsz) {
2888 ret = -ERANGE;
2889 goto out2;
2890 }
2891
2892 /* Copy names out */
2893 memset(names, 0, len);
2894 err = ext2fs_xattrs_iterate(h, copy_names, &names);
2895 if (err) {
2896 ret = translate_error(fs, ino, err);
2897 goto out2;
2898 }
2899 ret = bufsz;
2900out2:
2901 err = ext2fs_xattrs_close(&h);
f23be676 2902 if (err && !ret)
81cbf1ef
DW
2903 ret = translate_error(fs, ino, err);
2904out:
2905 pthread_mutex_unlock(&ff->bfl);
2906
2907 return ret;
2908}
2909
4e222d9b
TT
2910static int op_setxattr(const char *path EXT2FS_ATTR((unused)),
2911 const char *key, const char *value,
5c7fec61 2912 size_t len, int flags)
81cbf1ef
DW
2913{
2914 struct fuse_context *ctxt = fuse_get_context();
2915 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2916 ext2_filsys fs;
2917 struct ext2_xattr_handle *h;
81cbf1ef
DW
2918 ext2_ino_t ino;
2919 errcode_t err;
2920 int ret = 0;
2921
5c7fec61
DW
2922 if (flags & ~(XATTR_CREATE | XATTR_REPLACE))
2923 return -EOPNOTSUPP;
2924
010a73cd
DW
2925 if (!validate_xattr_name(key))
2926 return -EINVAL;
2927
81cbf1ef
DW
2928 FUSE2FS_CHECK_CONTEXT(ff);
2929 fs = ff->fs;
2930 pthread_mutex_lock(&ff->bfl);
7889640d 2931 if (!ext2fs_has_feature_xattr(fs->super)) {
81cbf1ef
DW
2932 ret = -ENOTSUP;
2933 goto out;
2934 }
2935
2936 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2937 if (err || ino == 0) {
2938 ret = translate_error(fs, 0, err);
2939 goto out;
2940 }
1686713e 2941 dbg_printf(ff, "%s: ino=%d name=%s\n", __func__, ino, key);
81cbf1ef 2942
65ddfa48 2943 ret = check_inum_access(ff, ino, W_OK);
81cbf1ef
DW
2944 if (ret == -EACCES) {
2945 ret = -EPERM;
2946 goto out;
2947 } else if (ret)
2948 goto out;
2949
2950 err = ext2fs_xattrs_open(fs, ino, &h);
2951 if (err) {
2952 ret = translate_error(fs, ino, err);
2953 goto out;
2954 }
2955
2956 err = ext2fs_xattrs_read(h);
2957 if (err) {
2958 ret = translate_error(fs, ino, err);
2959 goto out2;
2960 }
2961
5c7fec61
DW
2962 if (flags & (XATTR_CREATE | XATTR_REPLACE)) {
2963 void *buf;
2964 size_t buflen;
2965
2966 err = ext2fs_xattr_get(h, key, &buf, &buflen);
2967 switch (err) {
2968 case EXT2_ET_EA_KEY_NOT_FOUND:
2969 if (flags & XATTR_REPLACE) {
2970 ret = -ENODATA;
2971 goto out2;
2972 }
2973 break;
2974 case 0:
2975 ext2fs_free_mem(&buf);
2976 if (flags & XATTR_CREATE) {
2977 ret = -EEXIST;
2978 goto out2;
2979 }
2980 break;
2981 default:
2982 ret = translate_error(fs, ino, err);
2983 goto out2;
2984 }
2985 }
2986
0111bdb7 2987 err = ext2fs_xattr_set(h, key, value, len);
81cbf1ef
DW
2988 if (err) {
2989 ret = translate_error(fs, ino, err);
0111bdb7 2990 goto out2;
81cbf1ef
DW
2991 }
2992
81cbf1ef 2993 ret = update_ctime(fs, ino, NULL);
81cbf1ef
DW
2994out2:
2995 err = ext2fs_xattrs_close(&h);
2996 if (!ret && err)
2997 ret = translate_error(fs, ino, err);
2998out:
2999 pthread_mutex_unlock(&ff->bfl);
3000
3001 return ret;
3002}
3003
3004static int op_removexattr(const char *path, const char *key)
3005{
3006 struct fuse_context *ctxt = fuse_get_context();
3007 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
3008 ext2_filsys fs;
3009 struct ext2_xattr_handle *h;
cb115a87
DW
3010 void *buf;
3011 size_t buflen;
81cbf1ef
DW
3012 ext2_ino_t ino;
3013 errcode_t err;
3014 int ret = 0;
3015
010a73cd
DW
3016 /*
3017 * Once in a while libfuse gives us a no-name xattr to delete as part
3018 * of clearing ACLs. Just pretend we cleared them.
3019 */
3020 if (key[0] == 0)
3021 return 0;
3022
3023 if (!validate_xattr_name(key))
3024 return -ENODATA;
3025
81cbf1ef
DW
3026 FUSE2FS_CHECK_CONTEXT(ff);
3027 fs = ff->fs;
3028 pthread_mutex_lock(&ff->bfl);
7889640d 3029 if (!ext2fs_has_feature_xattr(fs->super)) {
81cbf1ef
DW
3030 ret = -ENOTSUP;
3031 goto out;
3032 }
3033
3034 if (!fs_can_allocate(ff, 1)) {
3035 ret = -ENOSPC;
3036 goto out;
3037 }
3038
3039 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
3040 if (err || ino == 0) {
3041 ret = translate_error(fs, 0, err);
3042 goto out;
3043 }
1686713e 3044 dbg_printf(ff, "%s: ino=%d name=%s\n", __func__, ino, key);
81cbf1ef 3045
65ddfa48 3046 ret = check_inum_access(ff, ino, W_OK);
81cbf1ef
DW
3047 if (ret)
3048 goto out;
3049
3050 err = ext2fs_xattrs_open(fs, ino, &h);
3051 if (err) {
3052 ret = translate_error(fs, ino, err);
3053 goto out;
3054 }
3055
3056 err = ext2fs_xattrs_read(h);
3057 if (err) {
3058 ret = translate_error(fs, ino, err);
3059 goto out2;
3060 }
3061
cb115a87
DW
3062 err = ext2fs_xattr_get(h, key, &buf, &buflen);
3063 switch (err) {
3064 case EXT2_ET_EA_KEY_NOT_FOUND:
3065 /*
3066 * ACLs are special snowflakes that require a 0 return when
3067 * the ACL never existed in the first place.
3068 */
3069 if (!strncmp(XATTR_SECURITY_PREFIX, key,
3070 XATTR_SECURITY_PREFIX_LEN))
3071 ret = 0;
3072 else
3073 ret = -ENODATA;
3074 goto out2;
3075 case 0:
3076 ext2fs_free_mem(&buf);
3077 break;
3078 default:
3079 ret = translate_error(fs, ino, err);
3080 goto out2;
3081 }
3082
81cbf1ef
DW
3083 err = ext2fs_xattr_remove(h, key);
3084 if (err) {
3085 ret = translate_error(fs, ino, err);
3086 goto out2;
3087 }
3088
81cbf1ef
DW
3089 ret = update_ctime(fs, ino, NULL);
3090out2:
3091 err = ext2fs_xattrs_close(&h);
f23be676 3092 if (err && !ret)
81cbf1ef
DW
3093 ret = translate_error(fs, ino, err);
3094out:
3095 pthread_mutex_unlock(&ff->bfl);
3096
3097 return ret;
3098}
3099
3100struct readdir_iter {
3101 void *buf;
7b5d75ef 3102 ext2_filsys fs;
81cbf1ef
DW
3103 fuse_fill_dir_t func;
3104};
3105
7b5d75ef
DW
3106static inline mode_t dirent_fmode(ext2_filsys fs,
3107 const struct ext2_dir_entry *dirent)
3108{
3109 if (!ext2fs_has_feature_filetype(fs->super))
3110 return 0;
3111
3112 switch (ext2fs_dirent_file_type(dirent)) {
3113 case EXT2_FT_REG_FILE:
3114 return S_IFREG;
3115 case EXT2_FT_DIR:
3116 return S_IFDIR;
3117 case EXT2_FT_CHRDEV:
3118 return S_IFCHR;
3119 case EXT2_FT_BLKDEV:
3120 return S_IFBLK;
3121 case EXT2_FT_FIFO:
3122 return S_IFIFO;
3123 case EXT2_FT_SOCK:
3124 return S_IFSOCK;
3125 case EXT2_FT_SYMLINK:
3126 return S_IFLNK;
3127 }
3128
3129 return 0;
3130}
3131
4e222d9b
TT
3132static int op_readdir_iter(ext2_ino_t dir EXT2FS_ATTR((unused)),
3133 int entry EXT2FS_ATTR((unused)),
3134 struct ext2_dir_entry *dirent,
3135 int offset EXT2FS_ATTR((unused)),
3136 int blocksize EXT2FS_ATTR((unused)),
3137 char *buf EXT2FS_ATTR((unused)), void *data)
81cbf1ef
DW
3138{
3139 struct readdir_iter *i = data;
3140 char namebuf[EXT2_NAME_LEN + 1];
7b5d75ef
DW
3141 struct stat stat = {
3142 .st_ino = dirent->inode,
3143 .st_mode = dirent_fmode(i->fs, dirent),
3144 };
81cbf1ef
DW
3145 int ret;
3146
3147 memcpy(namebuf, dirent->name, dirent->name_len & 0xFF);
3148 namebuf[dirent->name_len & 0xFF] = 0;
7b5d75ef 3149 ret = i->func(i->buf, namebuf, &stat, 0
448a3f8b
DD
3150#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
3151 , 0
3152#endif
3153 );
81cbf1ef
DW
3154 if (ret)
3155 return DIRENT_ABORT;
3156
3157 return 0;
3158}
3159
4e222d9b
TT
3160static int op_readdir(const char *path EXT2FS_ATTR((unused)),
3161 void *buf, fuse_fill_dir_t fill_func,
3162 off_t offset EXT2FS_ATTR((unused)),
448a3f8b
DD
3163 struct fuse_file_info *fp
3164#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
157b88c3 3165 , enum fuse_readdir_flags flags EXT2FS_ATTR((unused))
448a3f8b
DD
3166#endif
3167 )
81cbf1ef
DW
3168{
3169 struct fuse_context *ctxt = fuse_get_context();
3170 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
3171 struct fuse2fs_file_handle *fh =
3172 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3173 errcode_t err;
3174 struct readdir_iter i;
3175 int ret = 0;
3176
3177 FUSE2FS_CHECK_CONTEXT(ff);
7b5d75ef
DW
3178 i.fs = ff->fs;
3179 FUSE2FS_CHECK_MAGIC(i.fs, fh, FUSE2FS_FILE_MAGIC);
c4d34d7a 3180 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
81cbf1ef
DW
3181 pthread_mutex_lock(&ff->bfl);
3182 i.buf = buf;
3183 i.func = fill_func;
7b5d75ef 3184 err = ext2fs_dir_iterate2(i.fs, fh->ino, 0, NULL, op_readdir_iter, &i);
81cbf1ef 3185 if (err) {
7b5d75ef 3186 ret = translate_error(i.fs, fh->ino, err);
81cbf1ef
DW
3187 goto out;
3188 }
3189
7b5d75ef
DW
3190 if (fs_writeable(i.fs)) {
3191 ret = update_atime(i.fs, fh->ino);
81cbf1ef
DW
3192 if (ret)
3193 goto out;
3194 }
3195out:
3196 pthread_mutex_unlock(&ff->bfl);
3197 return ret;
3198}
3199
3200static int op_access(const char *path, int mask)
3201{
3202 struct fuse_context *ctxt = fuse_get_context();
3203 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
3204 ext2_filsys fs;
3205 errcode_t err;
3206 ext2_ino_t ino;
3207 int ret = 0;
3208
3209 FUSE2FS_CHECK_CONTEXT(ff);
3210 fs = ff->fs;
c4d34d7a 3211 dbg_printf(ff, "%s: path=%s mask=0x%x\n", __func__, path, mask);
81cbf1ef
DW
3212 pthread_mutex_lock(&ff->bfl);
3213 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
3214 if (err || ino == 0) {
3215 ret = translate_error(fs, 0, err);
3216 goto out;
3217 }
3218
65ddfa48 3219 ret = check_inum_access(ff, ino, mask);
81cbf1ef
DW
3220 if (ret)
3221 goto out;
3222
3223out:
3224 pthread_mutex_unlock(&ff->bfl);
3225 return ret;
3226}
3227
3228static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
3229{
3230 struct fuse_context *ctxt = fuse_get_context();
3231 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
3232 ext2_filsys fs;
3233 ext2_ino_t parent, child;
da9d5b80 3234 char *temp_path;
81cbf1ef
DW
3235 errcode_t err;
3236 char *node_name, a;
3237 int filetype;
3238 struct ext2_inode_large inode;
3239 int ret = 0;
3240
3241 FUSE2FS_CHECK_CONTEXT(ff);
3242 fs = ff->fs;
c4d34d7a 3243 dbg_printf(ff, "%s: path=%s mode=0%o\n", __func__, path, mode);
da9d5b80 3244 temp_path = strdup(path);
81cbf1ef
DW
3245 if (!temp_path) {
3246 ret = -ENOMEM;
3247 goto out;
3248 }
3249 node_name = strrchr(temp_path, '/');
3250 if (!node_name) {
3251 ret = -ENOMEM;
3252 goto out;
3253 }
3254 node_name++;
3255 a = *node_name;
3256 *node_name = 0;
3257
3258 pthread_mutex_lock(&ff->bfl);
3259 if (!fs_can_allocate(ff, 1)) {
3260 ret = -ENOSPC;
3261 goto out2;
3262 }
3263
3264 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
3265 &parent);
3266 if (err) {
3267 ret = translate_error(fs, 0, err);
3268 goto out2;
3269 }
3270
9f69dfc4 3271 ret = check_inum_access(ff, parent, A_OK | W_OK);
81cbf1ef
DW
3272 if (ret)
3273 goto out2;
3274
3275 *node_name = a;
3276
3277 filetype = ext2_file_type(mode);
3278
3279 err = ext2fs_new_inode(fs, parent, mode, 0, &child);
3280 if (err) {
3281 ret = translate_error(fs, parent, err);
3282 goto out2;
3283 }
3284
c4d34d7a 3285 dbg_printf(ff, "%s: creating ino=%d/name=%s in dir=%d\n", __func__, child,
81cbf1ef 3286 node_name, parent);
0a5dc78b
TT
3287 err = ext2fs_link(fs, parent, node_name, child,
3288 filetype | EXT2FS_LINK_EXPAND);
81cbf1ef
DW
3289 if (err) {
3290 ret = translate_error(fs, parent, err);
3291 goto out2;
3292 }
3293
3294 ret = update_mtime(fs, parent, NULL);
3295 if (ret)
3296 goto out2;
3297
3298 memset(&inode, 0, sizeof(inode));
3299 inode.i_mode = mode;
3300 inode.i_links_count = 1;
3301 inode.i_extra_isize = sizeof(struct ext2_inode_large) -
3302 EXT2_GOOD_OLD_INODE_SIZE;
f150bdec 3303 inode.i_uid = ctxt->uid;
dae1ecc2 3304 ext2fs_set_i_uid_high(inode, ctxt->uid >> 16);
f150bdec 3305 inode.i_gid = ctxt->gid;
dae1ecc2 3306 ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
7889640d 3307 if (ext2fs_has_feature_extents(fs->super)) {
81cbf1ef
DW
3308 ext2_extent_handle_t handle;
3309
3310 inode.i_flags &= ~EXT4_EXTENTS_FL;
3311 ret = ext2fs_extent_open2(fs, child,
a6d88edd 3312 EXT2_INODE(&inode), &handle);
9f6d1bf3
DW
3313 if (ret) {
3314 ret = translate_error(fs, child, err);
3315 goto out2;
3316 }
3317
81cbf1ef
DW
3318 ext2fs_extent_free(handle);
3319 }
3320
a6d88edd 3321 err = ext2fs_write_new_inode(fs, child, EXT2_INODE(&inode));
81cbf1ef
DW
3322 if (err) {
3323 ret = translate_error(fs, child, err);
3324 goto out2;
3325 }
3326
3327 inode.i_generation = ff->next_generation++;
3328 init_times(&inode);
ca3f4e3a 3329 err = fuse2fs_write_inode(fs, child, &inode);
81cbf1ef
DW
3330 if (err) {
3331 ret = translate_error(fs, child, err);
3332 goto out2;
3333 }
3334
3335 ext2fs_inode_alloc_stats2(fs, child, 1, 0);
3336
2c790038
DW
3337 ret = propagate_default_acls(ff, parent, child);
3338 if (ret)
3339 goto out2;
3340
81cbf1ef
DW
3341 ret = __op_open(ff, path, fp);
3342 if (ret)
3343 goto out2;
3344out2:
3345 pthread_mutex_unlock(&ff->bfl);
3346out:
3347 free(temp_path);
3348 return ret;
3349}
3350
448a3f8b 3351#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
4e222d9b
TT
3352static int op_ftruncate(const char *path EXT2FS_ATTR((unused)),
3353 off_t len, struct fuse_file_info *fp)
81cbf1ef
DW
3354{
3355 struct fuse_context *ctxt = fuse_get_context();
3356 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
3357 struct fuse2fs_file_handle *fh =
3358 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3359 ext2_filsys fs;
3360 ext2_file_t efp;
3361 errcode_t err;
3362 int ret = 0;
3363
3364 FUSE2FS_CHECK_CONTEXT(ff);
3365 fs = ff->fs;
3366 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
7dbac9cd
TT
3367 dbg_printf(ff, "%s: ino=%d len=%jd\n", __func__, fh->ino,
3368 (intmax_t) len);
81cbf1ef
DW
3369 pthread_mutex_lock(&ff->bfl);
3370 if (!fs_writeable(fs)) {
3371 ret = -EROFS;
3372 goto out;
3373 }
3374
3375 err = ext2fs_file_open(fs, fh->ino, fh->open_flags, &efp);
3376 if (err) {
3377 ret = translate_error(fs, fh->ino, err);
3378 goto out;
3379 }
3380
3381 err = ext2fs_file_set_size2(efp, len);
3382 if (err) {
3383 ret = translate_error(fs, fh->ino, err);
3384 goto out2;
3385 }
3386
3387out2:
3388 err = ext2fs_file_close(efp);
3389 if (ret)
3390 goto out;
3391 if (err) {
3392 ret = translate_error(fs, fh->ino, err);
3393 goto out;
3394 }
3395
3396 ret = update_mtime(fs, fh->ino, NULL);
3397 if (ret)
3398 goto out;
3399
3400out:
3401 pthread_mutex_unlock(&ff->bfl);
f23be676 3402 return ret;
81cbf1ef
DW
3403}
3404
4e222d9b
TT
3405static int op_fgetattr(const char *path EXT2FS_ATTR((unused)),
3406 struct stat *statbuf,
81cbf1ef
DW
3407 struct fuse_file_info *fp)
3408{
3409 struct fuse_context *ctxt = fuse_get_context();
3410 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
3411 ext2_filsys fs;
377e3a96
TT
3412 struct fuse2fs_file_handle *fh =
3413 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3414 int ret = 0;
3415
3416 FUSE2FS_CHECK_CONTEXT(ff);
3417 fs = ff->fs;
3418 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
c4d34d7a 3419 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
81cbf1ef
DW
3420 pthread_mutex_lock(&ff->bfl);
3421 ret = stat_inode(fs, fh->ino, statbuf);
3422 pthread_mutex_unlock(&ff->bfl);
3423
3424 return ret;
3425}
448a3f8b 3426#endif /* FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) */
81cbf1ef 3427
448a3f8b
DD
3428static int op_utimens(const char *path, const struct timespec ctv[2]
3429#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
157b88c3 3430 , struct fuse_file_info *fi EXT2FS_ATTR((unused))
448a3f8b
DD
3431#endif
3432 )
81cbf1ef
DW
3433{
3434 struct fuse_context *ctxt = fuse_get_context();
3435 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
4e222d9b 3436 struct timespec tv[2];
81cbf1ef
DW
3437 ext2_filsys fs;
3438 errcode_t err;
3439 ext2_ino_t ino;
3440 struct ext2_inode_large inode;
9f69dfc4 3441 int access = W_OK;
81cbf1ef
DW
3442 int ret = 0;
3443
3444 FUSE2FS_CHECK_CONTEXT(ff);
3445 fs = ff->fs;
3446 pthread_mutex_lock(&ff->bfl);
3447 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
3448 if (err) {
3449 ret = translate_error(fs, 0, err);
3450 goto out;
3451 }
e1339587
DW
3452 dbg_printf(ff, "%s: ino=%d atime=%lld.%ld mtime=%lld.%ld\n", __func__,
3453 ino,
3454 (long long int)ctv[0].tv_sec, ctv[0].tv_nsec,
3455 (long long int)ctv[1].tv_sec, ctv[1].tv_nsec);
81cbf1ef 3456
9f69dfc4
DW
3457 /*
3458 * ext4 allows timestamp updates of append-only files but only if we're
3459 * setting to current time
3460 */
3461 if (ctv[0].tv_nsec == UTIME_NOW && ctv[1].tv_nsec == UTIME_NOW)
3462 access |= A_OK;
3463 ret = check_inum_access(ff, ino, access);
81cbf1ef
DW
3464 if (ret)
3465 goto out;
3466
ca3f4e3a 3467 err = fuse2fs_read_inode(fs, ino, &inode);
81cbf1ef
DW
3468 if (err) {
3469 ret = translate_error(fs, ino, err);
3470 goto out;
3471 }
3472
4e222d9b
TT
3473 tv[0] = ctv[0];
3474 tv[1] = ctv[1];
81cbf1ef
DW
3475#ifdef UTIME_NOW
3476 if (tv[0].tv_nsec == UTIME_NOW)
3477 get_now(tv);
3478 if (tv[1].tv_nsec == UTIME_NOW)
3479 get_now(tv + 1);
3480#endif /* UTIME_NOW */
3481#ifdef UTIME_OMIT
3482 if (tv[0].tv_nsec != UTIME_OMIT)
e1339587 3483 EXT4_INODE_SET_XTIME(i_atime, &tv[0], &inode);
81cbf1ef 3484 if (tv[1].tv_nsec != UTIME_OMIT)
e1339587 3485 EXT4_INODE_SET_XTIME(i_mtime, &tv[1], &inode);
81cbf1ef
DW
3486#endif /* UTIME_OMIT */
3487 ret = update_ctime(fs, ino, &inode);
3488 if (ret)
3489 goto out;
3490
ca3f4e3a 3491 err = fuse2fs_write_inode(fs, ino, &inode);
81cbf1ef
DW
3492 if (err) {
3493 ret = translate_error(fs, ino, err);
3494 goto out;
3495 }
3496
3497out:
3498 pthread_mutex_unlock(&ff->bfl);
3499 return ret;
3500}
3501
9b5012c1 3502#define FUSE2FS_MODIFIABLE_IFLAGS \
0b47ce48
DW
3503 (EXT2_FL_USER_MODIFIABLE & ~(EXT4_EXTENTS_FL | EXT4_CASEFOLD_FL | \
3504 EXT3_JOURNAL_DATA_FL))
9b5012c1
DW
3505
3506static inline int set_iflags(struct ext2_inode_large *inode, __u32 iflags)
3507{
3508 if ((inode->i_flags ^ iflags) & ~FUSE2FS_MODIFIABLE_IFLAGS)
3509 return -EINVAL;
3510
3511 inode->i_flags = (inode->i_flags & ~FUSE2FS_MODIFIABLE_IFLAGS) |
3512 (iflags & FUSE2FS_MODIFIABLE_IFLAGS);
3513 return 0;
3514}
3515
81cbf1ef 3516#ifdef SUPPORT_I_FLAGS
c4d34d7a 3517static int ioctl_getflags(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
81cbf1ef
DW
3518 void *data)
3519{
c4d34d7a 3520 ext2_filsys fs = ff->fs;
81cbf1ef
DW
3521 errcode_t err;
3522 struct ext2_inode_large inode;
3523
3524 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
c4d34d7a 3525 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
ca3f4e3a 3526 err = fuse2fs_read_inode(fs, fh->ino, &inode);
81cbf1ef
DW
3527 if (err)
3528 return translate_error(fs, fh->ino, err);
3529
3530 *(__u32 *)data = inode.i_flags & EXT2_FL_USER_VISIBLE;
3531 return 0;
3532}
3533
c4d34d7a 3534static int ioctl_setflags(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
81cbf1ef
DW
3535 void *data)
3536{
c4d34d7a 3537 ext2_filsys fs = ff->fs;
81cbf1ef
DW
3538 errcode_t err;
3539 struct ext2_inode_large inode;
3540 int ret;
3541 __u32 flags = *(__u32 *)data;
3542 struct fuse_context *ctxt = fuse_get_context();
3543
3544 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
c4d34d7a 3545 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
ca3f4e3a 3546 err = fuse2fs_read_inode(fs, fh->ino, &inode);
81cbf1ef
DW
3547 if (err)
3548 return translate_error(fs, fh->ino, err);
3549
bc76e0f7 3550 if (want_check_owner(ff, ctxt) && inode_uid(inode) != ctxt->uid)
81cbf1ef
DW
3551 return -EPERM;
3552
9b5012c1
DW
3553 ret = set_iflags(&inode, flags);
3554 if (ret)
3555 return ret;
81cbf1ef
DW
3556
3557 ret = update_ctime(fs, fh->ino, &inode);
3558 if (ret)
3559 return ret;
3560
ca3f4e3a 3561 err = fuse2fs_write_inode(fs, fh->ino, &inode);
81cbf1ef
DW
3562 if (err)
3563 return translate_error(fs, fh->ino, err);
3564
3565 return 0;
3566}
3567
c4d34d7a 3568static int ioctl_getversion(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
81cbf1ef
DW
3569 void *data)
3570{
c4d34d7a 3571 ext2_filsys fs = ff->fs;
81cbf1ef
DW
3572 errcode_t err;
3573 struct ext2_inode_large inode;
3574
3575 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
c4d34d7a 3576 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
ca3f4e3a 3577 err = fuse2fs_read_inode(fs, fh->ino, &inode);
81cbf1ef
DW
3578 if (err)
3579 return translate_error(fs, fh->ino, err);
3580
3581 *(__u32 *)data = inode.i_generation;
3582 return 0;
3583}
3584
c4d34d7a 3585static int ioctl_setversion(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
81cbf1ef
DW
3586 void *data)
3587{
c4d34d7a 3588 ext2_filsys fs = ff->fs;
81cbf1ef
DW
3589 errcode_t err;
3590 struct ext2_inode_large inode;
3591 int ret;
3592 __u32 generation = *(__u32 *)data;
3593 struct fuse_context *ctxt = fuse_get_context();
3594
3595 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
c4d34d7a 3596 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
ca3f4e3a 3597 err = fuse2fs_read_inode(fs, fh->ino, &inode);
81cbf1ef
DW
3598 if (err)
3599 return translate_error(fs, fh->ino, err);
3600
bc76e0f7 3601 if (want_check_owner(ff, ctxt) && inode_uid(inode) != ctxt->uid)
81cbf1ef
DW
3602 return -EPERM;
3603
3604 inode.i_generation = generation;
3605
3606 ret = update_ctime(fs, fh->ino, &inode);
3607 if (ret)
3608 return ret;
3609
ca3f4e3a 3610 err = fuse2fs_write_inode(fs, fh->ino, &inode);
81cbf1ef
DW
3611 if (err)
3612 return translate_error(fs, fh->ino, err);
3613
3614 return 0;
3615}
3616#endif /* SUPPORT_I_FLAGS */
3617
9b5012c1
DW
3618#ifdef FS_IOC_FSGETXATTR
3619static __u32 iflags_to_fsxflags(__u32 iflags)
3620{
3621 __u32 xflags = 0;
3622
3623 if (iflags & FS_SYNC_FL)
3624 xflags |= FS_XFLAG_SYNC;
3625 if (iflags & FS_IMMUTABLE_FL)
3626 xflags |= FS_XFLAG_IMMUTABLE;
3627 if (iflags & FS_APPEND_FL)
3628 xflags |= FS_XFLAG_APPEND;
3629 if (iflags & FS_NODUMP_FL)
3630 xflags |= FS_XFLAG_NODUMP;
3631 if (iflags & FS_NOATIME_FL)
3632 xflags |= FS_XFLAG_NOATIME;
3633 if (iflags & FS_DAX_FL)
3634 xflags |= FS_XFLAG_DAX;
3635 if (iflags & FS_PROJINHERIT_FL)
3636 xflags |= FS_XFLAG_PROJINHERIT;
3637 return xflags;
3638}
3639
3640static int ioctl_fsgetxattr(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
3641 void *data)
3642{
3643 ext2_filsys fs = ff->fs;
3644 errcode_t err;
3645 struct ext2_inode_large inode;
3646 struct fsxattr *fsx = data;
3647 unsigned int inode_size;
3648
3649 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3650 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
ca3f4e3a 3651 err = fuse2fs_read_inode(fs, fh->ino, &inode);
9b5012c1
DW
3652 if (err)
3653 return translate_error(fs, fh->ino, err);
3654
3655 memset(fsx, 0, sizeof(*fsx));
3656 inode_size = EXT2_GOOD_OLD_INODE_SIZE + inode.i_extra_isize;
3657 if (ext2fs_inode_includes(inode_size, i_projid))
3658 fsx->fsx_projid = inode_projid(inode);
3659 fsx->fsx_xflags = iflags_to_fsxflags(inode.i_flags);
3660 return 0;
3661}
3662
3663static __u32 fsxflags_to_iflags(__u32 xflags)
3664{
3665 __u32 iflags = 0;
3666
3667 if (xflags & FS_XFLAG_IMMUTABLE)
3668 iflags |= FS_IMMUTABLE_FL;
3669 if (xflags & FS_XFLAG_APPEND)
3670 iflags |= FS_APPEND_FL;
3671 if (xflags & FS_XFLAG_SYNC)
3672 iflags |= FS_SYNC_FL;
3673 if (xflags & FS_XFLAG_NOATIME)
3674 iflags |= FS_NOATIME_FL;
3675 if (xflags & FS_XFLAG_NODUMP)
3676 iflags |= FS_NODUMP_FL;
3677 if (xflags & FS_XFLAG_DAX)
3678 iflags |= FS_DAX_FL;
3679 if (xflags & FS_XFLAG_PROJINHERIT)
3680 iflags |= FS_PROJINHERIT_FL;
3681 return iflags;
3682}
3683
3684static int ioctl_fssetxattr(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
3685 void *data)
3686{
3687 ext2_filsys fs = ff->fs;
3688 errcode_t err;
3689 struct ext2_inode_large inode;
3690 int ret;
3691 struct fuse_context *ctxt = fuse_get_context();
3692 struct fsxattr *fsx = data;
3693 __u32 flags = fsxflags_to_iflags(fsx->fsx_xflags);
3694 unsigned int inode_size;
3695
3696 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3697 dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
ca3f4e3a 3698 err = fuse2fs_read_inode(fs, fh->ino, &inode);
9b5012c1
DW
3699 if (err)
3700 return translate_error(fs, fh->ino, err);
3701
bc76e0f7 3702 if (want_check_owner(ff, ctxt) && inode_uid(inode) != ctxt->uid)
9b5012c1
DW
3703 return -EPERM;
3704
3705 ret = set_iflags(&inode, flags);
3706 if (ret)
3707 return ret;
3708
3709 inode_size = EXT2_GOOD_OLD_INODE_SIZE + inode.i_extra_isize;
3710 if (ext2fs_inode_includes(inode_size, i_projid))
3711 inode.i_projid = fsx->fsx_projid;
3712
3713 ret = update_ctime(fs, fh->ino, &inode);
3714 if (ret)
3715 return ret;
3716
ca3f4e3a 3717 err = fuse2fs_write_inode(fs, fh->ino, &inode);
9b5012c1
DW
3718 if (err)
3719 return translate_error(fs, fh->ino, err);
3720
3721 return 0;
3722}
3723#endif /* FS_IOC_FSGETXATTR */
3724
81cbf1ef 3725#ifdef FITRIM
c4d34d7a 3726static int ioctl_fitrim(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
81cbf1ef
DW
3727 void *data)
3728{
c4d34d7a 3729 ext2_filsys fs = ff->fs;
81cbf1ef 3730 struct fstrim_range *fr = data;
7235b585
DW
3731 blk64_t start, end, max_blocks, b, cleared, minlen;
3732 blk64_t max_blks = ext2fs_blocks_count(fs->super);
81cbf1ef
DW
3733 errcode_t err = 0;
3734
7235b585
DW
3735 if (!fs_writeable(fs))
3736 return -EROFS;
3737
241dae1b
DW
3738 start = FUSE2FS_B_TO_FSBT(ff, fr->start);
3739 end = FUSE2FS_B_TO_FSBT(ff, fr->start + fr->len - 1);
3740 minlen = FUSE2FS_B_TO_FSBT(ff, fr->minlen);
7235b585
DW
3741
3742 if (EXT2FS_NUM_B2C(fs, minlen) > EXT2_CLUSTERS_PER_GROUP(fs->super) ||
3743 start >= max_blks ||
3744 fr->len < fs->blocksize)
3745 return -EINVAL;
3746
3747 dbg_printf(ff, "%s: start=%llu end=%llu minlen=%llu\n", __func__,
3748 start, end, minlen);
81cbf1ef
DW
3749
3750 if (start < fs->super->s_first_data_block)
3751 start = fs->super->s_first_data_block;
81cbf1ef
DW
3752
3753 if (end < fs->super->s_first_data_block)
3754 end = fs->super->s_first_data_block;
3755 if (end >= ext2fs_blocks_count(fs->super))
3756 end = ext2fs_blocks_count(fs->super) - 1;
3757
3758 cleared = 0;
241dae1b 3759 max_blocks = FUSE2FS_B_TO_FSBT(ff, 2048ULL * 1024 * 1024);
81cbf1ef
DW
3760
3761 fr->len = 0;
3762 while (start <= end) {
3763 err = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
3764 start, end, &start);
7235b585
DW
3765 switch (err) {
3766 case 0:
3767 break;
3768 case ENOENT:
3769 /* no free blocks found, so we're done */
3770 err = 0;
3771 goto out;
3772 default:
81cbf1ef 3773 return translate_error(fs, fh->ino, err);
7235b585 3774 }
81cbf1ef
DW
3775
3776 b = start + max_blocks < end ? start + max_blocks : end;
3777 err = ext2fs_find_first_set_block_bitmap2(fs->block_map,
3778 start, b, &b);
3779 if (err && err != ENOENT)
3780 return translate_error(fs, fh->ino, err);
7235b585 3781 if (b - start >= minlen) {
81cbf1ef
DW
3782 err = io_channel_discard(fs->io, start, b - start);
3783 if (err)
3784 return translate_error(fs, fh->ino, err);
3785 cleared += b - start;
241dae1b 3786 fr->len = FUSE2FS_FSB_TO_B(ff, cleared);
81cbf1ef
DW
3787 }
3788 start = b + 1;
3789 }
3790
7235b585
DW
3791out:
3792 fr->len = cleared;
81cbf1ef
DW
3793 return err;
3794}
3795#endif /* FITRIM */
3796
3797#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
448a3f8b
DD
3798static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
3799#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
3800 unsigned int cmd,
3801#else
3802 int cmd,
3803#endif
4e222d9b
TT
3804 void *arg EXT2FS_ATTR((unused)),
3805 struct fuse_file_info *fp,
3806 unsigned int flags EXT2FS_ATTR((unused)), void *data)
81cbf1ef
DW
3807{
3808 struct fuse_context *ctxt = fuse_get_context();
3809 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
3810 struct fuse2fs_file_handle *fh =
3811 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3812 int ret = 0;
3813
3814 FUSE2FS_CHECK_CONTEXT(ff);
81cbf1ef 3815 pthread_mutex_lock(&ff->bfl);
4e222d9b 3816 switch ((unsigned long) cmd) {
81cbf1ef
DW
3817#ifdef SUPPORT_I_FLAGS
3818 case EXT2_IOC_GETFLAGS:
c4d34d7a 3819 ret = ioctl_getflags(ff, fh, data);
81cbf1ef
DW
3820 break;
3821 case EXT2_IOC_SETFLAGS:
c4d34d7a 3822 ret = ioctl_setflags(ff, fh, data);
81cbf1ef
DW
3823 break;
3824 case EXT2_IOC_GETVERSION:
c4d34d7a 3825 ret = ioctl_getversion(ff, fh, data);
81cbf1ef
DW
3826 break;
3827 case EXT2_IOC_SETVERSION:
c4d34d7a 3828 ret = ioctl_setversion(ff, fh, data);
81cbf1ef
DW
3829 break;
3830#endif
9b5012c1
DW
3831#ifdef FS_IOC_FSGETXATTR
3832 case FS_IOC_FSGETXATTR:
3833 ret = ioctl_fsgetxattr(ff, fh, data);
3834 break;
3835 case FS_IOC_FSSETXATTR:
3836 ret = ioctl_fssetxattr(ff, fh, data);
3837 break;
3838#endif
81cbf1ef
DW
3839#ifdef FITRIM
3840 case FITRIM:
c4d34d7a 3841 ret = ioctl_fitrim(ff, fh, data);
81cbf1ef
DW
3842 break;
3843#endif
3844 default:
c4d34d7a 3845 dbg_printf(ff, "%s: Unknown ioctl %d\n", __func__, cmd);
81cbf1ef
DW
3846 ret = -ENOTTY;
3847 }
3848 pthread_mutex_unlock(&ff->bfl);
3849
3850 return ret;
3851}
3852#endif /* FUSE 28 */
3853
4e222d9b
TT
3854static int op_bmap(const char *path, size_t blocksize EXT2FS_ATTR((unused)),
3855 uint64_t *idx)
81cbf1ef
DW
3856{
3857 struct fuse_context *ctxt = fuse_get_context();
3858 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
3859 ext2_filsys fs;
3860 ext2_ino_t ino;
3861 errcode_t err;
3862 int ret = 0;
3863
3864 FUSE2FS_CHECK_CONTEXT(ff);
3865 fs = ff->fs;
3866 pthread_mutex_lock(&ff->bfl);
3867 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
3868 if (err) {
3869 ret = translate_error(fs, 0, err);
3870 goto out;
3871 }
c4d34d7a 3872 dbg_printf(ff, "%s: ino=%d blk=%"PRIu64"\n", __func__, ino, *idx);
81cbf1ef
DW
3873
3874 err = ext2fs_bmap2(fs, ino, NULL, NULL, 0, *idx, 0, (blk64_t *)idx);
3875 if (err) {
3876 ret = translate_error(fs, ino, err);
3877 goto out;
3878 }
3879
3880out:
3881 pthread_mutex_unlock(&ff->bfl);
3882 return ret;
3883}
3884
3885#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
3886# ifdef SUPPORT_FALLOCATE
3887static int fallocate_helper(struct fuse_file_info *fp, int mode, off_t offset,
3888 off_t len)
3889{
3890 struct fuse_context *ctxt = fuse_get_context();
3891 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
3892 struct fuse2fs_file_handle *fh =
3893 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3894 ext2_filsys fs;
3895 struct ext2_inode_large inode;
3896 blk64_t start, end;
3897 __u64 fsize;
3898 errcode_t err;
3899 int flags;
3900
3901 FUSE2FS_CHECK_CONTEXT(ff);
3902 fs = ff->fs;
3903 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
241dae1b
DW
3904 start = FUSE2FS_B_TO_FSBT(ff, offset);
3905 end = FUSE2FS_B_TO_FSBT(ff, offset + len - 1);
3906 dbg_printf(ff, "%s: ino=%d mode=0x%x start=%llu end=%llu\n", __func__,
3907 fh->ino, mode, start, end);
3908 if (!fs_can_allocate(ff, FUSE2FS_B_TO_FSB(ff, len)))
81cbf1ef
DW
3909 return -ENOSPC;
3910
ca3f4e3a 3911 err = fuse2fs_read_inode(fs, fh->ino, &inode);
81cbf1ef
DW
3912 if (err)
3913 return err;
3914 fsize = EXT2_I_SIZE(&inode);
3915
3916 /* Allocate a bunch of blocks */
3917 flags = (mode & FL_KEEP_SIZE_FLAG ? 0 :
3918 EXT2_FALLOCATE_INIT_BEYOND_EOF);
3919 err = ext2fs_fallocate(fs, flags, fh->ino,
a6d88edd 3920 EXT2_INODE(&inode),
81cbf1ef
DW
3921 ~0ULL, start, end - start + 1);
3922 if (err && err != EXT2_ET_BLOCK_ALLOC_FAIL)
3923 return translate_error(fs, fh->ino, err);
3924
3925 /* Update i_size */
3926 if (!(mode & FL_KEEP_SIZE_FLAG)) {
4e222d9b 3927 if ((__u64) offset + len > fsize) {
81cbf1ef 3928 err = ext2fs_inode_size_set(fs,
a6d88edd 3929 EXT2_INODE(&inode),
81cbf1ef
DW
3930 offset + len);
3931 if (err)
3932 return translate_error(fs, fh->ino, err);
3933 }
3934 }
3935
3936 err = update_mtime(fs, fh->ino, &inode);
3937 if (err)
3938 return err;
3939
ca3f4e3a 3940 err = fuse2fs_write_inode(fs, fh->ino, &inode);
81cbf1ef
DW
3941 if (err)
3942 return translate_error(fs, fh->ino, err);
3943
3944 return err;
3945}
3946
241dae1b
DW
3947static errcode_t clean_block_middle(struct fuse2fs *ff, ext2_ino_t ino,
3948 struct ext2_inode_large *inode,
3949 off_t offset, off_t len, char **buf)
81cbf1ef 3950{
241dae1b 3951 ext2_filsys fs = ff->fs;
81cbf1ef
DW
3952 blk64_t blk;
3953 off_t residue;
3954 int retflags;
3955 errcode_t err;
3956
241dae1b 3957 residue = FUSE2FS_OFF_IN_FSB(ff, offset);
81cbf1ef
DW
3958 if (residue == 0)
3959 return 0;
3960
3961 if (!*buf) {
3962 err = ext2fs_get_mem(fs->blocksize, buf);
3963 if (err)
3964 return err;
3965 }
3966
a6d88edd 3967 err = ext2fs_bmap2(fs, ino, EXT2_INODE(inode), *buf, 0,
241dae1b 3968 FUSE2FS_B_TO_FSBT(ff, offset), &retflags, &blk);
81cbf1ef
DW
3969 if (err)
3970 return err;
3971 if (!blk || (retflags & BMAP_RET_UNINIT))
3972 return 0;
3973
3974 err = io_channel_read_blk(fs->io, blk, 1, *buf);
3975 if (err)
3976 return err;
3977
3978 memset(*buf + residue, 0, len);
3979
3980 return io_channel_write_blk(fs->io, blk, 1, *buf);
3981}
3982
241dae1b 3983static errcode_t clean_block_edge(struct fuse2fs *ff, ext2_ino_t ino,
81cbf1ef
DW
3984 struct ext2_inode_large *inode, off_t offset,
3985 int clean_before, char **buf)
3986{
241dae1b 3987 ext2_filsys fs = ff->fs;
81cbf1ef
DW
3988 blk64_t blk;
3989 int retflags;
3990 off_t residue;
3991 errcode_t err;
3992
241dae1b 3993 residue = FUSE2FS_OFF_IN_FSB(ff, offset);
81cbf1ef
DW
3994 if (residue == 0)
3995 return 0;
3996
3997 if (!*buf) {
3998 err = ext2fs_get_mem(fs->blocksize, buf);
3999 if (err)
4000 return err;
4001 }
4002
a6d88edd 4003 err = ext2fs_bmap2(fs, ino, EXT2_INODE(inode), *buf, 0,
241dae1b 4004 FUSE2FS_B_TO_FSBT(ff, offset), &retflags, &blk);
81cbf1ef
DW
4005 if (err)
4006 return err;
4007
4008 err = io_channel_read_blk(fs->io, blk, 1, *buf);
4009 if (err)
4010 return err;
4011 if (!blk || (retflags & BMAP_RET_UNINIT))
4012 return 0;
4013
4014 if (clean_before)
4015 memset(*buf, 0, residue);
4016 else
4017 memset(*buf + residue, 0, fs->blocksize - residue);
4018
4019 return io_channel_write_blk(fs->io, blk, 1, *buf);
4020}
4021
4022static int punch_helper(struct fuse_file_info *fp, int mode, off_t offset,
4023 off_t len)
4024{
4025 struct fuse_context *ctxt = fuse_get_context();
4026 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
4027 struct fuse2fs_file_handle *fh =
4028 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
4029 ext2_filsys fs;
4030 struct ext2_inode_large inode;
4031 blk64_t start, end;
4032 errcode_t err;
4033 char *buf = NULL;
4034
4035 FUSE2FS_CHECK_CONTEXT(ff);
4036 fs = ff->fs;
4037 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
7dbac9cd
TT
4038 dbg_printf(ff, "%s: offset=%jd len=%jd\n", __func__,
4039 (intmax_t) offset, (intmax_t) len);
81cbf1ef
DW
4040
4041 /* kernel ext4 punch requires this flag to be set */
4042 if (!(mode & FL_KEEP_SIZE_FLAG))
4043 return -EINVAL;
4044
4045 /* Punch out a bunch of blocks */
241dae1b 4046 start = FUSE2FS_B_TO_FSB(ff, offset);
81cbf1ef 4047 end = (offset + len - fs->blocksize) / fs->blocksize;
c4d34d7a 4048 dbg_printf(ff, "%s: ino=%d mode=0x%x start=%llu end=%llu\n", __func__,
81cbf1ef
DW
4049 fh->ino, mode, start, end);
4050
ca3f4e3a 4051 err = fuse2fs_read_inode(fs, fh->ino, &inode);
81cbf1ef
DW
4052 if (err)
4053 return translate_error(fs, fh->ino, err);
4054
4055 /* Zero everything before the first block and after the last block */
241dae1b
DW
4056 if (FUSE2FS_B_TO_FSBT(ff, offset) == FUSE2FS_B_TO_FSBT(ff, offset + len))
4057 err = clean_block_middle(ff, fh->ino, &inode, offset,
81cbf1ef
DW
4058 len, &buf);
4059 else {
241dae1b 4060 err = clean_block_edge(ff, fh->ino, &inode, offset, 0, &buf);
81cbf1ef 4061 if (!err)
241dae1b 4062 err = clean_block_edge(ff, fh->ino, &inode,
81cbf1ef
DW
4063 offset + len, 1, &buf);
4064 }
4065 if (buf)
4066 ext2fs_free_mem(&buf);
4067 if (err)
4068 return translate_error(fs, fh->ino, err);
4069
4070 /* Unmap full blocks in the middle */
4071 if (start <= end) {
a6d88edd 4072 err = ext2fs_punch(fs, fh->ino, EXT2_INODE(&inode),
81cbf1ef
DW
4073 NULL, start, end);
4074 if (err)
4075 return translate_error(fs, fh->ino, err);
4076 }
4077
4078 err = update_mtime(fs, fh->ino, &inode);
4079 if (err)
4080 return err;
4081
ca3f4e3a 4082 err = fuse2fs_write_inode(fs, fh->ino, &inode);
81cbf1ef
DW
4083 if (err)
4084 return translate_error(fs, fh->ino, err);
4085
4086 return 0;
4087}
4088
44e024fd
DW
4089static int zero_helper(struct fuse_file_info *fp, int mode, off_t offset,
4090 off_t len)
4091{
4092 int ret = punch_helper(fp, mode | FL_KEEP_SIZE_FLAG, offset, len);
4093
4094 if (!ret)
4095 ret = fallocate_helper(fp, mode, offset, len);
4096 return ret;
4097}
4098
4e222d9b
TT
4099static int op_fallocate(const char *path EXT2FS_ATTR((unused)), int mode,
4100 off_t offset, off_t len,
81cbf1ef
DW
4101 struct fuse_file_info *fp)
4102{
4103 struct fuse_context *ctxt = fuse_get_context();
4104 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
4105 ext2_filsys fs = ff->fs;
4106 int ret;
4107
4108 /* Catch unknown flags */
f09e999b 4109 if (mode & ~(FL_ZERO_RANGE_FLAG | FL_PUNCH_HOLE_FLAG | FL_KEEP_SIZE_FLAG))
7775293c 4110 return -EOPNOTSUPP;
81cbf1ef
DW
4111
4112 pthread_mutex_lock(&ff->bfl);
4113 if (!fs_writeable(fs)) {
4114 ret = -EROFS;
4115 goto out;
4116 }
44e024fd
DW
4117 if (mode & FL_ZERO_RANGE_FLAG)
4118 ret = zero_helper(fp, mode, offset, len);
4119 else if (mode & FL_PUNCH_HOLE_FLAG)
81cbf1ef
DW
4120 ret = punch_helper(fp, mode, offset, len);
4121 else
4122 ret = fallocate_helper(fp, mode, offset, len);
4123out:
4124 pthread_mutex_unlock(&ff->bfl);
4125
4126 return ret;
4127}
4128# endif /* SUPPORT_FALLOCATE */
4129#endif /* FUSE 29 */
4130
4131static struct fuse_operations fs_ops = {
4132 .init = op_init,
4133 .destroy = op_destroy,
4134 .getattr = op_getattr,
4135 .readlink = op_readlink,
4136 .mknod = op_mknod,
4137 .mkdir = op_mkdir,
4138 .unlink = op_unlink,
4139 .rmdir = op_rmdir,
4140 .symlink = op_symlink,
4141 .rename = op_rename,
4142 .link = op_link,
4143 .chmod = op_chmod,
4144 .chown = op_chown,
4145 .truncate = op_truncate,
4146 .open = op_open,
4147 .read = op_read,
4148 .write = op_write,
4149 .statfs = op_statfs,
4150 .release = op_release,
4151 .fsync = op_fsync,
4152 .setxattr = op_setxattr,
4153 .getxattr = op_getxattr,
4154 .listxattr = op_listxattr,
4155 .removexattr = op_removexattr,
4156 .opendir = op_open,
4157 .readdir = op_readdir,
4158 .releasedir = op_release,
4159 .fsyncdir = op_fsync,
4160 .access = op_access,
4161 .create = op_create,
448a3f8b 4162#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
81cbf1ef
DW
4163 .ftruncate = op_ftruncate,
4164 .fgetattr = op_fgetattr,
448a3f8b 4165#endif
81cbf1ef 4166 .utimens = op_utimens,
448a3f8b 4167#if (FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)) && (FUSE_VERSION < FUSE_MAKE_VERSION(3, 0))
81cbf1ef
DW
4168# if defined(UTIME_NOW) || defined(UTIME_OMIT)
4169 .flag_utime_omit_ok = 1,
4170# endif
4171#endif
4172 .bmap = op_bmap,
4173#ifdef SUPERFLUOUS
4174 .lock = op_lock,
4175 .poll = op_poll,
4176#endif
4177#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
4178 .ioctl = op_ioctl,
448a3f8b 4179#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
81cbf1ef
DW
4180 .flag_nullpath_ok = 1,
4181#endif
448a3f8b 4182#endif
81cbf1ef 4183#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
448a3f8b 4184#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
81cbf1ef 4185 .flag_nopath = 1,
448a3f8b 4186#endif
81cbf1ef
DW
4187# ifdef SUPPORT_FALLOCATE
4188 .fallocate = op_fallocate,
4189# endif
4190#endif
4191};
4192
4193static int get_random_bytes(void *p, size_t sz)
4194{
4195 int fd;
4196 ssize_t r;
4197
4e222d9b 4198 fd = open("/dev/urandom", O_RDONLY);
81cbf1ef 4199 if (fd < 0) {
4e222d9b 4200 perror("/dev/urandom");
81cbf1ef
DW
4201 return 0;
4202 }
4203
4204 r = read(fd, p, sz);
4205
4206 close(fd);
4e222d9b 4207 return (size_t) r == sz;
81cbf1ef
DW
4208}
4209
69df8496 4210enum {
5776fc59 4211 FUSE2FS_IGNORED,
69df8496
TT
4212 FUSE2FS_VERSION,
4213 FUSE2FS_HELP,
4214 FUSE2FS_HELPFULL,
51680537 4215 FUSE2FS_CACHE_SIZE,
69df8496
TT
4216};
4217
4218#define FUSE2FS_OPT(t, p, v) { t, offsetof(struct fuse2fs, p), v }
4219
4220static struct fuse_opt fuse2fs_opts[] = {
4221 FUSE2FS_OPT("ro", ro, 1),
38753809 4222 FUSE2FS_OPT("rw", ro, 0),
69df8496
TT
4223 FUSE2FS_OPT("errors=panic", panic_on_error, 1),
4224 FUSE2FS_OPT("minixdf", minixdf, 1),
8ba9236f 4225 FUSE2FS_OPT("bsddf", minixdf, 0),
ea1a3fa2 4226 FUSE2FS_OPT("fakeroot", fakeroot, 1),
69df8496
TT
4227 FUSE2FS_OPT("fuse2fs_debug", debug, 1),
4228 FUSE2FS_OPT("no_default_opts", no_default_opts, 1),
75e3a9ef 4229 FUSE2FS_OPT("norecovery", norecovery, 1),
8ba9236f 4230 FUSE2FS_OPT("noload", norecovery, 1),
5776fc59 4231 FUSE2FS_OPT("offset=%lu", offset, 0),
667ea124 4232 FUSE2FS_OPT("kernel", kernel, 1),
60d0575a 4233 FUSE2FS_OPT("directio", directio, 1),
0a72f465
DW
4234 FUSE2FS_OPT("acl", acl, 1),
4235 FUSE2FS_OPT("noacl", acl, 0),
5776fc59 4236
5776fc59 4237 FUSE_OPT_KEY("user_xattr", FUSE2FS_IGNORED),
8ba9236f 4238 FUSE_OPT_KEY("noblock_validity", FUSE2FS_IGNORED),
bdef89e6 4239 FUSE_OPT_KEY("nodelalloc", FUSE2FS_IGNORED),
51680537 4240 FUSE_OPT_KEY("cache_size=%s", FUSE2FS_CACHE_SIZE),
e83c0df0 4241 FUSE2FS_OPT("lockfile=%s", lockfile, 0),
69df8496
TT
4242
4243 FUSE_OPT_KEY("-V", FUSE2FS_VERSION),
4244 FUSE_OPT_KEY("--version", FUSE2FS_VERSION),
4245 FUSE_OPT_KEY("-h", FUSE2FS_HELP),
4246 FUSE_OPT_KEY("--help", FUSE2FS_HELP),
4247 FUSE_OPT_KEY("--helpfull", FUSE2FS_HELPFULL),
4248 FUSE_OPT_END
4249};
4250
4251
4252static int fuse2fs_opt_proc(void *data, const char *arg,
4253 int key, struct fuse_args *outargs)
81cbf1ef 4254{
69df8496
TT
4255 struct fuse2fs *ff = data;
4256
4257 switch (key) {
4258 case FUSE_OPT_KEY_NONOPT:
4259 if (!ff->device) {
4260 ff->device = strdup(arg);
4261 return 0;
4262 }
4263 return 1;
51680537 4264 case FUSE2FS_CACHE_SIZE:
1d6ff538 4265 ff->cache_size = parse_num_blocks2(arg + 11, -1);
51680537
DW
4266 if (ff->cache_size < 1 || ff->cache_size > INT32_MAX) {
4267 fprintf(stderr, "%s: %s\n", arg,
4268 _("cache size must be between 1 block and 2GB."));
4269 return -1;
4270 }
4271
4272 /* do not pass through to libfuse */
4273 return 0;
5776fc59
DW
4274 case FUSE2FS_IGNORED:
4275 return 0;
69df8496
TT
4276 case FUSE2FS_HELP:
4277 case FUSE2FS_HELPFULL:
4278 fprintf(stderr,
4279 "usage: %s device/image mountpoint [options]\n"
4280 "\n"
4281 "general options:\n"
4282 " -o opt,[opt...] mount options\n"
4283 " -h --help print help\n"
4284 " -V --version print version\n"
4285 "\n"
4286 "fuse2fs options:\n"
69df8496
TT
4287 " -o errors=panic dump core on error\n"
4288 " -o minixdf minix-style df\n"
ea1a3fa2 4289 " -o fakeroot pretend to be root for permission checks\n"
69df8496 4290 " -o no_default_opts do not include default fuse options\n"
c4efea44 4291 " -o offset=<bytes> similar to mount -o offset=<bytes>, mount the partition starting at <bytes>\n"
38753809 4292 " -o norecovery don't replay the journal\n"
69df8496 4293 " -o fuse2fs_debug enable fuse2fs debugging\n"
e83c0df0 4294 " -o lockfile=<file> file to show that fuse is still using the file system image\n"
667ea124
DW
4295 " -o kernel run this as if it were the kernel, which sets:\n"
4296 " allow_others,default_permissions,suid,dev\n"
60d0575a 4297 " -o directio use O_DIRECT to read and write the disk\n"
51680537 4298 " -o cache_size=N[KMG] use a disk cache of this size\n"
69df8496
TT
4299 "\n",
4300 outargs->argv[0]);
4301 if (key == FUSE2FS_HELPFULL) {
e26c0d04 4302 fuse_opt_add_arg(outargs, "-h");
69df8496
TT
4303 fuse_main(outargs->argc, outargs->argv, &fs_ops, NULL);
4304 } else {
4305 fprintf(stderr, "Try --helpfull to get a list of "
4306 "all flags, including the FUSE options.\n");
4307 }
4308 exit(1);
4309
4310 case FUSE2FS_VERSION:
4311 fprintf(stderr, "fuse2fs %s (%s)\n", E2FSPROGS_VERSION,
4312 E2FSPROGS_DATE);
4313 fuse_opt_add_arg(outargs, "--version");
4314 fuse_main(outargs->argc, outargs->argv, &fs_ops, NULL);
4315 exit(0);
4316 }
4317 return 1;
81cbf1ef
DW
4318}
4319
13e365bb
DW
4320static const char *get_subtype(const char *argv0)
4321{
4322 size_t argvlen = strlen(argv0);
4323
4324 if (argvlen < 4)
4325 goto out_default;
4326
4327 if (argv0[argvlen - 4] == 'e' &&
4328 argv0[argvlen - 3] == 'x' &&
4329 argv0[argvlen - 2] == 't' &&
4330 isdigit(argv0[argvlen - 1]))
4331 return &argv0[argvlen - 4];
4332
4333out_default:
4334 return "ext4";
4335}
4336
51680537
DW
4337/* Figure out a reasonable default size for the disk cache */
4338static unsigned long long default_cache_size(void)
4339{
4340 long pages = 0, pagesize = 0;
5c38ce55
TT
4341 unsigned long long max_cache;
4342 unsigned long long ret = 32ULL << 20; /* 32 MB */
51680537
DW
4343
4344#ifdef _SC_PHYS_PAGES
4345 pages = sysconf(_SC_PHYS_PAGES);
4346#endif
4347#ifdef _SC_PAGESIZE
4348 pagesize = sysconf(_SC_PAGESIZE);
4349#endif
5c38ce55
TT
4350 if (pages > 0 && pagesize > 0) {
4351 max_cache = (unsigned long long)pagesize * pages / 20;
51680537 4352
5c38ce55
TT
4353 if (max_cache > 0 && ret > max_cache)
4354 ret = max_cache;
4355 }
51680537
DW
4356 return ret;
4357}
4358
81cbf1ef
DW
4359int main(int argc, char *argv[])
4360{
69df8496
TT
4361 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
4362 struct fuse2fs fctx;
81cbf1ef 4363 errcode_t err;
ce89945a 4364 FILE *orig_stderr = stderr;
8feb6f43 4365 char *logfile;
81cbf1ef 4366 char extra_args[BUFSIZ];
37d56864 4367 int ret = 0;
38753809
DW
4368 int flags = EXT2_FLAG_64BITS | EXT2_FLAG_THREADS | EXT2_FLAG_EXCLUSIVE |
4369 EXT2_FLAG_RW;
81cbf1ef 4370
69df8496
TT
4371 memset(&fctx, 0, sizeof(fctx));
4372 fctx.magic = FUSE2FS_MAGIC;
81cbf1ef 4373
69df8496
TT
4374 fuse_opt_parse(&args, &fctx, fuse2fs_opts, fuse2fs_opt_proc);
4375 if (fctx.device == NULL) {
4376 fprintf(stderr, "Missing ext4 device/image\n");
4377 fprintf(stderr, "See '%s -h' for usage\n", argv[0]);
4378 exit(1);
81cbf1ef
DW
4379 }
4380
c4d34d7a
DW
4381 /* /dev/sda -> sda for reporting */
4382 fctx.shortdev = strrchr(fctx.device, '/');
4383 if (fctx.shortdev)
4384 fctx.shortdev++;
4385 else
4386 fctx.shortdev = fctx.device;
4387
81cbf1ef
DW
4388#ifdef ENABLE_NLS
4389 setlocale(LC_MESSAGES, "");
4390 setlocale(LC_CTYPE, "");
4391 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
4392 textdomain(NLS_CAT_NAME);
4393 set_com_err_gettext(gettext);
4394#endif
4395 add_error_table(&et_ext2_error_table);
4396
81cbf1ef
DW
4397 /* Set up error logging */
4398 logfile = getenv("FUSE2FS_LOGFILE");
4399 if (logfile) {
5cdebf3e
DW
4400 FILE *fp = fopen(logfile, "a");
4401 if (!fp) {
81cbf1ef 4402 perror(logfile);
6ae16a68 4403 goto out;
81cbf1ef 4404 }
5cdebf3e 4405 stderr = fp;
7e651c35 4406 stdout = fp;
667ea124
DW
4407 } else if (fctx.kernel) {
4408 /* in kernel mode, try to log errors to the kernel log */
4409 FILE *fp = fopen("/dev/ttyprintk", "a");
4410 if (fp) {
4411 stderr = fp;
4412 stdout = fp;
4413 }
5cdebf3e 4414 }
81cbf1ef
DW
4415
4416 /* Will we allow users to allocate every last block? */
4417 if (getenv("FUSE2FS_ALLOC_ALL_BLOCKS")) {
cbbf7811
DW
4418 log_printf(&fctx, "%s\n",
4419 _("Allowing users to allocate all blocks. This is dangerous!"));
69df8496 4420 fctx.alloc_all_blocks = 1;
81cbf1ef
DW
4421 }
4422
e50fbaa4
DW
4423 if (fctx.lockfile) {
4424 FILE *lockfile = fopen(fctx.lockfile, "w");
4425 char *resolved;
4426
4427 if (!lockfile) {
4428 err = errno;
4429 err_printf(&fctx, "%s: %s: %s\n", fctx.lockfile,
4430 _("opening lockfile failed"),
4431 strerror(err));
4432 fctx.lockfile = NULL;
4433 ret |= 32;
4434 goto out;
08dcb612 4435 }
e83c0df0 4436 fclose(lockfile);
e50fbaa4
DW
4437
4438 resolved = realpath(fctx.lockfile, NULL);
08dcb612 4439 if (!resolved) {
e50fbaa4
DW
4440 err = errno;
4441 err_printf(&fctx, "%s: %s: %s\n", fctx.lockfile,
4442 _("resolving lockfile failed"),
4443 strerror(err));
e83c0df0 4444 unlink(fctx.lockfile);
e50fbaa4
DW
4445 fctx.lockfile = NULL;
4446 ret |= 32;
4447 goto out;
08dcb612 4448 }
e83c0df0
TT
4449 free(fctx.lockfile);
4450 fctx.lockfile = resolved;
08dcb612
TT
4451 }
4452
81cbf1ef
DW
4453 /* Start up the fs (while we still can use stdout) */
4454 ret = 2;
c4efea44
MS
4455 char options[50];
4456 sprintf(options, "offset=%lu", fctx.offset);
60d0575a
DW
4457 if (fctx.directio)
4458 flags |= EXT2_FLAG_DIRECT_IO;
c4efea44 4459 err = ext2fs_open2(fctx.device, options, flags, 0, 0, unix_io_manager,
81cbf1ef
DW
4460 &global_fs);
4461 if (err) {
348d8481
DW
4462 err_printf(&fctx, "%s.\n", error_message(err));
4463 err_printf(&fctx, "%s\n", _("Please run e2fsck -fy."));
6ae16a68 4464 goto out;
81cbf1ef 4465 }
69df8496
TT
4466 fctx.fs = global_fs;
4467 global_fs->priv_data = &fctx;
241dae1b
DW
4468 fctx.blocklog = u_log2(fctx.fs->blocksize);
4469 fctx.blockmask = fctx.fs->blocksize - 1;
81cbf1ef 4470
51680537
DW
4471 if (!fctx.cache_size)
4472 fctx.cache_size = default_cache_size();
4473 if (fctx.cache_size) {
4474 char buf[55];
4475
4476 snprintf(buf, sizeof(buf), "cache_blocks=%llu",
241dae1b 4477 FUSE2FS_B_TO_FSBT(&fctx, fctx.cache_size));
51680537
DW
4478 err = io_channel_set_options(global_fs->io, buf);
4479 if (err) {
4480 err_printf(&fctx, "%s %lluk: %s\n",
4481 _("cannot set disk cache size to"),
4482 fctx.cache_size >> 10,
4483 error_message(err));
4484 goto out;
4485 }
4486 }
4487
81cbf1ef 4488 ret = 3;
aca77cad 4489
ccbc6f24
DW
4490 if (ext2fs_has_feature_quota(global_fs->super)) {
4491 err_printf(&fctx, "%s", _("quotas not supported."));
4492 goto out;
4493 }
4494 if (ext2fs_has_feature_verity(global_fs->super)) {
4495 err_printf(&fctx, "%s", _("verity not supported."));
4496 goto out;
4497 }
4498 if (ext2fs_has_feature_encrypt(global_fs->super)) {
4499 err_printf(&fctx, "%s", _("encryption not supported."));
4500 goto out;
4501 }
4502 if (ext2fs_has_feature_casefold(global_fs->super)) {
4503 err_printf(&fctx, "%s", _("casefolding not supported."));
4504 goto out;
4505 }
4506
4507 if (ext2fs_has_feature_shared_blocks(global_fs->super))
4508 fctx.ro = 1;
4509
7889640d 4510 if (ext2fs_has_feature_journal_needs_recovery(global_fs->super)) {
75e3a9ef 4511 if (fctx.norecovery) {
cbbf7811 4512 log_printf(&fctx, "%s\n",
ac2ff60c 4513 _("Mounting read-only without recovering journal."));
38753809
DW
4514 fctx.ro = 1;
4515 global_fs->flags &= ~EXT2_FLAG_RW;
4516 } else {
cbbf7811 4517 log_printf(&fctx, "%s\n", _("Recovering journal."));
81cbf1ef
DW
4518 err = ext2fs_run_ext3_journal(&global_fs);
4519 if (err) {
348d8481
DW
4520 err_printf(&fctx, "%s.\n", error_message(err));
4521 err_printf(&fctx, "%s\n",
4522 _("Please run e2fsck -fy."));
81cbf1ef
DW
4523 goto out;
4524 }
7889640d 4525 ext2fs_clear_feature_journal_needs_recovery(global_fs->super);
81cbf1ef 4526 ext2fs_mark_super_dirty(global_fs);
81cbf1ef
DW
4527 }
4528 }
4529
38753809 4530 if (global_fs->flags & EXT2_FLAG_RW) {
7889640d 4531 if (ext2fs_has_feature_journal(global_fs->super))
ac2ff60c
DW
4532 log_printf(&fctx, "%s",
4533 _("Warning: fuse2fs does not support using the journal.\n"
4534 "There may be file system corruption or data loss if\n"
4535 "the file system is not gracefully unmounted.\n"));
81cbf1ef
DW
4536 err = ext2fs_read_inode_bitmap(global_fs);
4537 if (err) {
4538 translate_error(global_fs, 0, err);
4539 goto out;
4540 }
4541 err = ext2fs_read_block_bitmap(global_fs);
4542 if (err) {
4543 translate_error(global_fs, 0, err);
4544 goto out;
4545 }
4546 }
4547
4548 if (!(global_fs->super->s_state & EXT2_VALID_FS))
348d8481
DW
4549 err_printf(&fctx, "%s\n",
4550 _("Warning: Mounting unchecked fs, running e2fsck is recommended."));
81cbf1ef
DW
4551 if (global_fs->super->s_max_mnt_count > 0 &&
4552 global_fs->super->s_mnt_count >= global_fs->super->s_max_mnt_count)
348d8481
DW
4553 err_printf(&fctx, "%s\n",
4554 _("Warning: Maximal mount count reached, running e2fsck is recommended."));
81cbf1ef 4555 if (global_fs->super->s_checkinterval > 0 &&
377e3a96
TT
4556 (time_t) (global_fs->super->s_lastcheck +
4557 global_fs->super->s_checkinterval) <= time(0))
348d8481
DW
4558 err_printf(&fctx, "%s\n",
4559 _("Warning: Check time reached; running e2fsck is recommended."));
81cbf1ef 4560 if (global_fs->super->s_last_orphan)
348d8481
DW
4561 err_printf(&fctx, "%s\n",
4562 _("Orphans detected; running e2fsck is recommended."));
81cbf1ef
DW
4563
4564 if (global_fs->super->s_state & EXT2_ERROR_FS) {
348d8481
DW
4565 err_printf(&fctx, "%s\n",
4566 _("Errors detected; running e2fsck is required."));
81cbf1ef
DW
4567 goto out;
4568 }
4569
4570 /* Initialize generation counter */
69df8496 4571 get_random_bytes(&fctx.next_generation, sizeof(unsigned int));
81cbf1ef 4572
69df8496 4573 /* Set up default fuse parameters */
13e365bb 4574 snprintf(extra_args, BUFSIZ, "-okernel_cache,subtype=%s,"
69df8496 4575 "fsname=%s,attr_timeout=0" FUSE_PLATFORM_OPTS,
13e365bb 4576 get_subtype(argv[0]),
4dda2315 4577 fctx.device);
69df8496
TT
4578 if (fctx.no_default_opts == 0)
4579 fuse_opt_add_arg(&args, extra_args);
4580
38753809
DW
4581 if (fctx.ro)
4582 fuse_opt_add_arg(&args, "-oro");
4583
ea1a3fa2
NC
4584 if (fctx.fakeroot) {
4585#ifdef HAVE_MOUNT_NODEV
4586 fuse_opt_add_arg(&args,"-onodev");
4587#endif
4588#ifdef HAVE_MOUNT_NOSUID
4589 fuse_opt_add_arg(&args,"-onosuid");
4590#endif
4591 }
4592
8646830d
DW
4593 if (fctx.kernel) {
4594 /*
4595 * ACLs are always enforced when kernel mode is enabled, to
4596 * match the kernel ext4 driver which always enables ACLs.
4597 */
4598 fctx.acl = 1;
667ea124
DW
4599 fuse_opt_insert_arg(&args, 1,
4600 "-oallow_other,default_permissions,suid,dev");
8646830d 4601 }
667ea124 4602
69df8496
TT
4603 if (fctx.debug) {
4604 int i;
4605
c4d34d7a 4606 printf("FUSE2FS (%s): fuse arguments:", fctx.shortdev);
69df8496
TT
4607 for (i = 0; i < args.argc; i++)
4608 printf(" '%s'", args.argv[i]);
4609 printf("\n");
c4d34d7a 4610 fflush(stdout);
69df8496 4611 }
81cbf1ef 4612
69df8496 4613 pthread_mutex_init(&fctx.bfl, NULL);
ce89945a 4614 ret = fuse_main(args.argc, args.argv, &fs_ops, &fctx);
69df8496 4615 pthread_mutex_destroy(&fctx.bfl);
81cbf1ef 4616
ce89945a
DW
4617 switch(ret) {
4618 case 0:
4619 /* success */
4620 ret = 0;
4621 break;
4622 case 1:
4623 case 2:
4624 /* invalid option or no mountpoint */
4625 ret = 1;
4626 break;
4627 case 3:
4628 case 4:
4629 case 5:
4630 case 6:
4631 case 7:
4632 /* setup or mounting failed */
4633 ret = 32;
4634 break;
4635 default:
4636 /* fuse started up enough to call op_init */
4637 ret = 0;
4638 break;
4639 }
81cbf1ef 4640out:
ce89945a
DW
4641 if (ret & 1) {
4642 fprintf(orig_stderr, "%s\n",
4643 _("Mount failed due to unrecognized options. Check dmesg(1) for details."));
4644 fflush(orig_stderr);
4645 }
4646 if (ret & 32) {
4647 fprintf(orig_stderr, "%s\n",
4648 _("Mount failed while opening filesystem. Check dmesg(1) for details."));
4649 fflush(orig_stderr);
4650 }
6ae16a68
TT
4651 if (global_fs) {
4652 err = ext2fs_close(global_fs);
4653 if (err)
4654 com_err(argv[0], err, "while closing fs");
4655 global_fs = NULL;
4656 }
e50fbaa4
DW
4657 if (fctx.lockfile) {
4658 if (unlink(fctx.lockfile)) {
4659 err = errno;
4660 err_printf(&fctx, "%s: %s: %s\n", fctx.lockfile,
4661 _("removing lockfile failed"),
4662 strerror(err));
4663 }
e83c0df0 4664 free(fctx.lockfile);
e50fbaa4 4665 }
7c08dcb5
DW
4666 if (fctx.device)
4667 free(fctx.device);
4668 fuse_opt_free_args(&args);
81cbf1ef
DW
4669 return ret;
4670}
4671
bb2a4e6c 4672static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err,
81cbf1ef
DW
4673 const char *file, int line)
4674{
4675 struct timespec now;
4676 int ret = err;
4677 struct fuse2fs *ff = fs->priv_data;
4678 int is_err = 0;
4679
4680 /* Translate ext2 error to unix error code */
81cbf1ef 4681 switch (err) {
71f046a7
TT
4682 case 0:
4683 break;
81cbf1ef
DW
4684 case EXT2_ET_NO_MEMORY:
4685 case EXT2_ET_TDB_ERR_OOM:
4686 ret = -ENOMEM;
4687 break;
4688 case EXT2_ET_INVALID_ARGUMENT:
4689 case EXT2_ET_LLSEEK_FAILED:
4690 ret = -EINVAL;
4691 break;
4692 case EXT2_ET_NO_DIRECTORY:
4693 ret = -ENOTDIR;
4694 break;
4695 case EXT2_ET_FILE_NOT_FOUND:
4696 ret = -ENOENT;
4697 break;
4698 case EXT2_ET_DIR_NO_SPACE:
4699 is_err = 1;
da9d5b80 4700 /* fallthrough */
81cbf1ef
DW
4701 case EXT2_ET_TOOSMALL:
4702 case EXT2_ET_BLOCK_ALLOC_FAIL:
4703 case EXT2_ET_INODE_ALLOC_FAIL:
4704 case EXT2_ET_EA_NO_SPACE:
4705 ret = -ENOSPC;
4706 break;
4707 case EXT2_ET_SYMLINK_LOOP:
4708 ret = -EMLINK;
4709 break;
4710 case EXT2_ET_FILE_TOO_BIG:
4711 ret = -EFBIG;
4712 break;
4713 case EXT2_ET_TDB_ERR_EXISTS:
4714 case EXT2_ET_FILE_EXISTS:
4715 ret = -EEXIST;
4716 break;
4717 case EXT2_ET_MMP_FAILED:
4718 case EXT2_ET_MMP_FSCK_ON:
4719 ret = -EBUSY;
4720 break;
4721 case EXT2_ET_EA_KEY_NOT_FOUND:
81cbf1ef 4722 ret = -ENODATA;
81cbf1ef
DW
4723 break;
4724 /* Sometimes fuse returns a garbage file handle pointer to us... */
4725 case EXT2_ET_MAGIC_EXT2_FILE:
4726 ret = -EFAULT;
4727 break;
4728 case EXT2_ET_UNIMPLEMENTED:
4729 ret = -EOPNOTSUPP;
4730 break;
3ec4cd88
DW
4731 case EXT2_ET_MAGIC_EXT2FS_FILSYS:
4732 case EXT2_ET_MAGIC_BADBLOCKS_LIST:
4733 case EXT2_ET_MAGIC_BADBLOCKS_ITERATE:
4734 case EXT2_ET_MAGIC_INODE_SCAN:
4735 case EXT2_ET_MAGIC_IO_CHANNEL:
4736 case EXT2_ET_MAGIC_UNIX_IO_CHANNEL:
4737 case EXT2_ET_MAGIC_IO_MANAGER:
4738 case EXT2_ET_MAGIC_BLOCK_BITMAP:
4739 case EXT2_ET_MAGIC_INODE_BITMAP:
4740 case EXT2_ET_MAGIC_GENERIC_BITMAP:
4741 case EXT2_ET_MAGIC_TEST_IO_CHANNEL:
4742 case EXT2_ET_MAGIC_DBLIST:
4743 case EXT2_ET_MAGIC_ICOUNT:
4744 case EXT2_ET_MAGIC_PQ_IO_CHANNEL:
4745 case EXT2_ET_MAGIC_E2IMAGE:
4746 case EXT2_ET_MAGIC_INODE_IO_CHANNEL:
4747 case EXT2_ET_MAGIC_EXTENT_HANDLE:
4748 case EXT2_ET_BAD_MAGIC:
4749 case EXT2_ET_MAGIC_EXTENT_PATH:
4750 case EXT2_ET_MAGIC_GENERIC_BITMAP64:
4751 case EXT2_ET_MAGIC_BLOCK_BITMAP64:
4752 case EXT2_ET_MAGIC_INODE_BITMAP64:
4753 case EXT2_ET_MAGIC_RESERVED_13:
4754 case EXT2_ET_MAGIC_RESERVED_14:
4755 case EXT2_ET_MAGIC_RESERVED_15:
4756 case EXT2_ET_MAGIC_RESERVED_16:
4757 case EXT2_ET_MAGIC_RESERVED_17:
4758 case EXT2_ET_MAGIC_RESERVED_18:
4759 case EXT2_ET_MAGIC_RESERVED_19:
4760 case EXT2_ET_MMP_MAGIC_INVALID:
4761 case EXT2_ET_MAGIC_EA_HANDLE:
dca02019
DW
4762 case EXT2_ET_DIR_CORRUPTED:
4763 case EXT2_ET_CORRUPT_SUPERBLOCK:
4764 case EXT2_ET_RESIZE_INODE_CORRUPT:
4765 case EXT2_ET_TDB_ERR_CORRUPT:
4766 case EXT2_ET_UNDO_FILE_CORRUPT:
4767 case EXT2_ET_FILESYSTEM_CORRUPTED:
4768 case EXT2_ET_CORRUPT_JOURNAL_SB:
4769 case EXT2_ET_INODE_CORRUPTED:
4770 case EXT2_ET_EA_INODE_CORRUPTED:
4771 /* same errno that linux uses */
4772 is_err = 1;
4773 ret = -EUCLEAN;
4774 break;
81cbf1ef
DW
4775 default:
4776 is_err = 1;
71f046a7 4777 ret = (err < 256) ? -err : -EIO;
81cbf1ef
DW
4778 break;
4779 }
4780
81cbf1ef
DW
4781 if (!is_err)
4782 return ret;
4783
4784 if (ino)
5cdebf3e 4785 err_printf(ff, "%s (inode #%d) at %s:%d.\n",
81cbf1ef
DW
4786 error_message(err), ino, file, line);
4787 else
5cdebf3e 4788 err_printf(ff, "%s at %s:%d.\n",
81cbf1ef 4789 error_message(err), file, line);
81cbf1ef
DW
4790
4791 /* Make a note in the error log */
4792 get_now(&now);
ca8bc924 4793 ext2fs_set_tstamp(fs->super, s_last_error_time, now.tv_sec);
81cbf1ef
DW
4794 fs->super->s_last_error_ino = ino;
4795 fs->super->s_last_error_line = line;
4796 fs->super->s_last_error_block = err; /* Yeah... */
4797 strncpy((char *)fs->super->s_last_error_func, file,
4798 sizeof(fs->super->s_last_error_func));
ca8bc924
AD
4799 if (ext2fs_get_tstamp(fs->super, s_first_error_time) == 0) {
4800 ext2fs_set_tstamp(fs->super, s_first_error_time, now.tv_sec);
81cbf1ef
DW
4801 fs->super->s_first_error_ino = ino;
4802 fs->super->s_first_error_line = line;
4803 fs->super->s_first_error_block = err;
4804 strncpy((char *)fs->super->s_first_error_func, file,
4805 sizeof(fs->super->s_first_error_func));
4806 }
4807
4808 fs->super->s_error_count++;
4809 ext2fs_mark_super_dirty(fs);
4810 ext2fs_flush(fs);
4811 if (ff->panic_on_error)
4812 abort();
4813
4814 return ret;
4815}