| /* Copyright (c) 2018 Google Inc |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * See LICENSE for details. |
| * |
| * fs_file: structs and helpers for files for 9ns devices |
| */ |
| |
| #pragma once |
| |
| #include <ns.h> |
| #include <pagemap.h> |
| |
| /* fs_file has all the info of the 9ns dir and the dirtab (which is a subset of |
| * struct dir), plus whatever is needed for generic mmappable filesystems. |
| * Specifically, it has a page_map and a few synchronization fields. It doesn't |
| * need to support mmap either. Longer range, fs_file can replace dirtab for |
| * static device filesystems. |
| * |
| * Most fs_files will end up being tree_files (see below). Some devices might |
| * use fs_files that are hooked in to their own tree structures. |
| * |
| * fs_info is for devices to use. For instance, #mnt will probably have an |
| * actual chan for every walked fs_file. It will use that chan/FID for all of |
| * its operations on the backend: walking to discover child files, stat()ing to |
| * get fs_file metadata, reading and writing pages for the page cache, etc. |
| * |
| * |
| * Synchronization rules |
| * ------------------ |
| * Note this qlock is also used by tree_file code. See below for details. |
| * |
| * Hold the qlock when changing the metadata of a file. This includes changing |
| * parts of dir (length, name, permissions/mode, atimes), etc. Tree files will |
| * hold this qlock when changing a parent's children. See the notes around |
| * fs_file_punch_hole for details about read, write, trunc, and the length. |
| * |
| * Readers who just 'peak' at atomically-writable fields do not need to grab the |
| * qlock. For example, you can glance at dir->perms. In arbitrary cases, you |
| * can't look at name, since we can't change it atomically (it can smear or run |
| * into other issues). 'name' is somewhat special: if you find the tree_file |
| * via a RCU protected wc hash lookup, you can access name. |
| * |
| * There's actually not a lot in dir that is atomically writable. |
| * atime/mtime/ctime won't be when we use timespecs. We might be able to craft |
| * something for lockless reads of dir state with seq counters. That would save |
| * a qlock for each fs_file on a readdir. It's a little tricky; we probably |
| * need to kfree_rcu the dir->name (never leak in a convD2M, even if we retry), |
| * and we need to be careful about multi-field changes where the changer might |
| * block. i.e. don't hold a seq lock when making a backend op, which could |
| * block. Not too concerned with this for now, esp since it's not clear how |
| * chown will work. |
| * |
| * Note that even if we can atomically write to the dir, we should still grab |
| * the qlock. Often we're syncing with other writers anyways, and there may be |
| * readers who want to make sure parts of the dir doesn't change. When we do |
| * write a field that can be locklessly read, use WRITE_ONCE. |
| * |
| * The PM code is pretty rough. For now, we're using the old VFS PM code, but |
| * it needs a few changes: |
| * - Heavily integrated with struct page. Maybe some sort of 'struct page' on |
| * demand, that is built for an IO mapping. There might be some huge-page |
| * stuff here too. |
| * - The pm_ops callbacks and whatnot could use some help, even just the |
| * arguments and indirections. Maybe integrate them more into the funcs, e.g. |
| * fs_file_*(). They probably should be fs_file_ops. For now, the pm ops |
| * assume the qlock is held. |
| */ |
| |
| struct fs_file; |
| |
| /* TODO: Once we get rid of the VFS and rework the PM, we can put the PM ops in |
| * here properly. */ |
| struct fs_file_ops { |
| struct page_map_operations; /* readpage and writepage */ |
| void (*punch_hole)(struct fs_file *f, off64_t begin, off64_t end); |
| bool (*can_grow_to)(struct fs_file *f, size_t len); |
| }; |
| |
| #define FSF_DIRTY (1 << 1) |
| |
| struct fs_file { |
| struct dir dir; |
| int flags; |
| qlock_t qlock; |
| struct fs_file_ops *ops; |
| struct page_map *pm; |
| void *priv; |
| |
| /* optional inline storage */ |
| char static_name[KNAMELEN]; /* dir->name */ |
| struct page_map static_pm; /* for pm */ |
| /* we need to be aligned to 64 bytes for the linker tables. */ |
| } __attribute__ ((aligned(64))); |
| |
| static inline bool caller_has_file_perms(struct fs_file *f, int omode) |
| { |
| return caller_has_dir_perms(&f->dir, omode); |
| } |
| |
| static inline void fs_file_perm_check(struct fs_file *f, int omode) |
| { |
| dir_perm_check(&f->dir, omode); |
| } |
| |
| static inline size_t fs_file_get_length(struct fs_file *f) |
| { |
| return ACCESS_ONCE(f->dir.length); |
| } |
| |
| void fs_file_init(struct fs_file *f, const char *name, struct fs_file_ops *ops); |
| void fs_file_set_basename(struct fs_file *f, const char *name); |
| void fs_file_change_basename(struct fs_file *f, const char *name); |
| void fs_file_init_dir(struct fs_file *f, int dir_type, int dir_dev, |
| struct username *user, int perm); |
| void fs_file_copy_from_dir(struct fs_file *f, struct dir *dir); |
| void cleanup_fs_file(struct fs_file *f); |
| |
| #define FSF_ATIME (1 << 0) |
| #define FSF_BTIME (1 << 1) |
| #define FSF_CTIME (1 << 2) |
| #define FSF_MTIME (1 << 3) |
| |
| void __set_acmtime_to(struct fs_file *f, int which, struct timespec *t); |
| void __set_acmtime(struct fs_file *f, int which); |
| void set_acmtime_to(struct fs_file *f, int which, struct timespec *t); |
| void set_acmtime_noperm(struct fs_file *f, int which); |
| |
| size_t fs_file_stat(struct fs_file *f, uint8_t *m_buf, size_t m_buf_sz); |
| void fs_file_truncate(struct fs_file *f, off64_t to); |
| size_t fs_file_read(struct fs_file *f, uint8_t *buf, size_t count, |
| off64_t offset); |
| size_t fs_file_write(struct fs_file *f, const uint8_t *buf, size_t count, |
| off64_t offset); |
| size_t fs_file_wstat(struct fs_file *f, uint8_t *m_buf, size_t m_buf_sz); |