]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/fuse2fs.c
libext2fs: add ea_inode support to set xattr
[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 */
11#define _FILE_OFFSET_BITS 64
12#define FUSE_USE_VERSION 29
4e222d9b 13#ifndef _GNU_SOURCE
81cbf1ef 14#define _GNU_SOURCE
4e222d9b 15#endif
81cbf1ef
DW
16#include "config.h"
17#include <pthread.h>
18#ifdef __linux__
19# include <linux/fs.h>
20# include <linux/falloc.h>
21# include <linux/xattr.h>
22# define FUSE_PLATFORM_OPTS ",nonempty,big_writes"
23# ifdef HAVE_SYS_ACL_H
24# define TRANSLATE_LINUX_ACLS
25# endif
26#else
27# define FUSE_PLATFORM_OPTS ""
28#endif
29#ifdef TRANSLATE_LINUX_ACLS
30# include <sys/acl.h>
31#endif
32#include <sys/ioctl.h>
33#include <unistd.h>
34#include <fuse.h>
35#include <inttypes.h>
36#include "ext2fs/ext2fs.h"
37#include "ext2fs/ext2_fs.h"
38
69df8496
TT
39#include "../version.h"
40
81cbf1ef
DW
41#ifdef ENABLE_NLS
42#include <libintl.h>
43#include <locale.h>
44#define _(a) (gettext(a))
45#ifdef gettext_noop
46#define N_(a) gettext_noop(a)
47#else
48#define N_(a) (a)
49#endif
50#define P_(singular, plural, n) (ngettext(singular, plural, n))
51#ifndef NLS_CAT_NAME
52#define NLS_CAT_NAME "e2fsprogs"
53#endif
54#ifndef LOCALEDIR
55#define LOCALEDIR "/usr/share/locale"
56#endif
57#else
58#define _(a) (a)
59#define N_(a) a
60#define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural))
61#endif
62
63static ext2_filsys global_fs; /* Try not to use this directly */
64
65#undef DEBUG
66
67#ifdef DEBUG
68# define dbg_printf(f, a...) do {printf("FUSE2FS-" f, ## a); \
69 fflush(stdout); \
70} while (0)
71#else
72# define dbg_printf(f, a...)
73#endif
74
75#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
76# ifdef _IOR
77# ifdef _IOW
78# define SUPPORT_I_FLAGS
79# endif
80# endif
81#endif
82
83#ifdef FALLOC_FL_KEEP_SIZE
84# define FL_KEEP_SIZE_FLAG FALLOC_FL_KEEP_SIZE
85# define SUPPORT_FALLOCATE
86#else
87# define FL_KEEP_SIZE_FLAG (0)
88#endif
89
90#ifdef FALLOC_FL_PUNCH_HOLE
91# define FL_PUNCH_HOLE_FLAG FALLOC_FL_PUNCH_HOLE
92#else
93# define FL_PUNCH_HOLE_FLAG (0)
94#endif
95
96errcode_t ext2fs_run_ext3_journal(ext2_filsys *fs);
97
1fc23b5e
TT
98#ifdef CONFIG_JBD_DEBUG /* Enabled by configure --enable-jbd-debug */
99int journal_enable_debug = -1;
100#endif
101
81cbf1ef
DW
102/* ACL translation stuff */
103#ifdef TRANSLATE_LINUX_ACLS
104/*
105 * Copied from acl_ea.h in libacl source; ACLs have to be sent to and from fuse
106 * in this format... at least on Linux.
107 */
108#define ACL_EA_ACCESS "system.posix_acl_access"
109#define ACL_EA_DEFAULT "system.posix_acl_default"
110
111#define ACL_EA_VERSION 0x0002
112
113typedef struct {
114 u_int16_t e_tag;
115 u_int16_t e_perm;
116 u_int32_t e_id;
117} acl_ea_entry;
118
119typedef struct {
120 u_int32_t a_version;
121 acl_ea_entry a_entries[0];
122} acl_ea_header;
123
124static inline size_t acl_ea_size(int count)
125{
126 return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry);
127}
128
129static inline int acl_ea_count(size_t size)
130{
131 if (size < sizeof(acl_ea_header))
132 return -1;
133 size -= sizeof(acl_ea_header);
134 if (size % sizeof(acl_ea_entry))
135 return -1;
136 return size / sizeof(acl_ea_entry);
137}
138
139/*
140 * ext4 ACL structures, copied from fs/ext4/acl.h.
141 */
142#define EXT4_ACL_VERSION 0x0001
143
144typedef struct {
145 __u16 e_tag;
146 __u16 e_perm;
147 __u32 e_id;
148} ext4_acl_entry;
149
150typedef struct {
151 __u16 e_tag;
152 __u16 e_perm;
153} ext4_acl_entry_short;
154
155typedef struct {
156 __u32 a_version;
157} ext4_acl_header;
158
159static inline size_t ext4_acl_size(int count)
160{
161 if (count <= 4) {
162 return sizeof(ext4_acl_header) +
163 count * sizeof(ext4_acl_entry_short);
164 } else {
165 return sizeof(ext4_acl_header) +
166 4 * sizeof(ext4_acl_entry_short) +
167 (count - 4) * sizeof(ext4_acl_entry);
168 }
169}
170
171static inline int ext4_acl_count(size_t size)
172{
173 ssize_t s;
174
175 size -= sizeof(ext4_acl_header);
176 s = size - 4 * sizeof(ext4_acl_entry_short);
177 if (s < 0) {
178 if (size % sizeof(ext4_acl_entry_short))
179 return -1;
180 return size / sizeof(ext4_acl_entry_short);
181 }
182 if (s % sizeof(ext4_acl_entry))
183 return -1;
184 return s / sizeof(ext4_acl_entry) + 4;
185}
186
187static errcode_t fuse_to_ext4_acl(acl_ea_header *facl, size_t facl_sz,
188 ext4_acl_header **eacl, size_t *eacl_sz)
189{
190 int i, facl_count;
191 ext4_acl_header *h;
192 size_t h_sz;
193 ext4_acl_entry *e;
194 acl_ea_entry *a;
4e222d9b 195 unsigned char *hptr;
81cbf1ef
DW
196 errcode_t err;
197
198 facl_count = acl_ea_count(facl_sz);
199 h_sz = ext4_acl_size(facl_count);
200 if (facl_count < 0 || facl->a_version != ACL_EA_VERSION)
201 return EXT2_ET_INVALID_ARGUMENT;
202
203 err = ext2fs_get_mem(h_sz, &h);
204 if (err)
205 return err;
206
207 h->a_version = ext2fs_cpu_to_le32(EXT4_ACL_VERSION);
4e222d9b 208 hptr = (unsigned char *) (h + 1);
81cbf1ef 209 for (i = 0, a = facl->a_entries; i < facl_count; i++, a++) {
4e222d9b 210 e = (ext4_acl_entry *) hptr;
81cbf1ef
DW
211 e->e_tag = ext2fs_cpu_to_le16(a->e_tag);
212 e->e_perm = ext2fs_cpu_to_le16(a->e_perm);
213
214 switch (a->e_tag) {
215 case ACL_USER:
216 case ACL_GROUP:
217 e->e_id = ext2fs_cpu_to_le32(a->e_id);
218 hptr += sizeof(ext4_acl_entry);
219 break;
220 case ACL_USER_OBJ:
221 case ACL_GROUP_OBJ:
222 case ACL_MASK:
223 case ACL_OTHER:
224 hptr += sizeof(ext4_acl_entry_short);
225 break;
226 default:
227 err = EXT2_ET_INVALID_ARGUMENT;
228 goto out;
229 }
230 }
231
232 *eacl = h;
233 *eacl_sz = h_sz;
234 return err;
235out:
236 ext2fs_free_mem(&h);
237 return err;
238}
239
240static errcode_t ext4_to_fuse_acl(acl_ea_header **facl, size_t *facl_sz,
241 ext4_acl_header *eacl, size_t eacl_sz)
242{
243 int i, eacl_count;
244 acl_ea_header *f;
245 ext4_acl_entry *e;
246 acl_ea_entry *a;
247 size_t f_sz;
4e222d9b 248 unsigned char *hptr;
81cbf1ef
DW
249 errcode_t err;
250
251 eacl_count = ext4_acl_count(eacl_sz);
252 f_sz = acl_ea_size(eacl_count);
253 if (eacl_count < 0 ||
254 eacl->a_version != ext2fs_cpu_to_le32(EXT4_ACL_VERSION))
255 return EXT2_ET_INVALID_ARGUMENT;
256
257 err = ext2fs_get_mem(f_sz, &f);
258 if (err)
259 return err;
260
261 f->a_version = ACL_EA_VERSION;
4e222d9b 262 hptr = (unsigned char *) (eacl + 1);
81cbf1ef 263 for (i = 0, a = f->a_entries; i < eacl_count; i++, a++) {
4e222d9b 264 e = (ext4_acl_entry *) hptr;
81cbf1ef
DW
265 a->e_tag = ext2fs_le16_to_cpu(e->e_tag);
266 a->e_perm = ext2fs_le16_to_cpu(e->e_perm);
267
268 switch (a->e_tag) {
269 case ACL_USER:
270 case ACL_GROUP:
271 a->e_id = ext2fs_le32_to_cpu(e->e_id);
272 hptr += sizeof(ext4_acl_entry);
273 break;
274 case ACL_USER_OBJ:
275 case ACL_GROUP_OBJ:
276 case ACL_MASK:
277 case ACL_OTHER:
278 hptr += sizeof(ext4_acl_entry_short);
279 break;
280 default:
281 err = EXT2_ET_INVALID_ARGUMENT;
282 goto out;
283 }
284 }
285
286 *facl = f;
287 *facl_sz = f_sz;
288 return err;
289out:
290 ext2fs_free_mem(&f);
291 return err;
292}
293#endif /* TRANSLATE_LINUX_ACLS */
294
295/*
296 * ext2_file_t contains a struct inode, so we can't leave files open.
297 * Use this as a proxy instead.
298 */
299#define FUSE2FS_FILE_MAGIC (0xEF53DEAFUL)
300struct fuse2fs_file_handle {
301 unsigned long magic;
302 ext2_ino_t ino;
303 int open_flags;
304};
305
306/* Main program context */
307#define FUSE2FS_MAGIC (0xEF53DEADUL)
308struct fuse2fs {
309 unsigned long magic;
310 ext2_filsys fs;
311 pthread_mutex_t bfl;
69df8496
TT
312 char *device;
313 int ro;
314 int debug;
315 int no_default_opts;
81cbf1ef
DW
316 int panic_on_error;
317 int minixdf;
318 int alloc_all_blocks;
319 FILE *err_fp;
320 unsigned int next_generation;
321};
322
323#define FUSE2FS_CHECK_MAGIC(fs, ptr, num) do {if ((ptr)->magic != (num)) \
324 return translate_error((fs), 0, EXT2_ET_MAGIC_EXT2_FILE); \
325} while (0)
326
327#define FUSE2FS_CHECK_CONTEXT(ptr) do {if ((ptr)->magic != FUSE2FS_MAGIC) \
328 return translate_error(global_fs, 0, EXT2_ET_BAD_MAGIC); \
329} while (0)
330
331static int __translate_error(ext2_filsys fs, errcode_t err, ext2_ino_t ino,
332 const char *file, int line);
333#define translate_error(fs, ino, err) __translate_error((fs), (err), (ino), \
334 __FILE__, __LINE__)
335
336/* for macosx */
337#ifndef W_OK
338# define W_OK 2
339#endif
340
341#ifndef R_OK
342# define R_OK 4
343#endif
344
345#define EXT4_EPOCH_BITS 2
346#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
347#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS)
348
349/*
350 * Extended fields will fit into an inode if the filesystem was formatted
351 * with large inodes (-I 256 or larger) and there are not currently any EAs
352 * consuming all of the available space. For new inodes we always reserve
353 * enough space for the kernel's known extended fields, but for inodes
354 * created with an old kernel this might not have been the case. None of
355 * the extended inode fields is critical for correct filesystem operation.
356 * This macro checks if a certain field fits in the inode. Note that
357 * inode-size = GOOD_OLD_INODE_SIZE + i_extra_isize
358 */
359#define EXT4_FITS_IN_INODE(ext4_inode, field) \
360 ((offsetof(typeof(*ext4_inode), field) + \
361 sizeof((ext4_inode)->field)) \
4e222d9b 362 <= ((size_t) EXT2_GOOD_OLD_INODE_SIZE + \
81cbf1ef
DW
363 (ext4_inode)->i_extra_isize)) \
364
365static inline __u32 ext4_encode_extra_time(const struct timespec *time)
366{
367 __u32 extra = sizeof(time->tv_sec) > 4 ?
368 ((time->tv_sec - (__s32)time->tv_sec) >> 32) &
369 EXT4_EPOCH_MASK : 0;
370 return extra | (time->tv_nsec << EXT4_EPOCH_BITS);
371}
372
373static inline void ext4_decode_extra_time(struct timespec *time, __u32 extra)
374{
375 if (sizeof(time->tv_sec) > 4 && (extra & EXT4_EPOCH_MASK)) {
376 __u64 extra_bits = extra & EXT4_EPOCH_MASK;
377 /*
378 * Prior to kernel 3.14?, we had a broken decode function,
379 * wherein we effectively did this:
380 * if (extra_bits == 3)
381 * extra_bits = 0;
382 */
383 time->tv_sec += extra_bits << 32;
384 }
385 time->tv_nsec = ((extra) & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS;
386}
387
388#define EXT4_INODE_SET_XTIME(xtime, timespec, raw_inode) \
389do { \
390 (raw_inode)->xtime = (timespec)->tv_sec; \
391 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \
392 (raw_inode)->xtime ## _extra = \
393 ext4_encode_extra_time(timespec); \
394} while (0)
395
396#define EXT4_EINODE_SET_XTIME(xtime, timespec, raw_inode) \
397do { \
398 if (EXT4_FITS_IN_INODE(raw_inode, xtime)) \
399 (raw_inode)->xtime = (timespec)->tv_sec; \
400 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \
401 (raw_inode)->xtime ## _extra = \
402 ext4_encode_extra_time(timespec); \
403} while (0)
404
405#define EXT4_INODE_GET_XTIME(xtime, timespec, raw_inode) \
406do { \
407 (timespec)->tv_sec = (signed)((raw_inode)->xtime); \
408 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \
409 ext4_decode_extra_time((timespec), \
410 (raw_inode)->xtime ## _extra); \
411 else \
412 (timespec)->tv_nsec = 0; \
413} while (0)
414
415#define EXT4_EINODE_GET_XTIME(xtime, timespec, raw_inode) \
416do { \
417 if (EXT4_FITS_IN_INODE(raw_inode, xtime)) \
418 (timespec)->tv_sec = \
419 (signed)((raw_inode)->xtime); \
420 if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \
421 ext4_decode_extra_time((timespec), \
422 raw_inode->xtime ## _extra); \
423 else \
424 (timespec)->tv_nsec = 0; \
425} while (0)
426
427static void get_now(struct timespec *now)
428{
429#ifdef CLOCK_REALTIME
430 if (!clock_gettime(CLOCK_REALTIME, now))
431 return;
432#endif
433
434 now->tv_sec = time(NULL);
435 now->tv_nsec = 0;
436}
437
438static void increment_version(struct ext2_inode_large *inode)
439{
440 __u64 ver;
441
442 ver = inode->osd1.linux1.l_i_version;
443 if (EXT4_FITS_IN_INODE(inode, i_version_hi))
444 ver |= (__u64)inode->i_version_hi << 32;
445 ver++;
446 inode->osd1.linux1.l_i_version = ver;
447 if (EXT4_FITS_IN_INODE(inode, i_version_hi))
448 inode->i_version_hi = ver >> 32;
449}
450
451static void init_times(struct ext2_inode_large *inode)
452{
453 struct timespec now;
454
455 get_now(&now);
456 EXT4_INODE_SET_XTIME(i_atime, &now, inode);
457 EXT4_INODE_SET_XTIME(i_ctime, &now, inode);
458 EXT4_INODE_SET_XTIME(i_mtime, &now, inode);
459 EXT4_EINODE_SET_XTIME(i_crtime, &now, inode);
460 increment_version(inode);
461}
462
463static int update_ctime(ext2_filsys fs, ext2_ino_t ino,
464 struct ext2_inode_large *pinode)
465{
466 errcode_t err;
467 struct timespec now;
468 struct ext2_inode_large inode;
469
470 get_now(&now);
471
472 /* If user already has a inode buffer, just update that */
473 if (pinode) {
474 increment_version(pinode);
475 EXT4_INODE_SET_XTIME(i_ctime, &now, pinode);
476 return 0;
477 }
478
479 /* Otherwise we have to read-modify-write the inode */
480 memset(&inode, 0, sizeof(inode));
481 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
482 sizeof(inode));
483 if (err)
484 return translate_error(fs, ino, err);
485
486 increment_version(&inode);
487 EXT4_INODE_SET_XTIME(i_ctime, &now, &inode);
488
489 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
490 sizeof(inode));
491 if (err)
492 return translate_error(fs, ino, err);
493
494 return 0;
495}
496
497static int update_atime(ext2_filsys fs, ext2_ino_t ino)
498{
499 errcode_t err;
500 struct ext2_inode_large inode, *pinode;
501 struct timespec atime, mtime, now;
502
503 if (!(fs->flags & EXT2_FLAG_RW))
504 return 0;
505 memset(&inode, 0, sizeof(inode));
506 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
507 sizeof(inode));
508 if (err)
509 return translate_error(fs, ino, err);
510
511 pinode = &inode;
512 EXT4_INODE_GET_XTIME(i_atime, &atime, pinode);
513 EXT4_INODE_GET_XTIME(i_mtime, &mtime, pinode);
514 get_now(&now);
515 /*
516 * If atime is newer than mtime and atime hasn't been updated in thirty
517 * seconds, skip the atime update. Same idea as Linux "relatime".
518 */
519 if (atime.tv_sec >= mtime.tv_sec && atime.tv_sec >= now.tv_sec - 30)
520 return 0;
521 EXT4_INODE_SET_XTIME(i_atime, &now, &inode);
522
523 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
524 sizeof(inode));
525 if (err)
526 return translate_error(fs, ino, err);
527
528 return 0;
529}
530
531static int update_mtime(ext2_filsys fs, ext2_ino_t ino,
532 struct ext2_inode_large *pinode)
533{
534 errcode_t err;
535 struct ext2_inode_large inode;
536 struct timespec now;
537
538 if (pinode) {
539 get_now(&now);
540 EXT4_INODE_SET_XTIME(i_mtime, &now, pinode);
541 EXT4_INODE_SET_XTIME(i_ctime, &now, pinode);
542 increment_version(pinode);
543 return 0;
544 }
545
546 memset(&inode, 0, sizeof(inode));
547 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
548 sizeof(inode));
549 if (err)
550 return translate_error(fs, ino, err);
551
552 get_now(&now);
553 EXT4_INODE_SET_XTIME(i_mtime, &now, &inode);
554 EXT4_INODE_SET_XTIME(i_ctime, &now, &inode);
555 increment_version(&inode);
556
557 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
558 sizeof(inode));
559 if (err)
560 return translate_error(fs, ino, err);
561
562 return 0;
563}
564
565static int ext2_file_type(unsigned int mode)
566{
567 if (LINUX_S_ISREG(mode))
568 return EXT2_FT_REG_FILE;
569
570 if (LINUX_S_ISDIR(mode))
571 return EXT2_FT_DIR;
572
573 if (LINUX_S_ISCHR(mode))
574 return EXT2_FT_CHRDEV;
575
576 if (LINUX_S_ISBLK(mode))
577 return EXT2_FT_BLKDEV;
578
579 if (LINUX_S_ISLNK(mode))
580 return EXT2_FT_SYMLINK;
581
582 if (LINUX_S_ISFIFO(mode))
583 return EXT2_FT_FIFO;
584
585 if (LINUX_S_ISSOCK(mode))
586 return EXT2_FT_SOCK;
587
588 return 0;
589}
590
591static int fs_can_allocate(struct fuse2fs *ff, blk64_t num)
592{
593 ext2_filsys fs = ff->fs;
594 blk64_t reserved;
595
596 dbg_printf("%s: Asking for %llu; alloc_all=%d total=%llu free=%llu "
597 "rsvd=%llu\n", __func__, num, ff->alloc_all_blocks,
598 ext2fs_blocks_count(fs->super),
599 ext2fs_free_blocks_count(fs->super),
600 ext2fs_r_blocks_count(fs->super));
601 if (num > ext2fs_blocks_count(fs->super))
602 return 0;
603
604 if (ff->alloc_all_blocks)
605 return 1;
606
607 /*
608 * Different meaning for r_blocks -- libext2fs has bugs where the FS
609 * can get corrupted if it totally runs out of blocks. Avoid this
610 * by refusing to allocate any of the reserve blocks to anybody.
611 */
612 reserved = ext2fs_r_blocks_count(fs->super);
613 if (reserved == 0)
614 reserved = ext2fs_blocks_count(fs->super) / 10;
615 return ext2fs_free_blocks_count(fs->super) > reserved + num;
616}
617
618static int fs_writeable(ext2_filsys fs)
619{
620 return (fs->flags & EXT2_FLAG_RW) && (fs->super->s_error_count == 0);
621}
622
4e222d9b 623static int check_inum_access(ext2_filsys fs, ext2_ino_t ino, mode_t mask)
81cbf1ef
DW
624{
625 struct fuse_context *ctxt = fuse_get_context();
626 struct ext2_inode inode;
627 mode_t perms;
628 errcode_t err;
629
630 /* no writing to read-only or broken fs */
631 if ((mask & W_OK) && !fs_writeable(fs))
632 return -EROFS;
633
634 err = ext2fs_read_inode(fs, ino, &inode);
635 if (err)
636 return translate_error(fs, ino, err);
637 perms = inode.i_mode & 0777;
638
639 dbg_printf("access ino=%d mask=e%s%s%s perms=0%o fuid=%d fgid=%d "
640 "uid=%d gid=%d\n", ino,
641 (mask & R_OK ? "r" : ""), (mask & W_OK ? "w" : ""),
642 (mask & X_OK ? "x" : ""), perms, inode.i_uid, inode.i_gid,
643 ctxt->uid, ctxt->gid);
644
645 /* existence check */
646 if (mask == 0)
647 return 0;
648
649 /* is immutable? */
650 if ((mask & W_OK) &&
651 (inode.i_flags & EXT2_IMMUTABLE_FL))
652 return -EACCES;
653
654 /* Figure out what root's allowed to do */
655 if (ctxt->uid == 0) {
656 /* Non-file access always ok */
657 if (!LINUX_S_ISREG(inode.i_mode))
658 return 0;
659
660 /* R/W access to a file always ok */
661 if (!(mask & X_OK))
662 return 0;
663
664 /* X access to a file ok if a user/group/other can X */
665 if (perms & 0111)
666 return 0;
667
668 /* Trying to execute a file that's not executable. BZZT! */
669 return -EACCES;
670 }
671
672 /* allow owner, if perms match */
673 if (inode.i_uid == ctxt->uid) {
674 if ((mask & (perms >> 6)) == mask)
675 return 0;
676 return -EACCES;
677 }
678
679 /* allow group, if perms match */
680 if (inode.i_gid == ctxt->gid) {
681 if ((mask & (perms >> 3)) == mask)
682 return 0;
683 return -EACCES;
684 }
685
686 /* otherwise check other */
687 if ((mask & perms) == mask)
688 return 0;
689 return -EACCES;
690}
691
4e222d9b 692static void op_destroy(void *p EXT2FS_ATTR((unused)))
81cbf1ef
DW
693{
694 struct fuse_context *ctxt = fuse_get_context();
695 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
696 ext2_filsys fs;
697 errcode_t err;
698
699 if (ff->magic != FUSE2FS_MAGIC) {
700 translate_error(global_fs, 0, EXT2_ET_BAD_MAGIC);
701 return;
702 }
703 fs = ff->fs;
704 dbg_printf("%s: dev=%s\n", __func__, fs->device_name);
705 if (fs->flags & EXT2_FLAG_RW) {
706 fs->super->s_state |= EXT2_VALID_FS;
707 if (fs->super->s_error_count)
708 fs->super->s_state |= EXT2_ERROR_FS;
709 ext2fs_mark_super_dirty(fs);
710 err = ext2fs_set_gdt_csum(fs);
711 if (err)
712 translate_error(fs, 0, err);
713
714 err = ext2fs_flush2(fs, 0);
715 if (err)
716 translate_error(fs, 0, err);
717 }
718}
719
720static void *op_init(struct fuse_conn_info *conn)
721{
722 struct fuse_context *ctxt = fuse_get_context();
723 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
724 ext2_filsys fs;
725 errcode_t err;
726
727 if (ff->magic != FUSE2FS_MAGIC) {
728 translate_error(global_fs, 0, EXT2_ET_BAD_MAGIC);
729 return NULL;
730 }
731 fs = ff->fs;
732 dbg_printf("%s: dev=%s\n", __func__, fs->device_name);
733#ifdef FUSE_CAP_IOCTL_DIR
734 conn->want |= FUSE_CAP_IOCTL_DIR;
735#endif
736 if (fs->flags & EXT2_FLAG_RW) {
737 fs->super->s_mnt_count++;
738 fs->super->s_mtime = time(NULL);
739 fs->super->s_state &= ~EXT2_VALID_FS;
740 ext2fs_mark_super_dirty(fs);
741 err = ext2fs_flush2(fs, 0);
742 if (err)
743 translate_error(fs, 0, err);
744 }
745 return ff;
746}
747
748static blkcnt_t blocks_from_inode(ext2_filsys fs,
749 struct ext2_inode_large *inode)
750{
751 blkcnt_t b;
752
753 b = inode->i_blocks;
7889640d 754 if (ext2fs_has_feature_huge_file(fs->super))
81cbf1ef
DW
755 b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32;
756
7889640d 757 if (!ext2fs_has_feature_huge_file(fs->super) ||
81cbf1ef
DW
758 !(inode->i_flags & EXT4_HUGE_FILE_FL))
759 b *= fs->blocksize / 512;
760 b *= EXT2FS_CLUSTER_RATIO(fs);
761
762 return b;
763}
764
765static int stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *statbuf)
766{
767 struct ext2_inode_large inode;
768 dev_t fakedev = 0;
769 errcode_t err;
770 int ret = 0;
771 struct timespec tv;
772
773 memset(&inode, 0, sizeof(inode));
774 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
775 sizeof(inode));
776 if (err)
777 return translate_error(fs, ino, err);
778
779 memcpy(&fakedev, fs->super->s_uuid, sizeof(fakedev));
780 statbuf->st_dev = fakedev;
781 statbuf->st_ino = ino;
782 statbuf->st_mode = inode.i_mode;
783 statbuf->st_nlink = inode.i_links_count;
784 statbuf->st_uid = inode.i_uid;
785 statbuf->st_gid = inode.i_gid;
786 statbuf->st_size = EXT2_I_SIZE(&inode);
787 statbuf->st_blksize = fs->blocksize;
788 statbuf->st_blocks = blocks_from_inode(fs, &inode);
789 EXT4_INODE_GET_XTIME(i_atime, &tv, &inode);
790 statbuf->st_atime = tv.tv_sec;
791 EXT4_INODE_GET_XTIME(i_mtime, &tv, &inode);
792 statbuf->st_mtime = tv.tv_sec;
793 EXT4_INODE_GET_XTIME(i_ctime, &tv, &inode);
794 statbuf->st_ctime = tv.tv_sec;
795 if (LINUX_S_ISCHR(inode.i_mode) ||
796 LINUX_S_ISBLK(inode.i_mode)) {
797 if (inode.i_block[0])
798 statbuf->st_rdev = inode.i_block[0];
799 else
800 statbuf->st_rdev = inode.i_block[1];
801 }
802
803 return ret;
804}
805
806static int op_getattr(const char *path, struct stat *statbuf)
807{
808 struct fuse_context *ctxt = fuse_get_context();
809 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
810 ext2_filsys fs;
811 ext2_ino_t ino;
812 errcode_t err;
813 int ret = 0;
814
815 FUSE2FS_CHECK_CONTEXT(ff);
816 fs = ff->fs;
817 dbg_printf("%s: path=%s\n", __func__, path);
818 pthread_mutex_lock(&ff->bfl);
819 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
820 if (err) {
821 ret = translate_error(fs, 0, err);
822 goto out;
823 }
824 ret = stat_inode(fs, ino, statbuf);
825out:
826 pthread_mutex_unlock(&ff->bfl);
827 return ret;
828}
829
830static int op_readlink(const char *path, char *buf, size_t len)
831{
832 struct fuse_context *ctxt = fuse_get_context();
833 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
834 ext2_filsys fs;
835 errcode_t err;
836 ext2_ino_t ino;
837 struct ext2_inode inode;
838 unsigned int got;
839 ext2_file_t file;
840 int ret = 0;
841
842 FUSE2FS_CHECK_CONTEXT(ff);
843 fs = ff->fs;
844 dbg_printf("%s: path=%s\n", __func__, path);
845 pthread_mutex_lock(&ff->bfl);
846 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
847 if (err || ino == 0) {
848 ret = translate_error(fs, 0, err);
849 goto out;
850 }
851
852 err = ext2fs_read_inode(fs, ino, &inode);
853 if (err) {
854 ret = translate_error(fs, ino, err);
855 goto out;
856 }
857
858 if (!LINUX_S_ISLNK(inode.i_mode)) {
859 ret = -EINVAL;
860 goto out;
861 }
862
863 len--;
864 if (inode.i_size < len)
865 len = inode.i_size;
cf0be234
TE
866 if (ext2fs_is_fast_symlink(&inode))
867 memcpy(buf, (char *)inode.i_block, len);
868 else {
81cbf1ef
DW
869 /* big/inline symlink */
870
871 err = ext2fs_file_open(fs, ino, 0, &file);
872 if (err) {
873 ret = translate_error(fs, ino, err);
874 goto out;
875 }
876
877 err = ext2fs_file_read(file, buf, len, &got);
878 if (err || got != len) {
879 ext2fs_file_close(file);
880 ret = translate_error(fs, ino, err);
881 goto out2;
882 }
883
884out2:
885 err = ext2fs_file_close(file);
886 if (ret)
887 goto out;
888 if (err) {
889 ret = translate_error(fs, ino, err);
890 goto out;
891 }
cf0be234 892 }
81cbf1ef
DW
893 buf[len] = 0;
894
895 if (fs_writeable(fs)) {
896 ret = update_atime(fs, ino);
897 if (ret)
898 goto out;
899 }
900
901out:
902 pthread_mutex_unlock(&ff->bfl);
903 return ret;
904}
905
906static int op_mknod(const char *path, mode_t mode, dev_t dev)
907{
908 struct fuse_context *ctxt = fuse_get_context();
909 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
910 ext2_filsys fs;
911 ext2_ino_t parent, child;
da9d5b80 912 char *temp_path;
81cbf1ef
DW
913 errcode_t err;
914 char *node_name, a;
915 int filetype;
916 struct ext2_inode_large inode;
917 int ret = 0;
918
919 FUSE2FS_CHECK_CONTEXT(ff);
920 fs = ff->fs;
921 dbg_printf("%s: path=%s mode=0%o dev=0x%x\n", __func__, path, mode,
922 (unsigned int)dev);
da9d5b80 923 temp_path = strdup(path);
81cbf1ef
DW
924 if (!temp_path) {
925 ret = -ENOMEM;
926 goto out;
927 }
928 node_name = strrchr(temp_path, '/');
929 if (!node_name) {
930 ret = -ENOMEM;
931 goto out;
932 }
933 node_name++;
934 a = *node_name;
935 *node_name = 0;
936
937 pthread_mutex_lock(&ff->bfl);
938 if (!fs_can_allocate(ff, 2)) {
939 ret = -ENOSPC;
940 goto out2;
941 }
942
943 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
944 &parent);
945 if (err) {
946 ret = translate_error(fs, 0, err);
947 goto out2;
948 }
949
950 ret = check_inum_access(fs, parent, W_OK);
951 if (ret)
952 goto out2;
953
954 *node_name = a;
955
956 if (LINUX_S_ISCHR(mode))
957 filetype = EXT2_FT_CHRDEV;
958 else if (LINUX_S_ISBLK(mode))
959 filetype = EXT2_FT_BLKDEV;
960 else if (LINUX_S_ISFIFO(mode))
961 filetype = EXT2_FT_FIFO;
962 else if (LINUX_S_ISSOCK(mode))
963 filetype = EXT2_FT_SOCK;
964 else {
965 ret = -EINVAL;
966 goto out2;
967 }
968
969 err = ext2fs_new_inode(fs, parent, mode, 0, &child);
970 if (err) {
971 ret = translate_error(fs, 0, err);
972 goto out2;
973 }
974
975 dbg_printf("%s: create ino=%d/name=%s in dir=%d\n", __func__, child,
976 node_name, parent);
977 err = ext2fs_link(fs, parent, node_name, child, filetype);
978 if (err == EXT2_ET_DIR_NO_SPACE) {
979 err = ext2fs_expand_dir(fs, parent);
980 if (err) {
981 ret = translate_error(fs, parent, err);
982 goto out2;
983 }
984
985 err = ext2fs_link(fs, parent, node_name, child,
986 filetype);
987 }
988 if (err) {
989 ret = translate_error(fs, parent, err);
990 goto out2;
991 }
992
993 ret = update_mtime(fs, parent, NULL);
994 if (ret)
995 goto out2;
996
997 memset(&inode, 0, sizeof(inode));
998 inode.i_mode = mode;
999
1000 if (dev & ~0xFFFF)
1001 inode.i_block[1] = dev;
1002 else
1003 inode.i_block[0] = dev;
1004 inode.i_links_count = 1;
1005 inode.i_extra_isize = sizeof(struct ext2_inode_large) -
1006 EXT2_GOOD_OLD_INODE_SIZE;
f150bdec
DW
1007 inode.i_uid = ctxt->uid;
1008 inode.i_gid = ctxt->gid;
81cbf1ef
DW
1009
1010 err = ext2fs_write_new_inode(fs, child, (struct ext2_inode *)&inode);
1011 if (err) {
1012 ret = translate_error(fs, child, err);
1013 goto out2;
1014 }
1015
1016 inode.i_generation = ff->next_generation++;
1017 init_times(&inode);
1018 err = ext2fs_write_inode_full(fs, child, (struct ext2_inode *)&inode,
1019 sizeof(inode));
1020 if (err) {
1021 ret = translate_error(fs, child, err);
1022 goto out2;
1023 }
1024
1025 ext2fs_inode_alloc_stats2(fs, child, 1, 0);
1026
1027out2:
1028 pthread_mutex_unlock(&ff->bfl);
1029out:
1030 free(temp_path);
1031 return ret;
1032}
1033
1034static int op_mkdir(const char *path, mode_t mode)
1035{
1036 struct fuse_context *ctxt = fuse_get_context();
1037 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1038 ext2_filsys fs;
1039 ext2_ino_t parent, child;
da9d5b80 1040 char *temp_path;
81cbf1ef
DW
1041 errcode_t err;
1042 char *node_name, a;
1043 struct ext2_inode_large inode;
1044 char *block;
1045 blk64_t blk;
1046 int ret = 0;
1047 mode_t parent_sgid;
1048
1049 FUSE2FS_CHECK_CONTEXT(ff);
1050 fs = ff->fs;
1051 dbg_printf("%s: path=%s mode=0%o\n", __func__, path, mode);
da9d5b80 1052 temp_path = strdup(path);
81cbf1ef
DW
1053 if (!temp_path) {
1054 ret = -ENOMEM;
1055 goto out;
1056 }
1057 node_name = strrchr(temp_path, '/');
1058 if (!node_name) {
1059 ret = -ENOMEM;
1060 goto out;
1061 }
1062 node_name++;
1063 a = *node_name;
1064 *node_name = 0;
1065
1066 pthread_mutex_lock(&ff->bfl);
1067 if (!fs_can_allocate(ff, 1)) {
1068 ret = -ENOSPC;
1069 goto out2;
1070 }
1071
1072 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1073 &parent);
1074 if (err) {
1075 ret = translate_error(fs, 0, err);
1076 goto out2;
1077 }
1078
1079 ret = check_inum_access(fs, parent, W_OK);
1080 if (ret)
1081 goto out2;
1082
1083 /* Is the parent dir sgid? */
1084 err = ext2fs_read_inode_full(fs, parent, (struct ext2_inode *)&inode,
1085 sizeof(inode));
1086 if (err) {
1087 ret = translate_error(fs, parent, err);
1088 goto out2;
1089 }
1090 parent_sgid = inode.i_mode & S_ISGID;
1091
1092 *node_name = a;
1093
1094 err = ext2fs_mkdir(fs, parent, 0, node_name);
1095 if (err == EXT2_ET_DIR_NO_SPACE) {
1096 err = ext2fs_expand_dir(fs, parent);
1097 if (err) {
1098 ret = translate_error(fs, parent, err);
1099 goto out2;
1100 }
1101
1102 err = ext2fs_mkdir(fs, parent, 0, node_name);
1103 }
1104 if (err) {
1105 ret = translate_error(fs, parent, err);
1106 goto out2;
1107 }
1108
1109 ret = update_mtime(fs, parent, NULL);
1110 if (ret)
1111 goto out2;
1112
1113 /* Still have to update the uid/gid of the dir */
1114 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1115 &child);
1116 if (err) {
1117 ret = translate_error(fs, 0, err);
1118 goto out2;
1119 }
1120 dbg_printf("%s: created ino=%d/path=%s in dir=%d\n", __func__, child,
1121 node_name, parent);
1122
1123 memset(&inode, 0, sizeof(inode));
1124 err = ext2fs_read_inode_full(fs, child, (struct ext2_inode *)&inode,
1125 sizeof(inode));
1126 if (err) {
1127 ret = translate_error(fs, child, err);
1128 goto out2;
1129 }
1130
1131 inode.i_uid = ctxt->uid;
1132 inode.i_gid = ctxt->gid;
1133 inode.i_mode = LINUX_S_IFDIR | (mode & ~(S_ISUID | fs->umask)) |
1134 parent_sgid;
1135 inode.i_generation = ff->next_generation++;
1136
1137 err = ext2fs_write_inode_full(fs, child, (struct ext2_inode *)&inode,
1138 sizeof(inode));
1139 if (err) {
1140 ret = translate_error(fs, child, err);
1141 goto out2;
1142 }
1143
1144 /* Rewrite the directory block checksum, having set i_generation */
1145 if ((inode.i_flags & EXT4_INLINE_DATA_FL) ||
7889640d 1146 !ext2fs_has_feature_metadata_csum(fs->super))
81cbf1ef
DW
1147 goto out2;
1148 err = ext2fs_new_dir_block(fs, child, parent, &block);
1149 if (err) {
1150 ret = translate_error(fs, child, err);
1151 goto out2;
1152 }
1153 err = ext2fs_bmap2(fs, child, (struct ext2_inode *)&inode, NULL, 0, 0,
1154 NULL, &blk);
1155 if (err) {
1156 ret = translate_error(fs, child, err);
1157 goto out3;
1158 }
1159 err = ext2fs_write_dir_block4(fs, blk, block, 0, child);
1160 if (err) {
1161 ret = translate_error(fs, child, err);
1162 goto out3;
1163 }
1164
1165out3:
1166 ext2fs_free_mem(&block);
1167out2:
1168 pthread_mutex_unlock(&ff->bfl);
1169out:
1170 free(temp_path);
1171 return ret;
1172}
1173
1174static int unlink_file_by_name(ext2_filsys fs, const char *path)
1175{
1176 errcode_t err;
1177 ext2_ino_t dir;
1178 char *filename = strdup(path);
1179 char *base_name;
1180 int ret;
1181
1182 base_name = strrchr(filename, '/');
1183 if (base_name) {
1184 *base_name++ = '\0';
1185 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, filename,
1186 &dir);
1187 if (err) {
1188 free(filename);
1189 return translate_error(fs, 0, err);
1190 }
1191 } else {
1192 dir = EXT2_ROOT_INO;
1193 base_name = filename;
1194 }
1195
1196 ret = check_inum_access(fs, dir, W_OK);
1197 if (ret) {
1198 free(filename);
1199 return ret;
1200 }
1201
1202 dbg_printf("%s: unlinking name=%s from dir=%d\n", __func__,
1203 base_name, dir);
1204 err = ext2fs_unlink(fs, dir, base_name, 0, 0);
1205 free(filename);
1206 if (err)
1207 return translate_error(fs, dir, err);
1208
1209 return update_mtime(fs, dir, NULL);
1210}
1211
1212static int remove_inode(struct fuse2fs *ff, ext2_ino_t ino)
1213{
1214 ext2_filsys fs = ff->fs;
1215 errcode_t err;
1216 struct ext2_inode_large inode;
1217 int ret = 0;
1218
1219 memset(&inode, 0, sizeof(inode));
1220 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
1221 sizeof(inode));
1222 if (err) {
1223 ret = translate_error(fs, ino, err);
1224 goto out;
1225 }
1226 dbg_printf("%s: put ino=%d links=%d\n", __func__, ino,
1227 inode.i_links_count);
1228
1229 switch (inode.i_links_count) {
1230 case 0:
1231 return 0; /* XXX: already done? */
1232 case 1:
1233 inode.i_links_count--;
1234 inode.i_dtime = fs->now ? fs->now : time(0);
1235 break;
1236 default:
1237 inode.i_links_count--;
1238 }
1239
1240 ret = update_ctime(fs, ino, &inode);
1241 if (ret)
1242 goto out;
1243
1244 if (inode.i_links_count)
1245 goto write_out;
1246
1247 /* Nobody holds this file; free its blocks! */
1248 err = ext2fs_free_ext_attr(fs, ino, &inode);
1249 if (err)
1250 goto write_out;
1251
1252 if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *)&inode)) {
1253 err = ext2fs_punch(fs, ino, (struct ext2_inode *)&inode, NULL,
1254 0, ~0ULL);
1255 if (err) {
1256 ret = translate_error(fs, ino, err);
1257 goto write_out;
1258 }
1259 }
1260
1261 ext2fs_inode_alloc_stats2(fs, ino, -1,
1262 LINUX_S_ISDIR(inode.i_mode));
1263
1264write_out:
1265 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
1266 sizeof(inode));
1267 if (err) {
1268 ret = translate_error(fs, ino, err);
1269 goto out;
1270 }
1271out:
1272 return ret;
1273}
1274
1275static int __op_unlink(struct fuse2fs *ff, const char *path)
1276{
1277 ext2_filsys fs = ff->fs;
1278 ext2_ino_t ino;
1279 errcode_t err;
1280 int ret = 0;
1281
1282 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
1283 if (err) {
1284 ret = translate_error(fs, 0, err);
1285 goto out;
1286 }
1287
1288 ret = unlink_file_by_name(fs, path);
1289 if (ret)
1290 goto out;
1291
1292 ret = remove_inode(ff, ino);
1293 if (ret)
1294 goto out;
1295out:
1296 return ret;
1297}
1298
1299static int op_unlink(const char *path)
1300{
1301 struct fuse_context *ctxt = fuse_get_context();
1302 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1303 int ret;
1304
1305 FUSE2FS_CHECK_CONTEXT(ff);
1306 pthread_mutex_lock(&ff->bfl);
1307 ret = __op_unlink(ff, path);
1308 pthread_mutex_unlock(&ff->bfl);
1309 return ret;
1310}
1311
1312struct rd_struct {
1313 ext2_ino_t parent;
1314 int empty;
1315};
1316
1317static int rmdir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
1318 int entry EXT2FS_ATTR((unused)),
1319 struct ext2_dir_entry *dirent,
1320 int offset EXT2FS_ATTR((unused)),
1321 int blocksize EXT2FS_ATTR((unused)),
1322 char *buf EXT2FS_ATTR((unused)),
1323 void *private)
1324{
1325 struct rd_struct *rds = (struct rd_struct *) private;
1326
1327 if (dirent->inode == 0)
1328 return 0;
1329 if (((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.'))
1330 return 0;
1331 if (((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') &&
1332 (dirent->name[1] == '.')) {
1333 rds->parent = dirent->inode;
1334 return 0;
1335 }
1336 rds->empty = 0;
1337 return 0;
1338}
1339
1340static int __op_rmdir(struct fuse2fs *ff, const char *path)
1341{
1342 ext2_filsys fs = ff->fs;
1343 ext2_ino_t child;
1344 errcode_t err;
1345 struct ext2_inode_large inode;
1346 struct rd_struct rds;
1347 int ret = 0;
1348
1349 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &child);
1350 if (err) {
1351 ret = translate_error(fs, 0, err);
1352 goto out;
1353 }
1354 dbg_printf("%s: rmdir path=%s ino=%d\n", __func__, path, child);
1355
1356 rds.parent = 0;
1357 rds.empty = 1;
1358
1359 err = ext2fs_dir_iterate2(fs, child, 0, 0, rmdir_proc, &rds);
1360 if (err) {
1361 ret = translate_error(fs, child, err);
1362 goto out;
1363 }
1364
1365 if (rds.empty == 0) {
1366 ret = -ENOTEMPTY;
1367 goto out;
1368 }
1369
1370 ret = unlink_file_by_name(fs, path);
1371 if (ret)
1372 goto out;
1373 /* Directories have to be "removed" twice. */
1374 ret = remove_inode(ff, child);
1375 if (ret)
1376 goto out;
1377 ret = remove_inode(ff, child);
1378 if (ret)
1379 goto out;
1380
1381 if (rds.parent) {
1382 dbg_printf("%s: decr dir=%d link count\n", __func__,
1383 rds.parent);
1384 err = ext2fs_read_inode_full(fs, rds.parent,
1385 (struct ext2_inode *)&inode,
1386 sizeof(inode));
1387 if (err) {
1388 ret = translate_error(fs, rds.parent, err);
1389 goto out;
1390 }
1391 if (inode.i_links_count > 1)
1392 inode.i_links_count--;
1393 ret = update_mtime(fs, rds.parent, &inode);
1394 if (ret)
1395 goto out;
1396 err = ext2fs_write_inode_full(fs, rds.parent,
1397 (struct ext2_inode *)&inode,
1398 sizeof(inode));
1399 if (err) {
1400 ret = translate_error(fs, rds.parent, err);
1401 goto out;
1402 }
1403 }
1404
1405out:
1406 return ret;
1407}
1408
1409static int op_rmdir(const char *path)
1410{
1411 struct fuse_context *ctxt = fuse_get_context();
1412 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1413 int ret;
1414
1415 FUSE2FS_CHECK_CONTEXT(ff);
1416 pthread_mutex_lock(&ff->bfl);
1417 ret = __op_rmdir(ff, path);
1418 pthread_mutex_unlock(&ff->bfl);
1419 return ret;
1420}
1421
1422static int op_symlink(const char *src, const char *dest)
1423{
1424 struct fuse_context *ctxt = fuse_get_context();
1425 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1426 ext2_filsys fs;
1427 ext2_ino_t parent, child;
da9d5b80 1428 char *temp_path;
81cbf1ef
DW
1429 errcode_t err;
1430 char *node_name, a;
1431 struct ext2_inode_large inode;
1432 int ret = 0;
1433
1434 FUSE2FS_CHECK_CONTEXT(ff);
1435 fs = ff->fs;
1436 dbg_printf("%s: symlink %s to %s\n", __func__, src, dest);
da9d5b80 1437 temp_path = strdup(dest);
81cbf1ef
DW
1438 if (!temp_path) {
1439 ret = -ENOMEM;
1440 goto out;
1441 }
1442 node_name = strrchr(temp_path, '/');
1443 if (!node_name) {
1444 ret = -ENOMEM;
1445 goto out;
1446 }
1447 node_name++;
1448 a = *node_name;
1449 *node_name = 0;
1450
1451 pthread_mutex_lock(&ff->bfl);
1452 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1453 &parent);
1454 *node_name = a;
1455 if (err) {
1456 ret = translate_error(fs, 0, err);
1457 goto out2;
1458 }
1459
1460 ret = check_inum_access(fs, parent, W_OK);
1461 if (ret)
1462 goto out2;
1463
1464
1465 /* Create symlink */
4e222d9b 1466 err = ext2fs_symlink(fs, parent, 0, node_name, src);
81cbf1ef
DW
1467 if (err == EXT2_ET_DIR_NO_SPACE) {
1468 err = ext2fs_expand_dir(fs, parent);
1469 if (err) {
1470 ret = translate_error(fs, parent, err);
1471 goto out2;
1472 }
1473
4e222d9b 1474 err = ext2fs_symlink(fs, parent, 0, node_name, src);
81cbf1ef
DW
1475 }
1476 if (err) {
1477 ret = translate_error(fs, parent, err);
1478 goto out2;
1479 }
1480
1481 /* Update parent dir's mtime */
1482 ret = update_mtime(fs, parent, NULL);
1483 if (ret)
1484 goto out2;
1485
1486 /* Still have to update the uid/gid of the symlink */
1487 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1488 &child);
1489 if (err) {
1490 ret = translate_error(fs, 0, err);
1491 goto out2;
1492 }
1493 dbg_printf("%s: symlinking ino=%d/name=%s to dir=%d\n", __func__,
1494 child, node_name, parent);
1495
1496 memset(&inode, 0, sizeof(inode));
1497 err = ext2fs_read_inode_full(fs, child, (struct ext2_inode *)&inode,
1498 sizeof(inode));
1499 if (err) {
1500 ret = translate_error(fs, child, err);
1501 goto out2;
1502 }
1503
1504 inode.i_uid = ctxt->uid;
1505 inode.i_gid = ctxt->gid;
1506 inode.i_generation = ff->next_generation++;
1507
1508 err = ext2fs_write_inode_full(fs, child, (struct ext2_inode *)&inode,
1509 sizeof(inode));
1510 if (err) {
1511 ret = translate_error(fs, child, err);
1512 goto out2;
1513 }
1514out2:
1515 pthread_mutex_unlock(&ff->bfl);
1516out:
1517 free(temp_path);
1518 return ret;
1519}
1520
1521struct update_dotdot {
1522 ext2_ino_t new_dotdot;
1523};
1524
4e222d9b
TT
1525static int update_dotdot_helper(ext2_ino_t dir EXT2FS_ATTR((unused)),
1526 int entry EXT2FS_ATTR((unused)),
1527 struct ext2_dir_entry *dirent,
1528 int offset EXT2FS_ATTR((unused)),
1529 int blocksize EXT2FS_ATTR((unused)),
1530 char *buf EXT2FS_ATTR((unused)),
1531 void *priv_data)
81cbf1ef
DW
1532{
1533 struct update_dotdot *ud = priv_data;
1534
1535 if (ext2fs_dirent_name_len(dirent) == 2 &&
1536 dirent->name[0] == '.' && dirent->name[1] == '.') {
1537 dirent->inode = ud->new_dotdot;
1538 return DIRENT_CHANGED | DIRENT_ABORT;
1539 }
1540
1541 return 0;
1542}
1543
1544static int op_rename(const char *from, const char *to)
1545{
1546 struct fuse_context *ctxt = fuse_get_context();
1547 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1548 ext2_filsys fs;
1549 errcode_t err;
1550 ext2_ino_t from_ino, to_ino, to_dir_ino, from_dir_ino;
1551 char *temp_to = NULL, *temp_from = NULL;
1552 char *cp, a;
1553 struct ext2_inode inode;
1554 struct update_dotdot ud;
1555 int ret = 0;
1556
1557 FUSE2FS_CHECK_CONTEXT(ff);
1558 fs = ff->fs;
1559 dbg_printf("%s: renaming %s to %s\n", __func__, from, to);
1560 pthread_mutex_lock(&ff->bfl);
1561 if (!fs_can_allocate(ff, 5)) {
1562 ret = -ENOSPC;
1563 goto out;
1564 }
1565
1566 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, from, &from_ino);
1567 if (err || from_ino == 0) {
1568 ret = translate_error(fs, 0, err);
1569 goto out;
1570 }
1571
1572 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, to, &to_ino);
1573 if (err && err != EXT2_ET_FILE_NOT_FOUND) {
1574 ret = translate_error(fs, 0, err);
1575 goto out;
1576 }
1577
1578 if (err == EXT2_ET_FILE_NOT_FOUND)
1579 to_ino = 0;
1580
1581 /* Already the same file? */
1582 if (to_ino != 0 && to_ino == from_ino) {
1583 ret = 0;
1584 goto out;
1585 }
1586
1587 temp_to = strdup(to);
1588 if (!temp_to) {
1589 ret = -ENOMEM;
1590 goto out;
1591 }
1592
1593 temp_from = strdup(from);
1594 if (!temp_from) {
1595 ret = -ENOMEM;
1596 goto out2;
1597 }
1598
1599 /* Find parent dir of the source and check write access */
1600 cp = strrchr(temp_from, '/');
1601 if (!cp) {
1602 ret = -EINVAL;
1603 goto out2;
1604 }
1605
1606 a = *(cp + 1);
1607 *(cp + 1) = 0;
1608 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_from,
1609 &from_dir_ino);
1610 *(cp + 1) = a;
1611 if (err) {
1612 ret = translate_error(fs, 0, err);
1613 goto out2;
1614 }
1615 if (from_dir_ino == 0) {
1616 ret = -ENOENT;
1617 goto out2;
1618 }
1619
1620 ret = check_inum_access(fs, from_dir_ino, W_OK);
1621 if (ret)
1622 goto out2;
1623
1624 /* Find parent dir of the destination and check write access */
1625 cp = strrchr(temp_to, '/');
1626 if (!cp) {
1627 ret = -EINVAL;
1628 goto out2;
1629 }
1630
1631 a = *(cp + 1);
1632 *(cp + 1) = 0;
1633 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_to,
1634 &to_dir_ino);
1635 *(cp + 1) = a;
1636 if (err) {
1637 ret = translate_error(fs, 0, err);
1638 goto out2;
1639 }
1640 if (to_dir_ino == 0) {
1641 ret = -ENOENT;
1642 goto out2;
1643 }
1644
1645 ret = check_inum_access(fs, to_dir_ino, W_OK);
1646 if (ret)
1647 goto out2;
1648
1649 /* If the target exists, unlink it first */
1650 if (to_ino != 0) {
1651 err = ext2fs_read_inode(fs, to_ino, &inode);
1652 if (err) {
1653 ret = translate_error(fs, to_ino, err);
1654 goto out2;
1655 }
1656
1657 dbg_printf("%s: unlinking %s ino=%d\n", __func__,
1658 LINUX_S_ISDIR(inode.i_mode) ? "dir" : "file",
1659 to_ino);
1660 if (LINUX_S_ISDIR(inode.i_mode))
1661 ret = __op_rmdir(ff, to);
1662 else
1663 ret = __op_unlink(ff, to);
1664 if (ret)
1665 goto out2;
1666 }
1667
1668 /* Get ready to do the move */
1669 err = ext2fs_read_inode(fs, from_ino, &inode);
1670 if (err) {
1671 ret = translate_error(fs, from_ino, err);
1672 goto out2;
1673 }
1674
1675 /* Link in the new file */
1676 dbg_printf("%s: linking ino=%d/path=%s to dir=%d\n", __func__,
1677 from_ino, cp + 1, to_dir_ino);
1678 err = ext2fs_link(fs, to_dir_ino, cp + 1, from_ino,
1679 ext2_file_type(inode.i_mode));
1680 if (err == EXT2_ET_DIR_NO_SPACE) {
1681 err = ext2fs_expand_dir(fs, to_dir_ino);
1682 if (err) {
1683 ret = translate_error(fs, to_dir_ino, err);
1684 goto out2;
1685 }
1686
1687 err = ext2fs_link(fs, to_dir_ino, cp + 1, from_ino,
1688 ext2_file_type(inode.i_mode));
1689 }
1690 if (err) {
1691 ret = translate_error(fs, to_dir_ino, err);
1692 goto out2;
1693 }
1694
1695 /* Update '..' pointer if dir */
1696 err = ext2fs_read_inode(fs, from_ino, &inode);
1697 if (err) {
1698 ret = translate_error(fs, from_ino, err);
1699 goto out2;
1700 }
1701
1702 if (LINUX_S_ISDIR(inode.i_mode)) {
1703 ud.new_dotdot = to_dir_ino;
1704 dbg_printf("%s: updating .. entry for dir=%d\n", __func__,
1705 to_dir_ino);
1706 err = ext2fs_dir_iterate2(fs, from_ino, 0, NULL,
1707 update_dotdot_helper, &ud);
1708 if (err) {
1709 ret = translate_error(fs, from_ino, err);
1710 goto out2;
1711 }
1712
1713 /* Decrease from_dir_ino's links_count */
1714 dbg_printf("%s: moving linkcount from dir=%d to dir=%d\n",
1715 __func__, from_dir_ino, to_dir_ino);
1716 err = ext2fs_read_inode(fs, from_dir_ino, &inode);
1717 if (err) {
1718 ret = translate_error(fs, from_dir_ino, err);
1719 goto out2;
1720 }
1721 inode.i_links_count--;
1722 err = ext2fs_write_inode(fs, from_dir_ino, &inode);
1723 if (err) {
1724 ret = translate_error(fs, from_dir_ino, err);
1725 goto out2;
1726 }
1727
1728 /* Increase to_dir_ino's links_count */
1729 err = ext2fs_read_inode(fs, to_dir_ino, &inode);
1730 if (err) {
1731 ret = translate_error(fs, to_dir_ino, err);
1732 goto out2;
1733 }
1734 inode.i_links_count++;
1735 err = ext2fs_write_inode(fs, to_dir_ino, &inode);
1736 if (err) {
1737 ret = translate_error(fs, to_dir_ino, err);
1738 goto out2;
1739 }
1740 }
1741
1742 /* Update timestamps */
1743 ret = update_ctime(fs, from_ino, NULL);
1744 if (ret)
1745 goto out2;
1746
1747 ret = update_mtime(fs, to_dir_ino, NULL);
1748 if (ret)
1749 goto out2;
1750
1751 /* Remove the old file */
1752 ret = unlink_file_by_name(fs, from);
1753 if (ret)
1754 goto out2;
1755
1756 /* Flush the whole mess out */
1757 err = ext2fs_flush2(fs, 0);
1758 if (err)
1759 ret = translate_error(fs, 0, err);
1760
1761out2:
1762 free(temp_from);
1763 free(temp_to);
1764out:
1765 pthread_mutex_unlock(&ff->bfl);
1766 return ret;
1767}
1768
1769static int op_link(const char *src, const char *dest)
1770{
1771 struct fuse_context *ctxt = fuse_get_context();
1772 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1773 ext2_filsys fs;
da9d5b80 1774 char *temp_path;
81cbf1ef
DW
1775 errcode_t err;
1776 char *node_name, a;
1777 ext2_ino_t parent, ino;
1778 struct ext2_inode_large inode;
1779 int ret = 0;
1780
1781 FUSE2FS_CHECK_CONTEXT(ff);
1782 fs = ff->fs;
1783 dbg_printf("%s: src=%s dest=%s\n", __func__, src, dest);
da9d5b80 1784 temp_path = strdup(dest);
81cbf1ef
DW
1785 if (!temp_path) {
1786 ret = -ENOMEM;
1787 goto out;
1788 }
1789 node_name = strrchr(temp_path, '/');
1790 if (!node_name) {
1791 ret = -ENOMEM;
1792 goto out;
1793 }
1794 node_name++;
1795 a = *node_name;
1796 *node_name = 0;
1797
1798 pthread_mutex_lock(&ff->bfl);
1799 if (!fs_can_allocate(ff, 2)) {
1800 ret = -ENOSPC;
1801 goto out2;
1802 }
1803
1804 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
1805 &parent);
1806 *node_name = a;
1807 if (err) {
1808 err = -ENOENT;
1809 goto out2;
1810 }
1811
1812 ret = check_inum_access(fs, parent, W_OK);
1813 if (ret)
1814 goto out2;
1815
1816
1817 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, src, &ino);
1818 if (err || ino == 0) {
1819 ret = translate_error(fs, 0, err);
1820 goto out2;
1821 }
1822
1823 memset(&inode, 0, sizeof(inode));
1824 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
1825 sizeof(inode));
1826 if (err) {
1827 ret = translate_error(fs, ino, err);
1828 goto out2;
1829 }
1830
1831 inode.i_links_count++;
1832 ret = update_ctime(fs, ino, &inode);
1833 if (ret)
1834 goto out2;
1835
1836 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
1837 sizeof(inode));
1838 if (err) {
1839 ret = translate_error(fs, ino, err);
1840 goto out2;
1841 }
1842
1843 dbg_printf("%s: linking ino=%d/name=%s to dir=%d\n", __func__, ino,
1844 node_name, parent);
1845 err = ext2fs_link(fs, parent, node_name, ino,
1846 ext2_file_type(inode.i_mode));
1847 if (err == EXT2_ET_DIR_NO_SPACE) {
1848 err = ext2fs_expand_dir(fs, parent);
1849 if (err) {
1850 ret = translate_error(fs, parent, err);
1851 goto out2;
1852 }
1853
1854 err = ext2fs_link(fs, parent, node_name, ino,
1855 ext2_file_type(inode.i_mode));
1856 }
1857 if (err) {
1858 ret = translate_error(fs, parent, err);
1859 goto out2;
1860 }
1861
1862 ret = update_mtime(fs, parent, NULL);
1863 if (ret)
1864 goto out2;
1865
1866out2:
1867 pthread_mutex_unlock(&ff->bfl);
1868out:
1869 free(temp_path);
1870 return ret;
1871}
1872
1873static int op_chmod(const char *path, mode_t mode)
1874{
1875 struct fuse_context *ctxt = fuse_get_context();
1876 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1877 ext2_filsys fs;
1878 errcode_t err;
1879 ext2_ino_t ino;
1880 struct ext2_inode_large inode;
1881 int ret = 0;
1882
1883 FUSE2FS_CHECK_CONTEXT(ff);
1884 fs = ff->fs;
1885 pthread_mutex_lock(&ff->bfl);
1886 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
1887 if (err) {
1888 ret = translate_error(fs, 0, err);
1889 goto out;
1890 }
1891 dbg_printf("%s: path=%s mode=0%o ino=%d\n", __func__, path, mode, ino);
1892
1893 memset(&inode, 0, sizeof(inode));
1894 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
1895 sizeof(inode));
1896 if (err) {
1897 ret = translate_error(fs, ino, err);
1898 goto out;
1899 }
1900
1901 if (ctxt->uid != 0 && ctxt->uid != inode.i_uid) {
1902 ret = -EPERM;
1903 goto out;
1904 }
1905
1906 /*
1907 * XXX: We should really check that the inode gid is not in /any/
1908 * of the user's groups, but FUSE only tells us about the primary
1909 * group.
1910 */
1911 if (ctxt->uid != 0 && ctxt->gid != inode.i_gid)
1912 mode &= ~S_ISGID;
1913
1914 inode.i_mode &= ~0xFFF;
1915 inode.i_mode |= mode & 0xFFF;
1916 ret = update_ctime(fs, ino, &inode);
1917 if (ret)
1918 goto out;
1919
1920 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
1921 sizeof(inode));
1922 if (err) {
1923 ret = translate_error(fs, ino, err);
1924 goto out;
1925 }
1926
1927out:
1928 pthread_mutex_unlock(&ff->bfl);
1929 return ret;
1930}
1931
1932static int op_chown(const char *path, uid_t owner, gid_t group)
1933{
1934 struct fuse_context *ctxt = fuse_get_context();
1935 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
1936 ext2_filsys fs;
1937 errcode_t err;
1938 ext2_ino_t ino;
1939 struct ext2_inode_large inode;
1940 int ret = 0;
1941
1942 FUSE2FS_CHECK_CONTEXT(ff);
1943 fs = ff->fs;
1944 pthread_mutex_lock(&ff->bfl);
1945 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
1946 if (err) {
1947 ret = translate_error(fs, 0, err);
1948 goto out;
1949 }
1950 dbg_printf("%s: path=%s owner=%d group=%d ino=%d\n", __func__,
1951 path, owner, group, ino);
1952
1953 memset(&inode, 0, sizeof(inode));
1954 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
1955 sizeof(inode));
1956 if (err) {
1957 ret = translate_error(fs, ino, err);
1958 goto out;
1959 }
1960
1961 /* FUSE seems to feed us ~0 to mean "don't change" */
4e222d9b 1962 if (owner != (uid_t) ~0) {
81cbf1ef
DW
1963 /* Only root gets to change UID. */
1964 if (ctxt->uid != 0 &&
1965 !(inode.i_uid == ctxt->uid && owner == ctxt->uid)) {
1966 ret = -EPERM;
1967 goto out;
1968 }
1969 inode.i_uid = owner;
1970 }
1971
4e222d9b 1972 if (group != (gid_t) ~0) {
81cbf1ef
DW
1973 /* Only root or the owner get to change GID. */
1974 if (ctxt->uid != 0 && inode.i_uid != ctxt->uid) {
1975 ret = -EPERM;
1976 goto out;
1977 }
1978
1979 /* XXX: We /should/ check group membership but FUSE */
1980 inode.i_gid = group;
1981 }
1982
1983 ret = update_ctime(fs, ino, &inode);
1984 if (ret)
1985 goto out;
1986
1987 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
1988 sizeof(inode));
1989 if (err) {
1990 ret = translate_error(fs, ino, err);
1991 goto out;
1992 }
1993
1994out:
1995 pthread_mutex_unlock(&ff->bfl);
1996 return ret;
1997}
1998
1999static int op_truncate(const char *path, off_t len)
2000{
2001 struct fuse_context *ctxt = fuse_get_context();
2002 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2003 ext2_filsys fs;
2004 errcode_t err;
2005 ext2_ino_t ino;
2006 ext2_file_t file;
2007 int ret = 0;
2008
2009 FUSE2FS_CHECK_CONTEXT(ff);
2010 fs = ff->fs;
2011 pthread_mutex_lock(&ff->bfl);
2012 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2013 if (err || ino == 0) {
2014 ret = translate_error(fs, 0, err);
2015 goto out;
2016 }
2017 dbg_printf("%s: ino=%d len=%jd\n", __func__, ino, len);
2018
2019 ret = check_inum_access(fs, ino, W_OK);
2020 if (ret)
2021 goto out;
2022
2023 err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &file);
2024 if (err) {
2025 ret = translate_error(fs, ino, err);
2026 goto out;
2027 }
2028
2029 err = ext2fs_file_set_size2(file, len);
2030 if (err) {
2031 ret = translate_error(fs, ino, err);
2032 goto out2;
2033 }
2034
2035out2:
2036 err = ext2fs_file_close(file);
2037 if (ret)
2038 goto out;
2039 if (err) {
2040 ret = translate_error(fs, ino, err);
2041 goto out;
2042 }
2043
2044 ret = update_mtime(fs, ino, NULL);
2045
2046out:
2047 pthread_mutex_unlock(&ff->bfl);
2048 return err;
2049}
2050
2051#ifdef __linux__
2052static void detect_linux_executable_open(int kernel_flags, int *access_check,
2053 int *e2fs_open_flags)
2054{
2055 /*
2056 * On Linux, execve will bleed __FMODE_EXEC into the file mode flags,
2057 * and FUSE is more than happy to let that slip through.
2058 */
2059 if (kernel_flags & 0x20) {
2060 *access_check = X_OK;
2061 *e2fs_open_flags &= ~EXT2_FILE_WRITE;
2062 }
2063}
2064#else
2065static void detect_linux_executable_open(int kernel_flags, int *access_check,
2066 int *e2fs_open_flags)
2067{
2068 /* empty */
2069}
2070#endif /* __linux__ */
2071
2072static int __op_open(struct fuse2fs *ff, const char *path,
2073 struct fuse_file_info *fp)
2074{
2075 ext2_filsys fs = ff->fs;
2076 errcode_t err;
2077 struct fuse2fs_file_handle *file;
2078 int check = 0, ret = 0;
2079
2080 dbg_printf("%s: path=%s\n", __func__, path);
2081 err = ext2fs_get_mem(sizeof(*file), &file);
2082 if (err)
2083 return translate_error(fs, 0, err);
2084 file->magic = FUSE2FS_FILE_MAGIC;
2085
2086 file->open_flags = 0;
2087 switch (fp->flags & O_ACCMODE) {
2088 case O_RDONLY:
2089 check = R_OK;
2090 break;
2091 case O_WRONLY:
2092 check = W_OK;
2093 file->open_flags |= EXT2_FILE_WRITE;
2094 break;
2095 case O_RDWR:
2096 check = R_OK | W_OK;
2097 file->open_flags |= EXT2_FILE_WRITE;
2098 break;
2099 }
2100
2101 detect_linux_executable_open(fp->flags, &check, &file->open_flags);
2102
2103 if (fp->flags & O_CREAT)
2104 file->open_flags |= EXT2_FILE_CREATE;
2105
2106 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &file->ino);
2107 if (err || file->ino == 0) {
2108 ret = translate_error(fs, 0, err);
2109 goto out;
2110 }
2111 dbg_printf("%s: ino=%d\n", __func__, file->ino);
2112
2113 ret = check_inum_access(fs, file->ino, check);
2114 if (ret) {
2115 /*
2116 * In a regular (Linux) fs driver, the kernel will open
2117 * binaries for reading if the user has --x privileges (i.e.
2118 * execute without read). Since the kernel doesn't have any
2119 * way to tell us if it's opening a file via execve, we'll
2120 * just assume that allowing access is ok if asking for ro mode
2121 * fails but asking for x mode succeeds. Of course we can
2122 * also employ undocumented hacks (see above).
2123 */
2124 if (check == R_OK) {
2125 ret = check_inum_access(fs, file->ino, X_OK);
2126 if (ret)
2127 goto out;
2128 } else
2129 goto out;
2130 }
377e3a96 2131 fp->fh = (uintptr_t)file;
81cbf1ef
DW
2132
2133out:
2134 if (ret)
2135 ext2fs_free_mem(&file);
2136 return ret;
2137}
2138
2139static int op_open(const char *path, struct fuse_file_info *fp)
2140{
2141 struct fuse_context *ctxt = fuse_get_context();
2142 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2143 int ret;
2144
2145 FUSE2FS_CHECK_CONTEXT(ff);
2146 pthread_mutex_lock(&ff->bfl);
2147 ret = __op_open(ff, path, fp);
2148 pthread_mutex_unlock(&ff->bfl);
2149 return ret;
2150}
2151
4e222d9b
TT
2152static int op_read(const char *path EXT2FS_ATTR((unused)), char *buf,
2153 size_t len, off_t offset,
2154 struct fuse_file_info *fp)
81cbf1ef
DW
2155{
2156 struct fuse_context *ctxt = fuse_get_context();
2157 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2158 struct fuse2fs_file_handle *fh =
2159 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2160 ext2_filsys fs;
2161 ext2_file_t efp;
2162 errcode_t err;
2163 unsigned int got = 0;
2164 int ret = 0;
2165
2166 FUSE2FS_CHECK_CONTEXT(ff);
2167 fs = ff->fs;
2168 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
2169 dbg_printf("%s: ino=%d off=%jd len=%jd\n", __func__, fh->ino, offset,
2170 len);
2171 pthread_mutex_lock(&ff->bfl);
2172 err = ext2fs_file_open(fs, fh->ino, fh->open_flags, &efp);
2173 if (err) {
2174 ret = translate_error(fs, fh->ino, err);
2175 goto out;
2176 }
2177
2178 err = ext2fs_file_llseek(efp, offset, SEEK_SET, NULL);
2179 if (err) {
2180 ret = translate_error(fs, fh->ino, err);
2181 goto out2;
2182 }
2183
2184 err = ext2fs_file_read(efp, buf, len, &got);
2185 if (err) {
2186 ret = translate_error(fs, fh->ino, err);
2187 goto out2;
2188 }
2189
2190out2:
2191 err = ext2fs_file_close(efp);
2192 if (ret)
2193 goto out;
2194 if (err) {
2195 ret = translate_error(fs, fh->ino, err);
2196 goto out;
2197 }
2198
2199 if (fs_writeable(fs)) {
2200 ret = update_atime(fs, fh->ino);
2201 if (ret)
2202 goto out;
2203 }
2204out:
2205 pthread_mutex_unlock(&ff->bfl);
4e222d9b 2206 return got ? (int) got : ret;
81cbf1ef
DW
2207}
2208
4e222d9b
TT
2209static int op_write(const char *path EXT2FS_ATTR((unused)),
2210 const char *buf, size_t len, off_t offset,
2211 struct fuse_file_info *fp)
81cbf1ef
DW
2212{
2213 struct fuse_context *ctxt = fuse_get_context();
2214 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2215 struct fuse2fs_file_handle *fh =
2216 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2217 ext2_filsys fs;
2218 ext2_file_t efp;
2219 errcode_t err;
2220 unsigned int got = 0;
2221 int ret = 0;
2222
2223 FUSE2FS_CHECK_CONTEXT(ff);
2224 fs = ff->fs;
2225 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
2226 dbg_printf("%s: ino=%d off=%jd len=%jd\n", __func__, fh->ino, offset,
2227 len);
2228 pthread_mutex_lock(&ff->bfl);
2229 if (!fs_writeable(fs)) {
2230 ret = -EROFS;
2231 goto out;
2232 }
2233
2234 if (!fs_can_allocate(ff, len / fs->blocksize)) {
2235 ret = -ENOSPC;
2236 goto out;
2237 }
2238
2239 err = ext2fs_file_open(fs, fh->ino, fh->open_flags, &efp);
2240 if (err) {
2241 ret = translate_error(fs, fh->ino, err);
2242 goto out;
2243 }
2244
2245 err = ext2fs_file_llseek(efp, offset, SEEK_SET, NULL);
2246 if (err) {
2247 ret = translate_error(fs, fh->ino, err);
2248 goto out2;
2249 }
2250
2251 err = ext2fs_file_write(efp, buf, len, &got);
2252 if (err) {
2253 ret = translate_error(fs, fh->ino, err);
2254 goto out2;
2255 }
2256
2257 err = ext2fs_file_flush(efp);
2258 if (err) {
2259 got = 0;
2260 ret = translate_error(fs, fh->ino, err);
2261 goto out2;
2262 }
2263
2264out2:
2265 err = ext2fs_file_close(efp);
2266 if (ret)
2267 goto out;
2268 if (err) {
2269 ret = translate_error(fs, fh->ino, err);
2270 goto out;
2271 }
2272
2273 ret = update_mtime(fs, fh->ino, NULL);
2274 if (ret)
2275 goto out;
2276
2277out:
2278 pthread_mutex_unlock(&ff->bfl);
4e222d9b 2279 return got ? (int) got : ret;
81cbf1ef
DW
2280}
2281
4e222d9b
TT
2282static int op_release(const char *path EXT2FS_ATTR((unused)),
2283 struct fuse_file_info *fp)
81cbf1ef
DW
2284{
2285 struct fuse_context *ctxt = fuse_get_context();
2286 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2287 struct fuse2fs_file_handle *fh =
2288 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2289 ext2_filsys fs;
2290 errcode_t err;
2291 int ret = 0;
2292
2293 FUSE2FS_CHECK_CONTEXT(ff);
2294 fs = ff->fs;
2295 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
2296 dbg_printf("%s: ino=%d\n", __func__, fh->ino);
2297 pthread_mutex_lock(&ff->bfl);
2298 if (fs_writeable(fs) && fh->open_flags & EXT2_FILE_WRITE) {
2299 err = ext2fs_flush2(fs, EXT2_FLAG_FLUSH_NO_SYNC);
2300 if (err)
2301 ret = translate_error(fs, fh->ino, err);
2302 }
2303 fp->fh = 0;
2304 pthread_mutex_unlock(&ff->bfl);
2305
2306 ext2fs_free_mem(&fh);
2307
2308 return ret;
2309}
2310
4e222d9b
TT
2311static int op_fsync(const char *path EXT2FS_ATTR((unused)),
2312 int datasync EXT2FS_ATTR((unused)),
2313 struct fuse_file_info *fp)
81cbf1ef
DW
2314{
2315 struct fuse_context *ctxt = fuse_get_context();
2316 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2317 struct fuse2fs_file_handle *fh =
2318 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2319 ext2_filsys fs;
2320 errcode_t err;
2321 int ret = 0;
2322
2323 FUSE2FS_CHECK_CONTEXT(ff);
2324 fs = ff->fs;
2325 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
2326 dbg_printf("%s: ino=%d\n", __func__, fh->ino);
2327 /* For now, flush everything, even if it's slow */
2328 pthread_mutex_lock(&ff->bfl);
2329 if (fs_writeable(fs) && fh->open_flags & EXT2_FILE_WRITE) {
2330 err = ext2fs_flush2(fs, 0);
2331 if (err)
2332 ret = translate_error(fs, fh->ino, err);
2333 }
2334 pthread_mutex_unlock(&ff->bfl);
2335
2336 return ret;
2337}
2338
4e222d9b
TT
2339static int op_statfs(const char *path EXT2FS_ATTR((unused)),
2340 struct statvfs *buf)
81cbf1ef
DW
2341{
2342 struct fuse_context *ctxt = fuse_get_context();
2343 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2344 ext2_filsys fs;
2345 uint64_t fsid, *f;
2346 blk64_t overhead, reserved, free;
2347
2348 FUSE2FS_CHECK_CONTEXT(ff);
2349 fs = ff->fs;
2350 dbg_printf("%s: path=%s\n", __func__, path);
2351 buf->f_bsize = fs->blocksize;
2352 buf->f_frsize = 0;
2353
2354 if (ff->minixdf)
2355 overhead = 0;
2356 else
2357 overhead = fs->desc_blocks +
2358 fs->group_desc_count *
2359 (fs->inode_blocks_per_group + 2);
2360 reserved = ext2fs_r_blocks_count(fs->super);
2361 if (!reserved)
2362 reserved = ext2fs_blocks_count(fs->super) / 10;
2363 free = ext2fs_free_blocks_count(fs->super);
2364
2365 buf->f_blocks = ext2fs_blocks_count(fs->super) - overhead;
2366 buf->f_bfree = free;
2367 if (free < reserved)
2368 buf->f_bavail = 0;
2369 else
2370 buf->f_bavail = free - reserved;
2371 buf->f_files = fs->super->s_inodes_count;
2372 buf->f_ffree = fs->super->s_free_inodes_count;
2373 buf->f_favail = fs->super->s_free_inodes_count;
2374 f = (uint64_t *)fs->super->s_uuid;
2375 fsid = *f;
2376 f++;
2377 fsid ^= *f;
2378 buf->f_fsid = fsid;
2379 buf->f_flag = 0;
2380 if (fs->flags & EXT2_FLAG_RW)
2381 buf->f_flag |= ST_RDONLY;
2382 buf->f_namemax = EXT2_NAME_LEN;
2383
2384 return 0;
2385}
2386
2387typedef errcode_t (*xattr_xlate_get)(void **cooked_buf, size_t *cooked_sz,
2388 const void *raw_buf, size_t raw_sz);
2389typedef errcode_t (*xattr_xlate_set)(const void *cooked_buf, size_t cooked_sz,
4e222d9b 2390 const void **raw_buf, size_t *raw_sz);
81cbf1ef
DW
2391struct xattr_translate {
2392 const char *prefix;
2393 xattr_xlate_get get;
2394 xattr_xlate_set set;
2395};
2396
2397#define XATTR_TRANSLATOR(p, g, s) \
2398 {.prefix = (p), \
2399 .get = (xattr_xlate_get)(g), \
2400 .set = (xattr_xlate_set)(s)}
2401
2402static struct xattr_translate xattr_translators[] = {
2403#ifdef TRANSLATE_LINUX_ACLS
2404 XATTR_TRANSLATOR(ACL_EA_ACCESS, ext4_to_fuse_acl, fuse_to_ext4_acl),
2405 XATTR_TRANSLATOR(ACL_EA_DEFAULT, ext4_to_fuse_acl, fuse_to_ext4_acl),
2406#endif
2407 XATTR_TRANSLATOR(NULL, NULL, NULL),
2408};
2409#undef XATTR_TRANSLATOR
2410
2411static int op_getxattr(const char *path, const char *key, char *value,
2412 size_t len)
2413{
2414 struct fuse_context *ctxt = fuse_get_context();
2415 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2416 ext2_filsys fs;
2417 struct ext2_xattr_handle *h;
2418 struct xattr_translate *xt;
2419 void *ptr, *cptr;
2420 size_t plen, clen;
2421 ext2_ino_t ino;
2422 errcode_t err;
2423 int ret = 0;
2424
2425 FUSE2FS_CHECK_CONTEXT(ff);
2426 fs = ff->fs;
2427 pthread_mutex_lock(&ff->bfl);
7889640d 2428 if (!ext2fs_has_feature_xattr(fs->super)) {
81cbf1ef
DW
2429 ret = -ENOTSUP;
2430 goto out;
2431 }
2432
2433 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2434 if (err || ino == 0) {
2435 ret = translate_error(fs, 0, err);
2436 goto out;
2437 }
2438 dbg_printf("%s: ino=%d\n", __func__, ino);
2439
2440 ret = check_inum_access(fs, ino, R_OK);
2441 if (ret)
2442 goto out;
2443
2444 err = ext2fs_xattrs_open(fs, ino, &h);
2445 if (err) {
2446 ret = translate_error(fs, ino, err);
2447 goto out;
2448 }
2449
2450 err = ext2fs_xattrs_read(h);
2451 if (err) {
2452 ret = translate_error(fs, ino, err);
2453 goto out2;
2454 }
2455
2456 err = ext2fs_xattr_get(h, key, &ptr, &plen);
2457 if (err) {
2458 ret = translate_error(fs, ino, err);
2459 goto out2;
2460 }
2461
2462 for (xt = xattr_translators; xt->prefix != NULL; xt++) {
2463 if (strncmp(key, xt->prefix, strlen(xt->prefix)) == 0) {
2464 err = xt->get(&cptr, &clen, ptr, plen);
2465 if (err)
2466 goto out3;
2467 ext2fs_free_mem(&ptr);
2468 ptr = cptr;
2469 plen = clen;
2470 }
2471 }
2472
2473 if (!len) {
2474 ret = plen;
2475 } else if (len < plen) {
2476 ret = -ERANGE;
2477 } else {
2478 memcpy(value, ptr, plen);
2479 ret = plen;
2480 }
2481
2482out3:
2483 ext2fs_free_mem(&ptr);
2484out2:
2485 err = ext2fs_xattrs_close(&h);
2486 if (err)
2487 ret = translate_error(fs, ino, err);
2488out:
2489 pthread_mutex_unlock(&ff->bfl);
2490
2491 return ret;
2492}
2493
4e222d9b
TT
2494static int count_buffer_space(char *name, char *value EXT2FS_ATTR((unused)),
2495 size_t value_len EXT2FS_ATTR((unused)),
81cbf1ef
DW
2496 void *data)
2497{
2498 unsigned int *x = data;
2499
2500 *x = *x + strlen(name) + 1;
2501 return 0;
2502}
2503
4e222d9b
TT
2504static int copy_names(char *name, char *value EXT2FS_ATTR((unused)),
2505 size_t value_len EXT2FS_ATTR((unused)), void *data)
81cbf1ef
DW
2506{
2507 char **b = data;
2508
2509 strncpy(*b, name, strlen(name));
2510 *b = *b + strlen(name) + 1;
2511
2512 return 0;
2513}
2514
2515static int op_listxattr(const char *path, char *names, size_t len)
2516{
2517 struct fuse_context *ctxt = fuse_get_context();
2518 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2519 ext2_filsys fs;
2520 struct ext2_xattr_handle *h;
2521 unsigned int bufsz;
2522 ext2_ino_t ino;
2523 errcode_t err;
2524 int ret = 0;
2525
2526 FUSE2FS_CHECK_CONTEXT(ff);
2527 fs = ff->fs;
2528 pthread_mutex_lock(&ff->bfl);
7889640d 2529 if (!ext2fs_has_feature_xattr(fs->super)) {
81cbf1ef
DW
2530 ret = -ENOTSUP;
2531 goto out;
2532 }
2533
2534 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2535 if (err || ino == 0) {
2536 ret = translate_error(fs, ino, err);
2537 goto out;
2538 }
2539 dbg_printf("%s: ino=%d\n", __func__, ino);
2540
2541 ret = check_inum_access(fs, ino, R_OK);
2542 if (ret)
da9d5b80 2543 goto out;
81cbf1ef
DW
2544
2545 err = ext2fs_xattrs_open(fs, ino, &h);
2546 if (err) {
2547 ret = translate_error(fs, ino, err);
2548 goto out;
2549 }
2550
2551 err = ext2fs_xattrs_read(h);
2552 if (err) {
2553 ret = translate_error(fs, ino, err);
2554 goto out2;
2555 }
2556
2557 /* Count buffer space needed for names */
2558 bufsz = 0;
2559 err = ext2fs_xattrs_iterate(h, count_buffer_space, &bufsz);
2560 if (err) {
2561 ret = translate_error(fs, ino, err);
2562 goto out2;
2563 }
2564
2565 if (len == 0) {
2566 ret = bufsz;
2567 goto out2;
2568 } else if (len < bufsz) {
2569 ret = -ERANGE;
2570 goto out2;
2571 }
2572
2573 /* Copy names out */
2574 memset(names, 0, len);
2575 err = ext2fs_xattrs_iterate(h, copy_names, &names);
2576 if (err) {
2577 ret = translate_error(fs, ino, err);
2578 goto out2;
2579 }
2580 ret = bufsz;
2581out2:
2582 err = ext2fs_xattrs_close(&h);
2583 if (err)
2584 ret = translate_error(fs, ino, err);
2585out:
2586 pthread_mutex_unlock(&ff->bfl);
2587
2588 return ret;
2589}
2590
4e222d9b
TT
2591static int op_setxattr(const char *path EXT2FS_ATTR((unused)),
2592 const char *key, const char *value,
2593 size_t len, int flags EXT2FS_ATTR((unused)))
81cbf1ef
DW
2594{
2595 struct fuse_context *ctxt = fuse_get_context();
2596 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2597 ext2_filsys fs;
2598 struct ext2_xattr_handle *h;
2599 struct xattr_translate *xt;
4e222d9b 2600 const void *cvalue;
81cbf1ef
DW
2601 size_t clen;
2602 ext2_ino_t ino;
2603 errcode_t err;
2604 int ret = 0;
2605
2606 FUSE2FS_CHECK_CONTEXT(ff);
2607 fs = ff->fs;
2608 pthread_mutex_lock(&ff->bfl);
7889640d 2609 if (!ext2fs_has_feature_xattr(fs->super)) {
81cbf1ef
DW
2610 ret = -ENOTSUP;
2611 goto out;
2612 }
2613
2614 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2615 if (err || ino == 0) {
2616 ret = translate_error(fs, 0, err);
2617 goto out;
2618 }
2619 dbg_printf("%s: ino=%d\n", __func__, ino);
2620
2621 ret = check_inum_access(fs, ino, W_OK);
2622 if (ret == -EACCES) {
2623 ret = -EPERM;
2624 goto out;
2625 } else if (ret)
2626 goto out;
2627
2628 err = ext2fs_xattrs_open(fs, ino, &h);
2629 if (err) {
2630 ret = translate_error(fs, ino, err);
2631 goto out;
2632 }
2633
2634 err = ext2fs_xattrs_read(h);
2635 if (err) {
2636 ret = translate_error(fs, ino, err);
2637 goto out2;
2638 }
2639
4e222d9b 2640 cvalue = value;
81cbf1ef
DW
2641 clen = len;
2642 for (xt = xattr_translators; xt->prefix != NULL; xt++) {
2643 if (strncmp(key, xt->prefix, strlen(xt->prefix)) == 0) {
2644 err = xt->set(value, len, &cvalue, &clen);
2645 if (err)
2646 goto out3;
2647 }
2648 }
2649
2650 err = ext2fs_xattr_set(h, key, cvalue, clen);
2651 if (err) {
2652 ret = translate_error(fs, ino, err);
2653 goto out3;
2654 }
2655
81cbf1ef
DW
2656 ret = update_ctime(fs, ino, NULL);
2657out3:
2658 if (cvalue != value)
2659 ext2fs_free_mem(&cvalue);
2660out2:
2661 err = ext2fs_xattrs_close(&h);
2662 if (!ret && err)
2663 ret = translate_error(fs, ino, err);
2664out:
2665 pthread_mutex_unlock(&ff->bfl);
2666
2667 return ret;
2668}
2669
2670static int op_removexattr(const char *path, const char *key)
2671{
2672 struct fuse_context *ctxt = fuse_get_context();
2673 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2674 ext2_filsys fs;
2675 struct ext2_xattr_handle *h;
2676 ext2_ino_t ino;
2677 errcode_t err;
2678 int ret = 0;
2679
2680 FUSE2FS_CHECK_CONTEXT(ff);
2681 fs = ff->fs;
2682 pthread_mutex_lock(&ff->bfl);
7889640d 2683 if (!ext2fs_has_feature_xattr(fs->super)) {
81cbf1ef
DW
2684 ret = -ENOTSUP;
2685 goto out;
2686 }
2687
2688 if (!fs_can_allocate(ff, 1)) {
2689 ret = -ENOSPC;
2690 goto out;
2691 }
2692
2693 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2694 if (err || ino == 0) {
2695 ret = translate_error(fs, 0, err);
2696 goto out;
2697 }
2698 dbg_printf("%s: ino=%d\n", __func__, ino);
2699
2700 ret = check_inum_access(fs, ino, W_OK);
2701 if (ret)
2702 goto out;
2703
2704 err = ext2fs_xattrs_open(fs, ino, &h);
2705 if (err) {
2706 ret = translate_error(fs, ino, err);
2707 goto out;
2708 }
2709
2710 err = ext2fs_xattrs_read(h);
2711 if (err) {
2712 ret = translate_error(fs, ino, err);
2713 goto out2;
2714 }
2715
2716 err = ext2fs_xattr_remove(h, key);
2717 if (err) {
2718 ret = translate_error(fs, ino, err);
2719 goto out2;
2720 }
2721
81cbf1ef
DW
2722 ret = update_ctime(fs, ino, NULL);
2723out2:
2724 err = ext2fs_xattrs_close(&h);
2725 if (err)
2726 ret = translate_error(fs, ino, err);
2727out:
2728 pthread_mutex_unlock(&ff->bfl);
2729
2730 return ret;
2731}
2732
2733struct readdir_iter {
2734 void *buf;
2735 fuse_fill_dir_t func;
2736};
2737
4e222d9b
TT
2738static int op_readdir_iter(ext2_ino_t dir EXT2FS_ATTR((unused)),
2739 int entry EXT2FS_ATTR((unused)),
2740 struct ext2_dir_entry *dirent,
2741 int offset EXT2FS_ATTR((unused)),
2742 int blocksize EXT2FS_ATTR((unused)),
2743 char *buf EXT2FS_ATTR((unused)), void *data)
81cbf1ef
DW
2744{
2745 struct readdir_iter *i = data;
2746 char namebuf[EXT2_NAME_LEN + 1];
2747 int ret;
2748
2749 memcpy(namebuf, dirent->name, dirent->name_len & 0xFF);
2750 namebuf[dirent->name_len & 0xFF] = 0;
2751 ret = i->func(i->buf, namebuf, NULL, 0);
2752 if (ret)
2753 return DIRENT_ABORT;
2754
2755 return 0;
2756}
2757
4e222d9b
TT
2758static int op_readdir(const char *path EXT2FS_ATTR((unused)),
2759 void *buf, fuse_fill_dir_t fill_func,
2760 off_t offset EXT2FS_ATTR((unused)),
2761 struct fuse_file_info *fp)
81cbf1ef
DW
2762{
2763 struct fuse_context *ctxt = fuse_get_context();
2764 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2765 struct fuse2fs_file_handle *fh =
2766 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2767 ext2_filsys fs;
2768 errcode_t err;
2769 struct readdir_iter i;
2770 int ret = 0;
2771
2772 FUSE2FS_CHECK_CONTEXT(ff);
2773 fs = ff->fs;
2774 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
2775 dbg_printf("%s: ino=%d\n", __func__, fh->ino);
2776 pthread_mutex_lock(&ff->bfl);
2777 i.buf = buf;
2778 i.func = fill_func;
2779 err = ext2fs_dir_iterate2(fs, fh->ino, 0, NULL, op_readdir_iter, &i);
2780 if (err) {
2781 ret = translate_error(fs, fh->ino, err);
2782 goto out;
2783 }
2784
2785 if (fs_writeable(fs)) {
2786 ret = update_atime(fs, fh->ino);
2787 if (ret)
2788 goto out;
2789 }
2790out:
2791 pthread_mutex_unlock(&ff->bfl);
2792 return ret;
2793}
2794
2795static int op_access(const char *path, int mask)
2796{
2797 struct fuse_context *ctxt = fuse_get_context();
2798 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2799 ext2_filsys fs;
2800 errcode_t err;
2801 ext2_ino_t ino;
2802 int ret = 0;
2803
2804 FUSE2FS_CHECK_CONTEXT(ff);
2805 fs = ff->fs;
2806 dbg_printf("%s: path=%s mask=0x%x\n", __func__, path, mask);
2807 pthread_mutex_lock(&ff->bfl);
2808 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
2809 if (err || ino == 0) {
2810 ret = translate_error(fs, 0, err);
2811 goto out;
2812 }
2813
2814 ret = check_inum_access(fs, ino, mask);
2815 if (ret)
2816 goto out;
2817
2818out:
2819 pthread_mutex_unlock(&ff->bfl);
2820 return ret;
2821}
2822
2823static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
2824{
2825 struct fuse_context *ctxt = fuse_get_context();
2826 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
2827 ext2_filsys fs;
2828 ext2_ino_t parent, child;
da9d5b80 2829 char *temp_path;
81cbf1ef
DW
2830 errcode_t err;
2831 char *node_name, a;
2832 int filetype;
2833 struct ext2_inode_large inode;
2834 int ret = 0;
2835
2836 FUSE2FS_CHECK_CONTEXT(ff);
2837 fs = ff->fs;
2838 dbg_printf("%s: path=%s mode=0%o\n", __func__, path, mode);
da9d5b80 2839 temp_path = strdup(path);
81cbf1ef
DW
2840 if (!temp_path) {
2841 ret = -ENOMEM;
2842 goto out;
2843 }
2844 node_name = strrchr(temp_path, '/');
2845 if (!node_name) {
2846 ret = -ENOMEM;
2847 goto out;
2848 }
2849 node_name++;
2850 a = *node_name;
2851 *node_name = 0;
2852
2853 pthread_mutex_lock(&ff->bfl);
2854 if (!fs_can_allocate(ff, 1)) {
2855 ret = -ENOSPC;
2856 goto out2;
2857 }
2858
2859 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
2860 &parent);
2861 if (err) {
2862 ret = translate_error(fs, 0, err);
2863 goto out2;
2864 }
2865
2866 ret = check_inum_access(fs, parent, W_OK);
2867 if (ret)
2868 goto out2;
2869
2870 *node_name = a;
2871
2872 filetype = ext2_file_type(mode);
2873
2874 err = ext2fs_new_inode(fs, parent, mode, 0, &child);
2875 if (err) {
2876 ret = translate_error(fs, parent, err);
2877 goto out2;
2878 }
2879
2880 dbg_printf("%s: creating ino=%d/name=%s in dir=%d\n", __func__, child,
2881 node_name, parent);
2882 err = ext2fs_link(fs, parent, node_name, child, filetype);
2883 if (err == EXT2_ET_DIR_NO_SPACE) {
2884 err = ext2fs_expand_dir(fs, parent);
2885 if (err) {
2886 ret = translate_error(fs, parent, err);
2887 goto out2;
2888 }
2889
2890 err = ext2fs_link(fs, parent, node_name, child,
2891 filetype);
2892 }
2893 if (err) {
2894 ret = translate_error(fs, parent, err);
2895 goto out2;
2896 }
2897
2898 ret = update_mtime(fs, parent, NULL);
2899 if (ret)
2900 goto out2;
2901
2902 memset(&inode, 0, sizeof(inode));
2903 inode.i_mode = mode;
2904 inode.i_links_count = 1;
2905 inode.i_extra_isize = sizeof(struct ext2_inode_large) -
2906 EXT2_GOOD_OLD_INODE_SIZE;
f150bdec
DW
2907 inode.i_uid = ctxt->uid;
2908 inode.i_gid = ctxt->gid;
7889640d 2909 if (ext2fs_has_feature_extents(fs->super)) {
81cbf1ef
DW
2910 ext2_extent_handle_t handle;
2911
2912 inode.i_flags &= ~EXT4_EXTENTS_FL;
2913 ret = ext2fs_extent_open2(fs, child,
2914 (struct ext2_inode *)&inode, &handle);
2915 if (ret)
2916 return ret;
2917 ext2fs_extent_free(handle);
2918 }
2919
2920 err = ext2fs_write_new_inode(fs, child, (struct ext2_inode *)&inode);
2921 if (err) {
2922 ret = translate_error(fs, child, err);
2923 goto out2;
2924 }
2925
2926 inode.i_generation = ff->next_generation++;
2927 init_times(&inode);
2928 err = ext2fs_write_inode_full(fs, child, (struct ext2_inode *)&inode,
2929 sizeof(inode));
2930 if (err) {
2931 ret = translate_error(fs, child, err);
2932 goto out2;
2933 }
2934
2935 ext2fs_inode_alloc_stats2(fs, child, 1, 0);
2936
2937 ret = __op_open(ff, path, fp);
2938 if (ret)
2939 goto out2;
2940out2:
2941 pthread_mutex_unlock(&ff->bfl);
2942out:
2943 free(temp_path);
2944 return ret;
2945}
2946
4e222d9b
TT
2947static int op_ftruncate(const char *path EXT2FS_ATTR((unused)),
2948 off_t len, struct fuse_file_info *fp)
81cbf1ef
DW
2949{
2950 struct fuse_context *ctxt = fuse_get_context();
2951 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
2952 struct fuse2fs_file_handle *fh =
2953 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
2954 ext2_filsys fs;
2955 ext2_file_t efp;
2956 errcode_t err;
2957 int ret = 0;
2958
2959 FUSE2FS_CHECK_CONTEXT(ff);
2960 fs = ff->fs;
2961 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
2962 dbg_printf("%s: ino=%d len=%jd\n", __func__, fh->ino, len);
2963 pthread_mutex_lock(&ff->bfl);
2964 if (!fs_writeable(fs)) {
2965 ret = -EROFS;
2966 goto out;
2967 }
2968
2969 err = ext2fs_file_open(fs, fh->ino, fh->open_flags, &efp);
2970 if (err) {
2971 ret = translate_error(fs, fh->ino, err);
2972 goto out;
2973 }
2974
2975 err = ext2fs_file_set_size2(efp, len);
2976 if (err) {
2977 ret = translate_error(fs, fh->ino, err);
2978 goto out2;
2979 }
2980
2981out2:
2982 err = ext2fs_file_close(efp);
2983 if (ret)
2984 goto out;
2985 if (err) {
2986 ret = translate_error(fs, fh->ino, err);
2987 goto out;
2988 }
2989
2990 ret = update_mtime(fs, fh->ino, NULL);
2991 if (ret)
2992 goto out;
2993
2994out:
2995 pthread_mutex_unlock(&ff->bfl);
2996 return 0;
2997}
2998
4e222d9b
TT
2999static int op_fgetattr(const char *path EXT2FS_ATTR((unused)),
3000 struct stat *statbuf,
81cbf1ef
DW
3001 struct fuse_file_info *fp)
3002{
3003 struct fuse_context *ctxt = fuse_get_context();
3004 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
3005 ext2_filsys fs;
377e3a96
TT
3006 struct fuse2fs_file_handle *fh =
3007 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3008 int ret = 0;
3009
3010 FUSE2FS_CHECK_CONTEXT(ff);
3011 fs = ff->fs;
3012 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3013 dbg_printf("%s: ino=%d\n", __func__, fh->ino);
3014 pthread_mutex_lock(&ff->bfl);
3015 ret = stat_inode(fs, fh->ino, statbuf);
3016 pthread_mutex_unlock(&ff->bfl);
3017
3018 return ret;
3019}
3020
3021static int op_utimens(const char *path, const struct timespec ctv[2])
3022{
3023 struct fuse_context *ctxt = fuse_get_context();
3024 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
4e222d9b 3025 struct timespec tv[2];
81cbf1ef
DW
3026 ext2_filsys fs;
3027 errcode_t err;
3028 ext2_ino_t ino;
3029 struct ext2_inode_large inode;
3030 int ret = 0;
3031
3032 FUSE2FS_CHECK_CONTEXT(ff);
3033 fs = ff->fs;
3034 pthread_mutex_lock(&ff->bfl);
3035 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
3036 if (err) {
3037 ret = translate_error(fs, 0, err);
3038 goto out;
3039 }
3040 dbg_printf("%s: ino=%d\n", __func__, ino);
3041
3042 ret = check_inum_access(fs, ino, W_OK);
3043 if (ret)
3044 goto out;
3045
3046 memset(&inode, 0, sizeof(inode));
3047 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
3048 sizeof(inode));
3049 if (err) {
3050 ret = translate_error(fs, ino, err);
3051 goto out;
3052 }
3053
4e222d9b
TT
3054 tv[0] = ctv[0];
3055 tv[1] = ctv[1];
81cbf1ef
DW
3056#ifdef UTIME_NOW
3057 if (tv[0].tv_nsec == UTIME_NOW)
3058 get_now(tv);
3059 if (tv[1].tv_nsec == UTIME_NOW)
3060 get_now(tv + 1);
3061#endif /* UTIME_NOW */
3062#ifdef UTIME_OMIT
3063 if (tv[0].tv_nsec != UTIME_OMIT)
3064 EXT4_INODE_SET_XTIME(i_atime, tv, &inode);
3065 if (tv[1].tv_nsec != UTIME_OMIT)
3066 EXT4_INODE_SET_XTIME(i_mtime, tv + 1, &inode);
3067#endif /* UTIME_OMIT */
3068 ret = update_ctime(fs, ino, &inode);
3069 if (ret)
3070 goto out;
3071
3072 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
3073 sizeof(inode));
3074 if (err) {
3075 ret = translate_error(fs, ino, err);
3076 goto out;
3077 }
3078
3079out:
3080 pthread_mutex_unlock(&ff->bfl);
3081 return ret;
3082}
3083
3084#ifdef SUPPORT_I_FLAGS
3085static int ioctl_getflags(ext2_filsys fs, struct fuse2fs_file_handle *fh,
3086 void *data)
3087{
3088 errcode_t err;
3089 struct ext2_inode_large inode;
3090
3091 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3092 dbg_printf("%s: ino=%d\n", __func__, fh->ino);
3093 memset(&inode, 0, sizeof(inode));
3094 err = ext2fs_read_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3095 sizeof(inode));
3096 if (err)
3097 return translate_error(fs, fh->ino, err);
3098
3099 *(__u32 *)data = inode.i_flags & EXT2_FL_USER_VISIBLE;
3100 return 0;
3101}
3102
3103#define FUSE2FS_MODIFIABLE_IFLAGS \
3104 (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL | EXT2_NODUMP_FL | \
3105 EXT2_NOATIME_FL | EXT3_JOURNAL_DATA_FL | EXT2_DIRSYNC_FL | \
3106 EXT2_TOPDIR_FL)
3107
3108static int ioctl_setflags(ext2_filsys fs, struct fuse2fs_file_handle *fh,
3109 void *data)
3110{
3111 errcode_t err;
3112 struct ext2_inode_large inode;
3113 int ret;
3114 __u32 flags = *(__u32 *)data;
3115 struct fuse_context *ctxt = fuse_get_context();
3116
3117 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3118 dbg_printf("%s: ino=%d\n", __func__, fh->ino);
3119 memset(&inode, 0, sizeof(inode));
3120 err = ext2fs_read_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3121 sizeof(inode));
3122 if (err)
3123 return translate_error(fs, fh->ino, err);
3124
3125 if (ctxt->uid != 0 && inode.i_uid != ctxt->uid)
3126 return -EPERM;
3127
3128 if ((inode.i_flags ^ flags) & ~FUSE2FS_MODIFIABLE_IFLAGS)
3129 return -EINVAL;
3130
3131 inode.i_flags = (inode.i_flags & ~FUSE2FS_MODIFIABLE_IFLAGS) |
3132 (flags & FUSE2FS_MODIFIABLE_IFLAGS);
3133
3134 ret = update_ctime(fs, fh->ino, &inode);
3135 if (ret)
3136 return ret;
3137
3138 err = ext2fs_write_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3139 sizeof(inode));
3140 if (err)
3141 return translate_error(fs, fh->ino, err);
3142
3143 return 0;
3144}
3145
3146static int ioctl_getversion(ext2_filsys fs, struct fuse2fs_file_handle *fh,
3147 void *data)
3148{
3149 errcode_t err;
3150 struct ext2_inode_large inode;
3151
3152 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3153 dbg_printf("%s: ino=%d\n", __func__, fh->ino);
3154 memset(&inode, 0, sizeof(inode));
3155 err = ext2fs_read_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3156 sizeof(inode));
3157 if (err)
3158 return translate_error(fs, fh->ino, err);
3159
3160 *(__u32 *)data = inode.i_generation;
3161 return 0;
3162}
3163
3164static int ioctl_setversion(ext2_filsys fs, struct fuse2fs_file_handle *fh,
3165 void *data)
3166{
3167 errcode_t err;
3168 struct ext2_inode_large inode;
3169 int ret;
3170 __u32 generation = *(__u32 *)data;
3171 struct fuse_context *ctxt = fuse_get_context();
3172
3173 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3174 dbg_printf("%s: ino=%d\n", __func__, fh->ino);
3175 memset(&inode, 0, sizeof(inode));
3176 err = ext2fs_read_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3177 sizeof(inode));
3178 if (err)
3179 return translate_error(fs, fh->ino, err);
3180
3181 if (ctxt->uid != 0 && inode.i_uid != ctxt->uid)
3182 return -EPERM;
3183
3184 inode.i_generation = generation;
3185
3186 ret = update_ctime(fs, fh->ino, &inode);
3187 if (ret)
3188 return ret;
3189
3190 err = ext2fs_write_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3191 sizeof(inode));
3192 if (err)
3193 return translate_error(fs, fh->ino, err);
3194
3195 return 0;
3196}
3197#endif /* SUPPORT_I_FLAGS */
3198
3199#ifdef FITRIM
3200static int ioctl_fitrim(ext2_filsys fs, struct fuse2fs_file_handle *fh,
3201 void *data)
3202{
3203 struct fstrim_range *fr = data;
3204 blk64_t start, end, max_blocks, b, cleared;
3205 errcode_t err = 0;
3206
3207 start = fr->start / fs->blocksize;
3208 end = (fr->start + fr->len - 1) / fs->blocksize;
3209 dbg_printf("%s: start=%llu end=%llu\n", __func__, start, end);
3210
3211 if (start < fs->super->s_first_data_block)
3212 start = fs->super->s_first_data_block;
3213 if (start >= ext2fs_blocks_count(fs->super))
3214 start = ext2fs_blocks_count(fs->super) - 1;
3215
3216 if (end < fs->super->s_first_data_block)
3217 end = fs->super->s_first_data_block;
3218 if (end >= ext2fs_blocks_count(fs->super))
3219 end = ext2fs_blocks_count(fs->super) - 1;
3220
3221 cleared = 0;
3222 max_blocks = 2048ULL * 1024 * 1024 / fs->blocksize;
3223
3224 fr->len = 0;
3225 while (start <= end) {
3226 err = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
3227 start, end, &start);
3228 if (err == ENOENT)
3229 return 0;
3230 else if (err)
3231 return translate_error(fs, fh->ino, err);
3232
3233 b = start + max_blocks < end ? start + max_blocks : end;
3234 err = ext2fs_find_first_set_block_bitmap2(fs->block_map,
3235 start, b, &b);
3236 if (err && err != ENOENT)
3237 return translate_error(fs, fh->ino, err);
3238 if (b - start >= fr->minlen) {
3239 err = io_channel_discard(fs->io, start, b - start);
3240 if (err)
3241 return translate_error(fs, fh->ino, err);
3242 cleared += b - start;
3243 fr->len = cleared * fs->blocksize;
3244 }
3245 start = b + 1;
3246 }
3247
3248 return err;
3249}
3250#endif /* FITRIM */
3251
3252#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
4e222d9b
TT
3253static int op_ioctl(const char *path EXT2FS_ATTR((unused)), int cmd,
3254 void *arg EXT2FS_ATTR((unused)),
3255 struct fuse_file_info *fp,
3256 unsigned int flags EXT2FS_ATTR((unused)), void *data)
81cbf1ef
DW
3257{
3258 struct fuse_context *ctxt = fuse_get_context();
3259 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
3260 struct fuse2fs_file_handle *fh =
3261 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3262 ext2_filsys fs;
3263 int ret = 0;
3264
3265 FUSE2FS_CHECK_CONTEXT(ff);
3266 fs = ff->fs;
3267 pthread_mutex_lock(&ff->bfl);
4e222d9b 3268 switch ((unsigned long) cmd) {
81cbf1ef
DW
3269#ifdef SUPPORT_I_FLAGS
3270 case EXT2_IOC_GETFLAGS:
3271 ret = ioctl_getflags(fs, fh, data);
3272 break;
3273 case EXT2_IOC_SETFLAGS:
3274 ret = ioctl_setflags(fs, fh, data);
3275 break;
3276 case EXT2_IOC_GETVERSION:
3277 ret = ioctl_getversion(fs, fh, data);
3278 break;
3279 case EXT2_IOC_SETVERSION:
3280 ret = ioctl_setversion(fs, fh, data);
3281 break;
3282#endif
3283#ifdef FITRIM
3284 case FITRIM:
3285 ret = ioctl_fitrim(fs, fh, data);
3286 break;
3287#endif
3288 default:
3289 dbg_printf("%s: Unknown ioctl %d\n", __func__, cmd);
3290 ret = -ENOTTY;
3291 }
3292 pthread_mutex_unlock(&ff->bfl);
3293
3294 return ret;
3295}
3296#endif /* FUSE 28 */
3297
4e222d9b
TT
3298static int op_bmap(const char *path, size_t blocksize EXT2FS_ATTR((unused)),
3299 uint64_t *idx)
81cbf1ef
DW
3300{
3301 struct fuse_context *ctxt = fuse_get_context();
3302 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
3303 ext2_filsys fs;
3304 ext2_ino_t ino;
3305 errcode_t err;
3306 int ret = 0;
3307
3308 FUSE2FS_CHECK_CONTEXT(ff);
3309 fs = ff->fs;
3310 pthread_mutex_lock(&ff->bfl);
3311 err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
3312 if (err) {
3313 ret = translate_error(fs, 0, err);
3314 goto out;
3315 }
3316 dbg_printf("%s: ino=%d blk=%"PRIu64"\n", __func__, ino, *idx);
3317
3318 err = ext2fs_bmap2(fs, ino, NULL, NULL, 0, *idx, 0, (blk64_t *)idx);
3319 if (err) {
3320 ret = translate_error(fs, ino, err);
3321 goto out;
3322 }
3323
3324out:
3325 pthread_mutex_unlock(&ff->bfl);
3326 return ret;
3327}
3328
3329#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
3330# ifdef SUPPORT_FALLOCATE
3331static int fallocate_helper(struct fuse_file_info *fp, int mode, off_t offset,
3332 off_t len)
3333{
3334 struct fuse_context *ctxt = fuse_get_context();
3335 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
3336 struct fuse2fs_file_handle *fh =
3337 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3338 ext2_filsys fs;
3339 struct ext2_inode_large inode;
3340 blk64_t start, end;
3341 __u64 fsize;
3342 errcode_t err;
3343 int flags;
3344
3345 FUSE2FS_CHECK_CONTEXT(ff);
3346 fs = ff->fs;
3347 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3348 start = offset / fs->blocksize;
3349 end = (offset + len - 1) / fs->blocksize;
3350 dbg_printf("%s: ino=%d mode=0x%x start=%jd end=%llu\n", __func__,
3351 fh->ino, mode, offset / fs->blocksize, end);
3352 if (!fs_can_allocate(ff, len / fs->blocksize))
3353 return -ENOSPC;
3354
3355 memset(&inode, 0, sizeof(inode));
3356 err = ext2fs_read_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3357 sizeof(inode));
3358 if (err)
3359 return err;
3360 fsize = EXT2_I_SIZE(&inode);
3361
3362 /* Allocate a bunch of blocks */
3363 flags = (mode & FL_KEEP_SIZE_FLAG ? 0 :
3364 EXT2_FALLOCATE_INIT_BEYOND_EOF);
3365 err = ext2fs_fallocate(fs, flags, fh->ino,
3366 (struct ext2_inode *)&inode,
3367 ~0ULL, start, end - start + 1);
3368 if (err && err != EXT2_ET_BLOCK_ALLOC_FAIL)
3369 return translate_error(fs, fh->ino, err);
3370
3371 /* Update i_size */
3372 if (!(mode & FL_KEEP_SIZE_FLAG)) {
4e222d9b 3373 if ((__u64) offset + len > fsize) {
81cbf1ef
DW
3374 err = ext2fs_inode_size_set(fs,
3375 (struct ext2_inode *)&inode,
3376 offset + len);
3377 if (err)
3378 return translate_error(fs, fh->ino, err);
3379 }
3380 }
3381
3382 err = update_mtime(fs, fh->ino, &inode);
3383 if (err)
3384 return err;
3385
3386 err = ext2fs_write_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3387 sizeof(inode));
3388 if (err)
3389 return translate_error(fs, fh->ino, err);
3390
3391 return err;
3392}
3393
3394static errcode_t clean_block_middle(ext2_filsys fs, ext2_ino_t ino,
3395 struct ext2_inode_large *inode, off_t offset,
3396 off_t len, char **buf)
3397{
3398 blk64_t blk;
3399 off_t residue;
3400 int retflags;
3401 errcode_t err;
3402
3403 residue = offset % fs->blocksize;
3404 if (residue == 0)
3405 return 0;
3406
3407 if (!*buf) {
3408 err = ext2fs_get_mem(fs->blocksize, buf);
3409 if (err)
3410 return err;
3411 }
3412
3413 err = ext2fs_bmap2(fs, ino, (struct ext2_inode *)inode, *buf, 0,
3414 offset / fs->blocksize, &retflags, &blk);
3415 if (err)
3416 return err;
3417 if (!blk || (retflags & BMAP_RET_UNINIT))
3418 return 0;
3419
3420 err = io_channel_read_blk(fs->io, blk, 1, *buf);
3421 if (err)
3422 return err;
3423
3424 memset(*buf + residue, 0, len);
3425
3426 return io_channel_write_blk(fs->io, blk, 1, *buf);
3427}
3428
3429static errcode_t clean_block_edge(ext2_filsys fs, ext2_ino_t ino,
3430 struct ext2_inode_large *inode, off_t offset,
3431 int clean_before, char **buf)
3432{
3433 blk64_t blk;
3434 int retflags;
3435 off_t residue;
3436 errcode_t err;
3437
3438 residue = offset % fs->blocksize;
3439 if (residue == 0)
3440 return 0;
3441
3442 if (!*buf) {
3443 err = ext2fs_get_mem(fs->blocksize, buf);
3444 if (err)
3445 return err;
3446 }
3447
3448 err = ext2fs_bmap2(fs, ino, (struct ext2_inode *)inode, *buf, 0,
3449 offset / fs->blocksize, &retflags, &blk);
3450 if (err)
3451 return err;
3452
3453 err = io_channel_read_blk(fs->io, blk, 1, *buf);
3454 if (err)
3455 return err;
3456 if (!blk || (retflags & BMAP_RET_UNINIT))
3457 return 0;
3458
3459 if (clean_before)
3460 memset(*buf, 0, residue);
3461 else
3462 memset(*buf + residue, 0, fs->blocksize - residue);
3463
3464 return io_channel_write_blk(fs->io, blk, 1, *buf);
3465}
3466
3467static int punch_helper(struct fuse_file_info *fp, int mode, off_t offset,
3468 off_t len)
3469{
3470 struct fuse_context *ctxt = fuse_get_context();
3471 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
377e3a96
TT
3472 struct fuse2fs_file_handle *fh =
3473 (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
81cbf1ef
DW
3474 ext2_filsys fs;
3475 struct ext2_inode_large inode;
3476 blk64_t start, end;
3477 errcode_t err;
3478 char *buf = NULL;
3479
3480 FUSE2FS_CHECK_CONTEXT(ff);
3481 fs = ff->fs;
3482 FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
3483 dbg_printf("%s: offset=%jd len=%jd\n", __func__, offset, len);
3484
3485 /* kernel ext4 punch requires this flag to be set */
3486 if (!(mode & FL_KEEP_SIZE_FLAG))
3487 return -EINVAL;
3488
3489 /* Punch out a bunch of blocks */
3490 start = (offset + fs->blocksize - 1) / fs->blocksize;
3491 end = (offset + len - fs->blocksize) / fs->blocksize;
3492 dbg_printf("%s: ino=%d mode=0x%x start=%llu end=%llu\n", __func__,
3493 fh->ino, mode, start, end);
3494
3495 memset(&inode, 0, sizeof(inode));
3496 err = ext2fs_read_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3497 sizeof(inode));
3498 if (err)
3499 return translate_error(fs, fh->ino, err);
3500
3501 /* Zero everything before the first block and after the last block */
3502 if ((offset / fs->blocksize) == ((offset + len) / fs->blocksize))
3503 err = clean_block_middle(fs, fh->ino, &inode, offset,
3504 len, &buf);
3505 else {
3506 err = clean_block_edge(fs, fh->ino, &inode, offset, 0, &buf);
3507 if (!err)
3508 err = clean_block_edge(fs, fh->ino, &inode,
3509 offset + len, 1, &buf);
3510 }
3511 if (buf)
3512 ext2fs_free_mem(&buf);
3513 if (err)
3514 return translate_error(fs, fh->ino, err);
3515
3516 /* Unmap full blocks in the middle */
3517 if (start <= end) {
3518 err = ext2fs_punch(fs, fh->ino, (struct ext2_inode *)&inode,
3519 NULL, start, end);
3520 if (err)
3521 return translate_error(fs, fh->ino, err);
3522 }
3523
3524 err = update_mtime(fs, fh->ino, &inode);
3525 if (err)
3526 return err;
3527
3528 err = ext2fs_write_inode_full(fs, fh->ino, (struct ext2_inode *)&inode,
3529 sizeof(inode));
3530 if (err)
3531 return translate_error(fs, fh->ino, err);
3532
3533 return 0;
3534}
3535
4e222d9b
TT
3536static int op_fallocate(const char *path EXT2FS_ATTR((unused)), int mode,
3537 off_t offset, off_t len,
81cbf1ef
DW
3538 struct fuse_file_info *fp)
3539{
3540 struct fuse_context *ctxt = fuse_get_context();
3541 struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
3542 ext2_filsys fs = ff->fs;
3543 int ret;
3544
3545 /* Catch unknown flags */
3546 if (mode & ~(FL_PUNCH_HOLE_FLAG | FL_KEEP_SIZE_FLAG))
3547 return -EINVAL;
3548
3549 pthread_mutex_lock(&ff->bfl);
3550 if (!fs_writeable(fs)) {
3551 ret = -EROFS;
3552 goto out;
3553 }
3554 if (mode & FL_PUNCH_HOLE_FLAG)
3555 ret = punch_helper(fp, mode, offset, len);
3556 else
3557 ret = fallocate_helper(fp, mode, offset, len);
3558out:
3559 pthread_mutex_unlock(&ff->bfl);
3560
3561 return ret;
3562}
3563# endif /* SUPPORT_FALLOCATE */
3564#endif /* FUSE 29 */
3565
3566static struct fuse_operations fs_ops = {
3567 .init = op_init,
3568 .destroy = op_destroy,
3569 .getattr = op_getattr,
3570 .readlink = op_readlink,
3571 .mknod = op_mknod,
3572 .mkdir = op_mkdir,
3573 .unlink = op_unlink,
3574 .rmdir = op_rmdir,
3575 .symlink = op_symlink,
3576 .rename = op_rename,
3577 .link = op_link,
3578 .chmod = op_chmod,
3579 .chown = op_chown,
3580 .truncate = op_truncate,
3581 .open = op_open,
3582 .read = op_read,
3583 .write = op_write,
3584 .statfs = op_statfs,
3585 .release = op_release,
3586 .fsync = op_fsync,
3587 .setxattr = op_setxattr,
3588 .getxattr = op_getxattr,
3589 .listxattr = op_listxattr,
3590 .removexattr = op_removexattr,
3591 .opendir = op_open,
3592 .readdir = op_readdir,
3593 .releasedir = op_release,
3594 .fsyncdir = op_fsync,
3595 .access = op_access,
3596 .create = op_create,
3597 .ftruncate = op_ftruncate,
3598 .fgetattr = op_fgetattr,
3599 .utimens = op_utimens,
3600#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
3601# if defined(UTIME_NOW) || defined(UTIME_OMIT)
3602 .flag_utime_omit_ok = 1,
3603# endif
3604#endif
3605 .bmap = op_bmap,
3606#ifdef SUPERFLUOUS
3607 .lock = op_lock,
3608 .poll = op_poll,
3609#endif
3610#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
3611 .ioctl = op_ioctl,
3612 .flag_nullpath_ok = 1,
3613#endif
3614#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
3615 .flag_nopath = 1,
3616# ifdef SUPPORT_FALLOCATE
3617 .fallocate = op_fallocate,
3618# endif
3619#endif
3620};
3621
3622static int get_random_bytes(void *p, size_t sz)
3623{
3624 int fd;
3625 ssize_t r;
3626
4e222d9b 3627 fd = open("/dev/urandom", O_RDONLY);
81cbf1ef 3628 if (fd < 0) {
4e222d9b 3629 perror("/dev/urandom");
81cbf1ef
DW
3630 return 0;
3631 }
3632
3633 r = read(fd, p, sz);
3634
3635 close(fd);
4e222d9b 3636 return (size_t) r == sz;
81cbf1ef
DW
3637}
3638
69df8496
TT
3639enum {
3640 FUSE2FS_VERSION,
3641 FUSE2FS_HELP,
3642 FUSE2FS_HELPFULL,
3643};
3644
3645#define FUSE2FS_OPT(t, p, v) { t, offsetof(struct fuse2fs, p), v }
3646
3647static struct fuse_opt fuse2fs_opts[] = {
3648 FUSE2FS_OPT("ro", ro, 1),
3649 FUSE2FS_OPT("errors=panic", panic_on_error, 1),
3650 FUSE2FS_OPT("minixdf", minixdf, 1),
3651 FUSE2FS_OPT("fuse2fs_debug", debug, 1),
3652 FUSE2FS_OPT("no_default_opts", no_default_opts, 1),
3653
3654 FUSE_OPT_KEY("-V", FUSE2FS_VERSION),
3655 FUSE_OPT_KEY("--version", FUSE2FS_VERSION),
3656 FUSE_OPT_KEY("-h", FUSE2FS_HELP),
3657 FUSE_OPT_KEY("--help", FUSE2FS_HELP),
3658 FUSE_OPT_KEY("--helpfull", FUSE2FS_HELPFULL),
3659 FUSE_OPT_END
3660};
3661
3662
3663static int fuse2fs_opt_proc(void *data, const char *arg,
3664 int key, struct fuse_args *outargs)
81cbf1ef 3665{
69df8496
TT
3666 struct fuse2fs *ff = data;
3667
3668 switch (key) {
3669 case FUSE_OPT_KEY_NONOPT:
3670 if (!ff->device) {
3671 ff->device = strdup(arg);
3672 return 0;
3673 }
3674 return 1;
3675 case FUSE2FS_HELP:
3676 case FUSE2FS_HELPFULL:
3677 fprintf(stderr,
3678 "usage: %s device/image mountpoint [options]\n"
3679 "\n"
3680 "general options:\n"
3681 " -o opt,[opt...] mount options\n"
3682 " -h --help print help\n"
3683 " -V --version print version\n"
3684 "\n"
3685 "fuse2fs options:\n"
3686 " -o ro read-only mount\n"
3687 " -o errors=panic dump core on error\n"
3688 " -o minixdf minix-style df\n"
3689 " -o no_default_opts do not include default fuse options\n"
3690 " -o fuse2fs_debug enable fuse2fs debugging\n"
3691 "\n",
3692 outargs->argv[0]);
3693 if (key == FUSE2FS_HELPFULL) {
3694 fuse_opt_add_arg(outargs, "-ho");
3695 fuse_main(outargs->argc, outargs->argv, &fs_ops, NULL);
3696 } else {
3697 fprintf(stderr, "Try --helpfull to get a list of "
3698 "all flags, including the FUSE options.\n");
3699 }
3700 exit(1);
3701
3702 case FUSE2FS_VERSION:
3703 fprintf(stderr, "fuse2fs %s (%s)\n", E2FSPROGS_VERSION,
3704 E2FSPROGS_DATE);
3705 fuse_opt_add_arg(outargs, "--version");
3706 fuse_main(outargs->argc, outargs->argv, &fs_ops, NULL);
3707 exit(0);
3708 }
3709 return 1;
81cbf1ef
DW
3710}
3711
3712int main(int argc, char *argv[])
3713{
69df8496
TT
3714 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
3715 struct fuse2fs fctx;
81cbf1ef 3716 errcode_t err;
8feb6f43 3717 char *logfile;
81cbf1ef
DW
3718 char extra_args[BUFSIZ];
3719 int ret = 0, flags = EXT2_FLAG_64BITS | EXT2_FLAG_EXCLUSIVE;
3720
69df8496
TT
3721 memset(&fctx, 0, sizeof(fctx));
3722 fctx.magic = FUSE2FS_MAGIC;
81cbf1ef 3723
69df8496
TT
3724 fuse_opt_parse(&args, &fctx, fuse2fs_opts, fuse2fs_opt_proc);
3725 if (fctx.device == NULL) {
3726 fprintf(stderr, "Missing ext4 device/image\n");
3727 fprintf(stderr, "See '%s -h' for usage\n", argv[0]);
3728 exit(1);
81cbf1ef
DW
3729 }
3730
69df8496 3731 if (fctx.ro)
81cbf1ef
DW
3732 printf("%s", _("Mounting read-only.\n"));
3733
3734#ifdef ENABLE_NLS
3735 setlocale(LC_MESSAGES, "");
3736 setlocale(LC_CTYPE, "");
3737 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
3738 textdomain(NLS_CAT_NAME);
3739 set_com_err_gettext(gettext);
3740#endif
3741 add_error_table(&et_ext2_error_table);
3742
81cbf1ef
DW
3743 /* Set up error logging */
3744 logfile = getenv("FUSE2FS_LOGFILE");
3745 if (logfile) {
69df8496
TT
3746 fctx.err_fp = fopen(logfile, "a");
3747 if (!fctx.err_fp) {
81cbf1ef
DW
3748 perror(logfile);
3749 goto out_nofs;
3750 }
3751 } else
69df8496 3752 fctx.err_fp = stderr;
81cbf1ef
DW
3753
3754 /* Will we allow users to allocate every last block? */
3755 if (getenv("FUSE2FS_ALLOC_ALL_BLOCKS")) {
3756 printf(_("%s: Allowing users to allocate all blocks. "
69df8496
TT
3757 "This is dangerous!\n"), fctx.device);
3758 fctx.alloc_all_blocks = 1;
81cbf1ef
DW
3759 }
3760
3761 /* Start up the fs (while we still can use stdout) */
3762 ret = 2;
69df8496 3763 if (!fctx.ro)
81cbf1ef 3764 flags |= EXT2_FLAG_RW;
69df8496 3765 err = ext2fs_open2(fctx.device, NULL, flags, 0, 0, unix_io_manager,
81cbf1ef
DW
3766 &global_fs);
3767 if (err) {
69df8496
TT
3768 printf(_("%s: %s.\n"), fctx.device, error_message(err));
3769 printf(_("Please run e2fsck -fy %s.\n"), fctx.device);
81cbf1ef
DW
3770 goto out_nofs;
3771 }
69df8496
TT
3772 fctx.fs = global_fs;
3773 global_fs->priv_data = &fctx;
81cbf1ef
DW
3774
3775 ret = 3;
aca77cad 3776
7889640d 3777 if (ext2fs_has_feature_journal_needs_recovery(global_fs->super)) {
69df8496
TT
3778 if (!fctx.ro) {
3779 printf(_("%s: recovering journal\n"), fctx.device);
81cbf1ef
DW
3780 err = ext2fs_run_ext3_journal(&global_fs);
3781 if (err) {
69df8496 3782 printf(_("%s: %s.\n"), fctx.device,
81cbf1ef
DW
3783 error_message(err));
3784 printf(_("Please run e2fsck -fy %s.\n"),
69df8496 3785 fctx.device);
81cbf1ef
DW
3786 goto out;
3787 }
7889640d 3788 ext2fs_clear_feature_journal_needs_recovery(global_fs->super);
81cbf1ef
DW
3789 ext2fs_mark_super_dirty(global_fs);
3790 } else {
3791 printf("%s", _("Journal needs recovery; running "
3792 "`e2fsck -E journal_only' is required.\n"));
3793 goto out;
3794 }
3795 }
3796
69df8496 3797 if (!fctx.ro) {
7889640d 3798 if (ext2fs_has_feature_journal(global_fs->super))
81cbf1ef 3799 printf(_("%s: Writing to the journal is not supported.\n"),
69df8496 3800 fctx.device);
81cbf1ef
DW
3801 err = ext2fs_read_inode_bitmap(global_fs);
3802 if (err) {
3803 translate_error(global_fs, 0, err);
3804 goto out;
3805 }
3806 err = ext2fs_read_block_bitmap(global_fs);
3807 if (err) {
3808 translate_error(global_fs, 0, err);
3809 goto out;
3810 }
3811 }
3812
3813 if (!(global_fs->super->s_state & EXT2_VALID_FS))
3814 printf("%s", _("Warning: Mounting unchecked fs, running e2fsck "
3815 "is recommended.\n"));
3816 if (global_fs->super->s_max_mnt_count > 0 &&
3817 global_fs->super->s_mnt_count >= global_fs->super->s_max_mnt_count)
3818 printf("%s", _("Warning: Maximal mount count reached, running "
3819 "e2fsck is recommended.\n"));
3820 if (global_fs->super->s_checkinterval > 0 &&
377e3a96
TT
3821 (time_t) (global_fs->super->s_lastcheck +
3822 global_fs->super->s_checkinterval) <= time(0))
81cbf1ef
DW
3823 printf("%s", _("Warning: Check time reached; running e2fsck "
3824 "is recommended.\n"));
3825 if (global_fs->super->s_last_orphan)
3826 printf("%s",
3827 _("Orphans detected; running e2fsck is recommended.\n"));
3828
3829 if (global_fs->super->s_state & EXT2_ERROR_FS) {
3830 printf("%s",
3831 _("Errors detected; running e2fsck is required.\n"));
3832 goto out;
3833 }
3834
3835 /* Initialize generation counter */
69df8496 3836 get_random_bytes(&fctx.next_generation, sizeof(unsigned int));
81cbf1ef 3837
69df8496 3838 /* Set up default fuse parameters */
81cbf1ef 3839 snprintf(extra_args, BUFSIZ, "-okernel_cache,subtype=ext4,use_ino,"
69df8496 3840 "fsname=%s,attr_timeout=0" FUSE_PLATFORM_OPTS,
81cbf1ef 3841 argv[1]);
69df8496
TT
3842 if (fctx.no_default_opts == 0)
3843 fuse_opt_add_arg(&args, extra_args);
3844
3845 if (fctx.debug) {
3846 int i;
3847
3848 printf("fuse arguments:");
3849 for (i = 0; i < args.argc; i++)
3850 printf(" '%s'", args.argv[i]);
3851 printf("\n");
3852 }
81cbf1ef 3853
69df8496
TT
3854 pthread_mutex_init(&fctx.bfl, NULL);
3855 fuse_main(args.argc, args.argv, &fs_ops, &fctx);
3856 pthread_mutex_destroy(&fctx.bfl);
81cbf1ef
DW
3857
3858 ret = 0;
3859out:
3860 err = ext2fs_close(global_fs);
3861 if (err)
3862 com_err(argv[0], err, "while closing fs");
3863 global_fs = NULL;
3864out_nofs:
81cbf1ef
DW
3865
3866 return ret;
3867}
3868
3869static int __translate_error(ext2_filsys fs, errcode_t err, ext2_ino_t ino,
3870 const char *file, int line)
3871{
3872 struct timespec now;
3873 int ret = err;
3874 struct fuse2fs *ff = fs->priv_data;
3875 int is_err = 0;
3876
3877 /* Translate ext2 error to unix error code */
3878 if (err < EXT2_ET_BASE)
3879 goto no_translation;
3880 switch (err) {
3881 case EXT2_ET_NO_MEMORY:
3882 case EXT2_ET_TDB_ERR_OOM:
3883 ret = -ENOMEM;
3884 break;
3885 case EXT2_ET_INVALID_ARGUMENT:
3886 case EXT2_ET_LLSEEK_FAILED:
3887 ret = -EINVAL;
3888 break;
3889 case EXT2_ET_NO_DIRECTORY:
3890 ret = -ENOTDIR;
3891 break;
3892 case EXT2_ET_FILE_NOT_FOUND:
3893 ret = -ENOENT;
3894 break;
3895 case EXT2_ET_DIR_NO_SPACE:
3896 is_err = 1;
da9d5b80 3897 /* fallthrough */
81cbf1ef
DW
3898 case EXT2_ET_TOOSMALL:
3899 case EXT2_ET_BLOCK_ALLOC_FAIL:
3900 case EXT2_ET_INODE_ALLOC_FAIL:
3901 case EXT2_ET_EA_NO_SPACE:
3902 ret = -ENOSPC;
3903 break;
3904 case EXT2_ET_SYMLINK_LOOP:
3905 ret = -EMLINK;
3906 break;
3907 case EXT2_ET_FILE_TOO_BIG:
3908 ret = -EFBIG;
3909 break;
3910 case EXT2_ET_TDB_ERR_EXISTS:
3911 case EXT2_ET_FILE_EXISTS:
3912 ret = -EEXIST;
3913 break;
3914 case EXT2_ET_MMP_FAILED:
3915 case EXT2_ET_MMP_FSCK_ON:
3916 ret = -EBUSY;
3917 break;
3918 case EXT2_ET_EA_KEY_NOT_FOUND:
3919#ifdef ENODATA
3920 ret = -ENODATA;
3921#else
3922 ret = -ENOENT;
3923#endif
3924 break;
3925 /* Sometimes fuse returns a garbage file handle pointer to us... */
3926 case EXT2_ET_MAGIC_EXT2_FILE:
3927 ret = -EFAULT;
3928 break;
3929 case EXT2_ET_UNIMPLEMENTED:
3930 ret = -EOPNOTSUPP;
3931 break;
3932 default:
3933 is_err = 1;
3934 ret = -EIO;
3935 break;
3936 }
3937
3938no_translation:
3939 if (!is_err)
3940 return ret;
3941
3942 if (ino)
3943 fprintf(ff->err_fp, "FUSE2FS (%s): %s (inode #%d) at %s:%d.\n",
da9d5b80 3944 fs->device_name ? fs->device_name : "???",
81cbf1ef
DW
3945 error_message(err), ino, file, line);
3946 else
3947 fprintf(ff->err_fp, "FUSE2FS (%s): %s at %s:%d.\n",
da9d5b80 3948 fs->device_name ? fs->device_name : "???",
81cbf1ef
DW
3949 error_message(err), file, line);
3950 fflush(ff->err_fp);
3951
3952 /* Make a note in the error log */
3953 get_now(&now);
3954 fs->super->s_last_error_time = now.tv_sec;
3955 fs->super->s_last_error_ino = ino;
3956 fs->super->s_last_error_line = line;
3957 fs->super->s_last_error_block = err; /* Yeah... */
3958 strncpy((char *)fs->super->s_last_error_func, file,
3959 sizeof(fs->super->s_last_error_func));
3960 if (fs->super->s_first_error_time == 0) {
3961 fs->super->s_first_error_time = now.tv_sec;
3962 fs->super->s_first_error_ino = ino;
3963 fs->super->s_first_error_line = line;
3964 fs->super->s_first_error_block = err;
3965 strncpy((char *)fs->super->s_first_error_func, file,
3966 sizeof(fs->super->s_first_error_func));
3967 }
3968
3969 fs->super->s_error_count++;
3970 ext2fs_mark_super_dirty(fs);
3971 ext2fs_flush(fs);
3972 if (ff->panic_on_error)
3973 abort();
3974
3975 return ret;
3976}