1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * FUSE passthrough to backing file. 4 * 5 * Copyright (c) 2023 CTERA Networks. 6 */ 7 8 #include "dev.h" 9 #include "fuse_i.h" 10 11 #include <linux/file.h> 12 13 struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) 14 { 15 if (fb && refcount_inc_not_zero(&fb->count)) 16 return fb; 17 return NULL; 18 } 19 20 static void fuse_backing_free(struct fuse_backing *fb) 21 { 22 pr_debug("%s: fb=0x%p\n", __func__, fb); 23 24 if (fb->file) 25 fput(fb->file); 26 put_cred(fb->cred); 27 kfree_rcu(fb, rcu); 28 } 29 30 void fuse_backing_put(struct fuse_backing *fb) 31 { 32 if (fb && refcount_dec_and_test(&fb->count)) 33 fuse_backing_free(fb); 34 } 35 36 void fuse_backing_files_init(struct fuse_conn *fc) 37 { 38 idr_init(&fc->backing_files_map); 39 } 40 41 static int fuse_backing_id_alloc(struct fuse_conn *fc, struct fuse_backing *fb) 42 { 43 int id; 44 45 idr_preload(GFP_KERNEL); 46 spin_lock(&fc->lock); 47 /* FIXME: xarray might be space inefficient */ 48 id = idr_alloc_cyclic(&fc->backing_files_map, fb, 1, 0, GFP_ATOMIC); 49 spin_unlock(&fc->lock); 50 idr_preload_end(); 51 52 WARN_ON_ONCE(id == 0); 53 return id; 54 } 55 56 static struct fuse_backing *fuse_backing_id_remove(struct fuse_conn *fc, 57 int id) 58 { 59 struct fuse_backing *fb; 60 61 spin_lock(&fc->lock); 62 fb = idr_remove(&fc->backing_files_map, id); 63 spin_unlock(&fc->lock); 64 65 return fb; 66 } 67 68 static int fuse_backing_id_free(int id, void *p, void *data) 69 { 70 struct fuse_backing *fb = p; 71 72 WARN_ON_ONCE(refcount_read(&fb->count) != 1); 73 fuse_backing_free(fb); 74 return 0; 75 } 76 77 void fuse_backing_files_free(struct fuse_conn *fc) 78 { 79 idr_for_each(&fc->backing_files_map, fuse_backing_id_free, NULL); 80 idr_destroy(&fc->backing_files_map); 81 } 82 83 int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map) 84 { 85 struct file *file; 86 struct super_block *backing_sb; 87 struct fuse_backing *fb = NULL; 88 int res; 89 90 pr_debug("%s: fd=%d flags=0x%x\n", __func__, map->fd, map->flags); 91 92 /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */ 93 res = -EPERM; 94 if (!fc->passthrough || !capable(CAP_SYS_ADMIN)) 95 goto out; 96 97 res = -EINVAL; 98 if (map->flags || map->padding) 99 goto out; 100 101 file = fget_raw(map->fd); 102 res = -EBADF; 103 if (!file) 104 goto out; 105 106 /* read/write/splice/mmap passthrough only relevant for regular files */ 107 res = d_is_dir(file->f_path.dentry) ? -EISDIR : -EINVAL; 108 if (!d_is_reg(file->f_path.dentry)) 109 goto out_fput; 110 111 backing_sb = file_inode(file)->i_sb; 112 res = -ELOOP; 113 if (backing_sb->s_stack_depth >= fc->max_stack_depth) 114 goto out_fput; 115 116 fb = kmalloc_obj(struct fuse_backing); 117 res = -ENOMEM; 118 if (!fb) 119 goto out_fput; 120 121 fb->file = file; 122 fb->cred = get_current_cred(); 123 refcount_set(&fb->count, 1); 124 125 res = fuse_backing_id_alloc(fc, fb); 126 if (res < 0) { 127 fuse_backing_free(fb); 128 fb = NULL; 129 } 130 131 out: 132 pr_debug("%s: fb=0x%p, ret=%i\n", __func__, fb, res); 133 134 return res; 135 136 out_fput: 137 fput(file); 138 goto out; 139 } 140 141 int fuse_backing_close(struct fuse_conn *fc, int backing_id) 142 { 143 struct fuse_backing *fb = NULL; 144 int err; 145 146 pr_debug("%s: backing_id=%d\n", __func__, backing_id); 147 148 /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */ 149 err = -EPERM; 150 if (!fc->passthrough || !capable(CAP_SYS_ADMIN)) 151 goto out; 152 153 err = -EINVAL; 154 if (backing_id <= 0) 155 goto out; 156 157 err = -ENOENT; 158 fb = fuse_backing_id_remove(fc, backing_id); 159 if (!fb) 160 goto out; 161 162 fuse_backing_put(fb); 163 err = 0; 164 out: 165 pr_debug("%s: fb=0x%p, err=%i\n", __func__, fb, err); 166 167 return err; 168 } 169 170 struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, int backing_id) 171 { 172 struct fuse_backing *fb; 173 174 rcu_read_lock(); 175 fb = idr_find(&fc->backing_files_map, backing_id); 176 fb = fuse_backing_get(fb); 177 rcu_read_unlock(); 178 179 return fb; 180 } 181