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