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