1// SPDX-License-Identifier: GPL-2.0 2/* 3 * fs/hmdfs/file_local.c 4 * 5 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. 6 */ 7 8#include <linux/file.h> 9#include <linux/fs.h> 10#include <linux/namei.h> 11#include <linux/page-flags.h> 12#include <linux/pagemap.h> 13#include <linux/sched/signal.h> 14#include <linux/slab.h> 15 16#include "hmdfs_client.h" 17#include "hmdfs_dentryfile.h" 18#include "hmdfs_device_view.h" 19#include "hmdfs_merge_view.h" 20#include "hmdfs_share.h" 21#include "hmdfs_trace.h" 22 23int hmdfs_file_open_local(struct inode *inode, struct file *file) 24{ 25 int err = 0; 26 struct file *lower_file = NULL; 27 struct path lower_path; 28 struct super_block *sb = inode->i_sb; 29 const struct cred *cred = hmdfs_sb(sb)->cred; 30 struct hmdfs_file_info *gfi = kzalloc(sizeof(*gfi), GFP_KERNEL); 31 struct hmdfs_inode_info *info = hmdfs_i(inode); 32 33 if (!gfi) { 34 err = -ENOMEM; 35 goto out_err; 36 } 37 38 hmdfs_get_lower_path(file->f_path.dentry, &lower_path); 39 if (inode->i_mapping != NULL && 40 inode->i_mapping->a_ops == &hmdfs_aops_cloud) 41 lower_file = dentry_open(&lower_path, file->f_flags | O_DIRECT, 42 cred); 43 else 44 lower_file = dentry_open(&lower_path, file->f_flags, cred); 45 hmdfs_put_lower_path(&lower_path); 46 if (IS_ERR(lower_file)) { 47 err = PTR_ERR(lower_file); 48 kfree(gfi); 49 } else { 50 gfi->lower_file = lower_file; 51 file->private_data = gfi; 52 hmdfs_update_upper_file(file, lower_file); 53 if (file->f_flags & (O_RDWR | O_WRONLY)) 54 atomic_inc(&info->write_opened); 55 } 56out_err: 57 return err; 58} 59 60int hmdfs_file_release_local(struct inode *inode, struct file *file) 61{ 62 struct hmdfs_file_info *gfi = hmdfs_f(file); 63 struct hmdfs_inode_info *info = hmdfs_i(inode); 64 65 if (file->f_flags & (O_RDWR | O_WRONLY)) 66 atomic_dec(&info->write_opened); 67 file->private_data = NULL; 68 fput(gfi->lower_file); 69 kfree(gfi); 70 return 0; 71} 72 73static void hmdfs_file_accessed(struct file *file) 74{ 75 struct file *lower_file = hmdfs_f(file)->lower_file; 76 struct inode *inode = file_inode(file); 77 struct inode *lower_inode = file_inode(lower_file); 78 79 if (file->f_flags & O_NOATIME) 80 return; 81 82 inode->i_atime = lower_inode->i_atime; 83} 84 85ssize_t hmdfs_do_read_iter(struct file *file, struct iov_iter *iter, 86 loff_t *ppos) 87{ 88 ssize_t ret; 89 struct file *lower_file = hmdfs_f(file)->lower_file; 90 struct kiocb *iocb; 91 92 if (!iov_iter_count(iter)) 93 return 0; 94 95 if (file->f_inode->i_mapping != NULL && 96 file->f_inode->i_mapping->a_ops == &hmdfs_aops_cloud) { 97 iocb = container_of(ppos, struct kiocb, ki_pos); 98 ret = generic_file_read_iter(iocb, iter); 99 } else { 100 ret = vfs_iter_read(lower_file, iter, ppos, 0); 101 } 102 hmdfs_file_accessed(file); 103 104 return ret; 105} 106 107static ssize_t hmdfs_local_read_iter(struct kiocb *iocb, struct iov_iter *iter) 108{ 109 return hmdfs_do_read_iter(iocb->ki_filp, iter, &iocb->ki_pos); 110} 111 112static void hmdfs_file_modified(struct file *file) 113{ 114 struct inode *inode = file_inode(file); 115 struct dentry *dentry = file_dentry(file); 116 struct file *lower_file = hmdfs_f(file)->lower_file; 117 struct inode *lower_inode = file_inode(lower_file); 118 119 inode->i_atime = lower_inode->i_atime; 120 inode->i_ctime = lower_inode->i_ctime; 121 inode->i_mtime = lower_inode->i_mtime; 122 i_size_write(inode, i_size_read(lower_inode)); 123 124 if (!hmdfs_i_merge(hmdfs_i(inode))) 125 update_inode_to_dentry(dentry, inode); 126} 127 128ssize_t hmdfs_do_write_iter(struct file *file, struct iov_iter *iter, 129 loff_t *ppos) 130{ 131 ssize_t ret; 132 struct file *lower_file = hmdfs_f(file)->lower_file; 133 struct inode *inode = file_inode(file); 134 135 if (!iov_iter_count(iter)) 136 return 0; 137 138 inode_lock(inode); 139 140 ret = file_remove_privs(file); 141 if (ret) 142 goto out_unlock; 143 144 file_start_write(lower_file); 145 ret = vfs_iter_write(lower_file, iter, ppos, 0); 146 file_end_write(lower_file); 147 148 hmdfs_file_modified(file); 149 150out_unlock: 151 inode_unlock(inode); 152 return ret; 153} 154 155ssize_t hmdfs_local_write_iter(struct kiocb *iocb, struct iov_iter *iter) 156{ 157 return hmdfs_do_write_iter(iocb->ki_filp, iter, &iocb->ki_pos); 158} 159 160int hmdfs_fsync_local(struct file *file, loff_t start, loff_t end, int datasync) 161{ 162 int err; 163 struct file *lower_file = hmdfs_f(file)->lower_file; 164 165 err = __generic_file_fsync(file, start, end, datasync); 166 if (err) 167 goto out; 168 169 err = vfs_fsync_range(lower_file, start, end, datasync); 170out: 171 return err; 172} 173 174loff_t hmdfs_file_llseek_local(struct file *file, loff_t offset, int whence) 175{ 176 loff_t ret; 177 struct file *lower_file; 178 179 lower_file = hmdfs_f(file)->lower_file; 180 lower_file->f_pos = file->f_pos; 181 ret = vfs_llseek(lower_file, offset, whence); 182 file->f_pos = lower_file->f_pos; 183 184 return ret; 185} 186 187int hmdfs_file_mmap_local(struct file *file, struct vm_area_struct *vma) 188{ 189 struct hmdfs_file_info *private_data = file->private_data; 190 struct file *realfile = NULL; 191 int ret; 192 193 if (!private_data) 194 return -EINVAL; 195 196 realfile = private_data->lower_file; 197 if (!realfile) 198 return -EINVAL; 199 200 if (!realfile->f_op->mmap) 201 return -ENODEV; 202 203 if (WARN_ON(file != vma->vm_file)) 204 return -EIO; 205 206 vma->vm_file = get_file(realfile); 207 ret = call_mmap(vma->vm_file, vma); 208 if (ret) 209 fput(realfile); 210 else 211 fput(file); 212 213 file_accessed(file); 214 215 return ret; 216} 217 218const struct file_operations hmdfs_file_fops_local = { 219 .owner = THIS_MODULE, 220 .llseek = hmdfs_file_llseek_local, 221 .read_iter = hmdfs_local_read_iter, 222 .write_iter = hmdfs_local_write_iter, 223 .mmap = hmdfs_file_mmap_local, 224 .open = hmdfs_file_open_local, 225 .release = hmdfs_file_release_local, 226 .fsync = hmdfs_fsync_local, 227 .splice_read = generic_file_splice_read, 228 .splice_write = iter_file_splice_write, 229}; 230 231static int hmdfs_iterate_local(struct file *file, struct dir_context *ctx) 232{ 233 int err = 0; 234 loff_t start_pos = ctx->pos; 235 struct file *lower_file = hmdfs_f(file)->lower_file; 236 237 if (ctx->pos == -1) 238 return 0; 239 240 lower_file->f_pos = file->f_pos; 241 err = iterate_dir(lower_file, ctx); 242 file->f_pos = lower_file->f_pos; 243 244 if (err < 0) 245 ctx->pos = -1; 246 247 trace_hmdfs_iterate_local(file->f_path.dentry, start_pos, ctx->pos, 248 err); 249 return err; 250} 251 252int hmdfs_dir_open_local(struct inode *inode, struct file *file) 253{ 254 int err = 0; 255 struct file *lower_file = NULL; 256 struct dentry *dentry = file->f_path.dentry; 257 struct path lower_path; 258 struct super_block *sb = inode->i_sb; 259 const struct cred *cred = hmdfs_sb(sb)->cred; 260 struct hmdfs_file_info *gfi = kzalloc(sizeof(*gfi), GFP_KERNEL); 261 262 if (!gfi) 263 return -ENOMEM; 264 265 if (IS_ERR_OR_NULL(cred)) { 266 err = -EPERM; 267 goto out_err; 268 } 269 hmdfs_get_lower_path(dentry, &lower_path); 270 lower_file = dentry_open(&lower_path, file->f_flags, cred); 271 hmdfs_put_lower_path(&lower_path); 272 if (IS_ERR(lower_file)) { 273 err = PTR_ERR(lower_file); 274 goto out_err; 275 } else { 276 gfi->lower_file = lower_file; 277 file->private_data = gfi; 278 } 279 return err; 280 281out_err: 282 kfree(gfi); 283 return err; 284} 285 286static int hmdfs_dir_release_local(struct inode *inode, struct file *file) 287{ 288 struct hmdfs_file_info *gfi = hmdfs_f(file); 289 290 file->private_data = NULL; 291 fput(gfi->lower_file); 292 kfree(gfi); 293 return 0; 294} 295 296const struct file_operations hmdfs_dir_ops_local = { 297 .owner = THIS_MODULE, 298 .iterate = hmdfs_iterate_local, 299 .open = hmdfs_dir_open_local, 300 .release = hmdfs_dir_release_local, 301 .fsync = hmdfs_fsync_local, 302}; 303 304static int __hmdfs_ioc_set_share_path(struct file *file, 305 struct hmdfs_share_control *sc) 306{ 307 struct super_block *sb = file->f_inode->i_sb; 308 struct hmdfs_sb_info *sbi = hmdfs_sb(sb); 309 struct hmdfs_share_table *st = &sbi->share_table; 310 struct hmdfs_share_item *item; 311 struct dentry *dentry; 312 const char *dir_path, *full_path; 313 struct qstr relative_path; 314 struct fd src; 315 int err = 0; 316 317 src = fdget(sc->src_fd); 318 if (!src.file) 319 return -EBADF; 320 321 /* only reg file can be shared */ 322 if (!S_ISREG(src.file->f_inode->i_mode)) { 323 err = -EPERM; 324 goto err_out; 325 } 326 327 /* share file is not allowed to be shared */ 328 if (hmdfs_is_share_file(src.file)) { 329 err = -EPERM; 330 goto err_out; 331 } 332 333 dentry = src.file->f_path.dentry; 334 if (dentry->d_name.len > NAME_MAX) { 335 err = -ENAMETOOLONG; 336 goto err_out; 337 } 338 339 dir_path = hmdfs_get_dentry_relative_path(file->f_path.dentry); 340 if (unlikely(!dir_path)) { 341 err = -ENOMEM; 342 goto err_out; 343 } 344 345 full_path = hmdfs_connect_path(dir_path, dentry->d_name.name); 346 if (unlikely(!full_path)) { 347 err = -ENOMEM; 348 goto free_dir; 349 } 350 relative_path.name = full_path; 351 relative_path.len = strlen(full_path); 352 353 spin_lock(&sbi->share_table.item_list_lock); 354 item = hmdfs_lookup_share_item(st, &relative_path); 355 if (!item) { 356 err = insert_share_item(st, &relative_path, src.file, sc->cid); 357 goto unlock; 358 } 359 360 if (item->opened) 361 err = -EEXIST; 362 else 363 update_share_item(item, src.file, sc->cid); 364 365unlock: 366 spin_unlock(&sbi->share_table.item_list_lock); 367 kfree(full_path); 368free_dir: 369 kfree(dir_path); 370err_out: 371 fdput(src); 372 return err; 373} 374 375static int hmdfs_ioc_set_share_path(struct file *file, unsigned long arg) 376{ 377 struct hmdfs_share_control sc; 378 379 if (copy_from_user(&sc, (struct hmdfs_share_control __user *)arg, 380 sizeof(sc))) 381 return -EFAULT; 382 383 return __hmdfs_ioc_set_share_path(file, &sc); 384} 385 386static long hmdfs_dir_ioctl_local(struct file *file, unsigned int cmd, 387 unsigned long arg) 388{ 389 switch (cmd) { 390 case HMDFS_IOC_SET_SHARE_PATH: 391 return hmdfs_ioc_set_share_path(file, arg); 392 default: 393 return -ENOTTY; 394 } 395} 396 397const struct file_operations hmdfs_dir_ops_share = { 398 .owner = THIS_MODULE, 399 .iterate = hmdfs_iterate_local, 400 .open = hmdfs_dir_open_local, 401 .release = hmdfs_dir_release_local, 402 .fsync = hmdfs_fsync_local, 403 .unlocked_ioctl = hmdfs_dir_ioctl_local, 404 .compat_ioctl = hmdfs_dir_ioctl_local, 405}; 406