Skip to content

Commit 3b7008b

Browse files
noh4hssMiklos Szeredi
authored andcommitted
fuse: return -ECONNABORTED on /dev/fuse read after abort
Currently the userspace has no way of knowing whether the fuse connection ended because of umount or abort via sysfs. It makes it hard for filesystems to free the mountpoint after abort without worrying about removing some new mount. The patch fixes it by returning different errors when userspace reads from /dev/fuse (-ENODEV for umount and -ECONNABORTED for abort). Add a new capability flag FUSE_ABORT_ERROR. If set and the connection is gone because of sysfs abort, reading from the device will return -ECONNABORTED. Signed-off-by: Szymon Lukasz <noh4hss@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent df0e91d commit 3b7008b

File tree

6 files changed

+29
-13
lines changed

6 files changed

+29
-13
lines changed

fs/fuse/control.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf,
3535
{
3636
struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
3737
if (fc) {
38-
fuse_abort_conn(fc);
38+
fuse_abort_conn(fc, true);
3939
fuse_conn_put(fc);
4040
}
4141
return count;

fs/fuse/cuse.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
406406
err_region:
407407
unregister_chrdev_region(devt, 1);
408408
err:
409-
fuse_abort_conn(fc);
409+
fuse_abort_conn(fc, false);
410410
goto out;
411411
}
412412

@@ -581,7 +581,7 @@ static ssize_t cuse_class_abort_store(struct device *dev,
581581
{
582582
struct cuse_conn *cc = dev_get_drvdata(dev);
583583

584-
fuse_abort_conn(&cc->fc);
584+
fuse_abort_conn(&cc->fc, false);
585585
return count;
586586
}
587587
static DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store);

fs/fuse/dev.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,9 +1234,10 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
12341234
if (err)
12351235
goto err_unlock;
12361236

1237-
err = -ENODEV;
1238-
if (!fiq->connected)
1237+
if (!fiq->connected) {
1238+
err = (fc->aborted && fc->abort_err) ? -ECONNABORTED : -ENODEV;
12391239
goto err_unlock;
1240+
}
12401241

12411242
if (!list_empty(&fiq->interrupts)) {
12421243
req = list_entry(fiq->interrupts.next, struct fuse_req,
@@ -1287,7 +1288,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
12871288
spin_lock(&fpq->lock);
12881289
clear_bit(FR_LOCKED, &req->flags);
12891290
if (!fpq->connected) {
1290-
err = -ENODEV;
1291+
err = (fc->aborted && fc->abort_err) ? -ECONNABORTED : -ENODEV;
12911292
goto out_end;
12921293
}
12931294
if (err) {
@@ -2076,7 +2077,7 @@ static void end_polls(struct fuse_conn *fc)
20762077
* is OK, the request will in that case be removed from the list before we touch
20772078
* it.
20782079
*/
2079-
void fuse_abort_conn(struct fuse_conn *fc)
2080+
void fuse_abort_conn(struct fuse_conn *fc, bool is_abort)
20802081
{
20812082
struct fuse_iqueue *fiq = &fc->iq;
20822083

@@ -2089,6 +2090,7 @@ void fuse_abort_conn(struct fuse_conn *fc)
20892090

20902091
fc->connected = 0;
20912092
fc->blocked = 0;
2093+
fc->aborted = is_abort;
20922094
fuse_set_initialized(fc);
20932095
list_for_each_entry(fud, &fc->devices, entry) {
20942096
struct fuse_pqueue *fpq = &fud->pq;
@@ -2151,7 +2153,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)
21512153
/* Are we the last open device? */
21522154
if (atomic_dec_and_test(&fc->dev_count)) {
21532155
WARN_ON(fc->iq.fasync != NULL);
2154-
fuse_abort_conn(fc);
2156+
fuse_abort_conn(fc, false);
21552157
}
21562158
fuse_dev_free(fud);
21572159
}

fs/fuse/fuse_i.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,9 @@ struct fuse_conn {
515515
abort and device release */
516516
unsigned connected;
517517

518+
/** Connection aborted via sysfs */
519+
bool aborted;
520+
518521
/** Connection failed (version mismatch). Cannot race with
519522
setting other bitfields since it is only set once in INIT
520523
reply, before any other request, and never cleared */
@@ -526,6 +529,9 @@ struct fuse_conn {
526529
/** Do readpages asynchronously? Only set in INIT */
527530
unsigned async_read:1;
528531

532+
/** Return an unique read error after abort. Only set in INIT */
533+
unsigned abort_err:1;
534+
529535
/** Do not send separate SETATTR request before open(O_TRUNC) */
530536
unsigned atomic_o_trunc:1;
531537

@@ -851,7 +857,7 @@ void fuse_request_send_background_locked(struct fuse_conn *fc,
851857
struct fuse_req *req);
852858

853859
/* Abort all requests */
854-
void fuse_abort_conn(struct fuse_conn *fc);
860+
void fuse_abort_conn(struct fuse_conn *fc, bool is_abort);
855861

856862
/**
857863
* Invalidate inode attributes

fs/fuse/inode.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ void fuse_unlock_inode(struct inode *inode)
371371

372372
static void fuse_umount_begin(struct super_block *sb)
373373
{
374-
fuse_abort_conn(get_fuse_conn_super(sb));
374+
fuse_abort_conn(get_fuse_conn_super(sb), false);
375375
}
376376

377377
static void fuse_send_destroy(struct fuse_conn *fc)
@@ -393,7 +393,7 @@ static void fuse_put_super(struct super_block *sb)
393393

394394
fuse_send_destroy(fc);
395395

396-
fuse_abort_conn(fc);
396+
fuse_abort_conn(fc, false);
397397
mutex_lock(&fuse_mutex);
398398
list_del(&fc->entry);
399399
fuse_ctl_remove_conn(fc);
@@ -918,6 +918,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
918918
fc->posix_acl = 1;
919919
fc->sb->s_xattr = fuse_acl_xattr_handlers;
920920
}
921+
if (arg->flags & FUSE_ABORT_ERROR)
922+
fc->abort_err = 1;
921923
} else {
922924
ra_pages = fc->max_read / PAGE_SIZE;
923925
fc->no_lock = 1;
@@ -948,7 +950,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
948950
FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
949951
FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
950952
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
951-
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL;
953+
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
954+
FUSE_ABORT_ERROR;
952955
req->in.h.opcode = FUSE_INIT;
953956
req->in.numargs = 1;
954957
req->in.args[0].size = sizeof(*arg);

include/uapi/linux/fuse.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@
113113
* 7.26
114114
* - add FUSE_HANDLE_KILLPRIV
115115
* - add FUSE_POSIX_ACL
116+
*
117+
* 7.27
118+
* - add FUSE_ABORT_ERROR
116119
*/
117120

118121
#ifndef _LINUX_FUSE_H
@@ -148,7 +151,7 @@
148151
#define FUSE_KERNEL_VERSION 7
149152

150153
/** Minor version number of this interface */
151-
#define FUSE_KERNEL_MINOR_VERSION 26
154+
#define FUSE_KERNEL_MINOR_VERSION 27
152155

153156
/** The node ID of the root inode */
154157
#define FUSE_ROOT_ID 1
@@ -245,6 +248,7 @@ struct fuse_file_lock {
245248
* FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir
246249
* FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc
247250
* FUSE_POSIX_ACL: filesystem supports posix acls
251+
* FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED
248252
*/
249253
#define FUSE_ASYNC_READ (1 << 0)
250254
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -267,6 +271,7 @@ struct fuse_file_lock {
267271
#define FUSE_PARALLEL_DIROPS (1 << 18)
268272
#define FUSE_HANDLE_KILLPRIV (1 << 19)
269273
#define FUSE_POSIX_ACL (1 << 20)
274+
#define FUSE_ABORT_ERROR (1 << 21)
270275

271276
/**
272277
* CUSE INIT request/reply flags

0 commit comments

Comments
 (0)