| /* Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. |
| * Portions Copyright © 1997-1999 Vita Nuova Limited |
| * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited |
| * (www.vitanuova.com) |
| * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others |
| * |
| * Modified for the Akaros operating system: |
| * Copyright (c) 2013-2014 The Regents of the University of California |
| * Copyright (c) 2013-2015 Google Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. */ |
| |
| |
| #pragma once |
| |
| #include <err.h> |
| #include <rendez.h> |
| #include <rwlock.h> |
| #include <linker_func.h> |
| #include <fdtap.h> |
| #include <ros/fs.h> |
| #include <bitmask.h> |
| #include <mm.h> |
| #include <sys/uio.h> |
| #include <time.h> |
| |
| /* |
| * functions (possibly) linked in, complete, from libc. |
| */ |
| enum { |
| UTFmax = 4, /* maximum bytes per rune */ |
| Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ |
| Runeself = 0x80, /* rune and UTF sequences are the same (<) */ |
| Runeerror = 0xFFFD, /* decoding error in UTF */ |
| Runemax = 0x10FFFF, /* 21-bit rune */ |
| Runemask = 0x1FFFFF, /* bits used by runes (see grep) */ |
| NUMSIZE32 = 10, /* max size of formatted 32 bit number (hex or deci) */ |
| NUMSIZE64 = 20, /* max size of formatted 64 bit number (hex or deci) */ |
| }; |
| |
| /* |
| * math |
| */ |
| extern int isNaN(double); |
| extern int isInf(double, int); |
| extern double floor(double); |
| extern double frexp(double, int *); |
| extern double pow10(int); |
| |
| /* |
| * one-of-a-kind |
| */ |
| extern char *cleanname(char *unused_char_p_t); |
| |
| extern int getfields(char *unused_char_p_t, char **unused_char_pp_t, |
| int unused_int, int, char *); |
| extern int tokenize(char *unused_char_p_t, char **unused_char_pp_t, int); |
| extern int dec64(uint8_t * unused_uint8_p_t, int unused_int, |
| char *unused_char_p_t, int); |
| extern void qsort(void *, long, long, int (*)(void *, void *)); |
| |
| extern int toupper(int); |
| extern int myetheraddr(uint8_t * unused_uint8_p_t, char *unused_char_p_t); |
| extern int parseether(uint8_t * unused_uint8_p_t, char *unused_char_p_t); |
| |
| /* |
| * network dialling |
| */ |
| #define NETPATHLEN 40 |
| |
| /* |
| * Syscall data structures |
| */ |
| #define MORDER 0x0003 /* mask for bits defining order of mounting */ |
| #define MREPL 0x0000 /* mount replaces object */ |
| #define MBEFORE 0x0001 /* mount goes before others in union directory */ |
| #define MAFTER 0x0002 /* mount goes after others in union directory */ |
| #define MCREATE 0x0004 /* permit creation in mounted directory */ |
| #define MCACHE 0x0010 /* cache some data */ |
| #define MMASK 0x0017 /* all bits on */ |
| |
| #define NCONT 0 /* continue after note */ |
| #define NDFLT 1 /* terminate after note */ |
| #define NSAVE 2 /* clear note but hold state */ |
| #define NRSTR 3 /* restore saved state */ |
| |
| #define STATMAX 65535U /* max length of machine-independent stat structure */ |
| #define ERRMAX 128 /* max length of error string */ |
| #define KNAMELEN 28 /* max length of name held in kernel */ |
| |
| /* bits in Qid.type */ |
| #define QTDIR 0x80 /* type bit for directories */ |
| #define QTAPPEND 0x40 /* type bit for append only files */ |
| #define QTEXCL 0x20 /* type bit for exclusive use files */ |
| #define QTMOUNT 0x10 /* type bit for mounted channel */ |
| #define QTAUTH 0x08 /* type bit for authentication file */ |
| #define QTSYMLINK 0x02 /* type bit for symlinks */ |
| #define QTFILE 0x00 /* plain file. Yeah, a zero. Fucking 9p. */ |
| |
| /* bits in Dir.mode */ |
| #define DMDIR 0x80000000 /* mode bit for directories */ |
| #define DMAPPEND 0x40000000 /* mode bit for append only files */ |
| #define DMEXCL 0x20000000 /* mode bit for exclusive use files */ |
| #define DMMOUNT 0x10000000 /* mode bit for mounted channel */ |
| #define DMWRITABLE 0x08000000 /* non-standard, for select() */ |
| #define DMREADABLE 0x04000000 /* non-standard, for select() */ |
| #define DMSYMLINK 0x02000000 /* symlink -- from 9p2000.u */ |
| /* The lower parts of dir.mode are the three rwx perms (S_PMASK) */ |
| #define DMMODE_BITS (DMDIR | DMAPPEND | DMEXCL | DMMOUNT | DMWRITABLE \ |
| | DMREADABLE | DMSYMLINK) |
| |
| struct qid { |
| uint64_t path; |
| uint32_t vers; |
| uint8_t type; |
| }; |
| |
| static inline bool qid_is_file(struct qid q) |
| { |
| return (q.type & (QTDIR | QTSYMLINK)) == 0; |
| } |
| |
| struct dir { |
| /* system-modified data */ |
| uint16_t type; /* server type */ |
| uint32_t dev; /* server subtype */ |
| /* file data */ |
| struct qid qid; /* unique id from server */ |
| uint32_t mode; /* permissions */ |
| /* 9p stat has u32 atime (seconds) here */ |
| /* 9p stat has u32 mtime (seconds) here */ |
| uint64_t length; /* file length: see <u.h> */ |
| char *name; /* last element of path */ |
| char *uid; /* owner name */ |
| char *gid; /* group name */ |
| char *muid; /* last modifier name */ |
| char *ext; /* extensions for special files (symlinks) */ |
| uint32_t n_uid; /* numeric owner uid */ |
| uint32_t n_gid; /* numeric group id */ |
| uint32_t n_muid; /* numeric last modifier id */ |
| struct timespec atime; /* last access time */ |
| struct timespec btime; /* file creation time */ |
| struct timespec ctime; /* last attribute change time */ |
| struct timespec mtime; /* last data modification time */ |
| }; |
| |
| struct waitmsg { |
| int pid; /* of loved one */ |
| uint32_t time[3]; /* of loved one and descendants */ |
| char msg[ERRMAX]; /* actually variable-size in user mode */ |
| }; |
| |
| #define VERSION9P "9P2000" |
| |
| #define MAXWELEM 16 |
| |
| typedef |
| struct fcall { |
| uint8_t type; |
| uint32_t fid; |
| uint16_t tag; |
| /* union { */ |
| /* struct { */ |
| uint32_t msize; /* Tversion, Rversion */ |
| char *version; /* Tversion, Rversion */ |
| /* }; */ |
| /* struct { */ |
| uint16_t oldtag; /* Tflush */ |
| /* }; */ |
| /* struct { */ |
| char *ename; /* Rerror */ |
| /* }; */ |
| /* struct { */ |
| struct qid qid; /* Rattach, Ropen, Rcreate */ |
| uint32_t iounit; /* Ropen, Rcreate */ |
| /* }; */ |
| /* struct { */ |
| struct qid aqid; /* Rauth */ |
| /* }; */ |
| /* struct { */ |
| uint32_t afid; /* Tauth, Tattach */ |
| char *uname; /* Tauth, Tattach */ |
| char *aname; /* Tauth, Tattach */ |
| /* }; */ |
| /* struct { */ |
| uint32_t perm; /* Tcreate */ |
| char *name; /* Tcreate */ |
| uint8_t mode; /* Tcreate, Topen */ |
| /* }; */ |
| /* struct { */ |
| uint32_t newfid; /* Twalk */ |
| uint16_t nwname; /* Twalk */ |
| char *wname[MAXWELEM]; /* Twalk */ |
| /* }; */ |
| /* struct { */ |
| uint16_t nwqid; /* Rwalk */ |
| struct qid wqid[MAXWELEM]; /* Rwalk */ |
| /* }; */ |
| /* struct { */ |
| int64_t offset; /* Tread, Twrite */ |
| uint32_t count; /* Tread, Twrite, Rread */ |
| char *data; /* Twrite, Rread */ |
| /* }; */ |
| /* struct { */ |
| uint16_t nstat; /* Twstat, Rstat */ |
| uint8_t *stat; /* Twstat, Rstat */ |
| /* }; */ |
| /* }; */ |
| } fcall; |
| |
| #define GBIT8(p) ((p)[0]) |
| #define GBIT16(p) ((p)[0]|((p)[1]<<8)) |
| #define GBIT32(p) ((uint32_t)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))) |
| #define GBIT64(p) ((uint32_t)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\ |
| ((int64_t)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32)) |
| |
| #define PBIT8(p,v) (p)[0]=(v) |
| #define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8 |
| #define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24 |
| #define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\ |
| (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56 |
| |
| #define BIT8SZ 1 |
| #define BIT16SZ 2 |
| #define BIT32SZ 4 |
| #define BIT64SZ 8 |
| #define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ) |
| |
| /* The 9p STATFIXLENs include the leading 16-bit count. The count, however, |
| * excludes itself; total size is BIT16SZ + count. This is the amount of fixed |
| * length data in a stat buffer. This does not include the strings, but it |
| * includes the string counts (u16s) |
| * |
| * STAT_FIX_LEN_9P is the original 9p stat message: type to length, including |
| * u32 atime and u32 mtime. This is the bare minimum for a stat that we |
| * receive. We check in e.g. convM2D for any extra fields. |
| * |
| * STAT_FIX_LEN_AK is the stat message used by Akaros, which includes Eric VH's |
| * extensions and full timespecs. It is analogous to struct dir, including the |
| * u32s for the legacy atime/mtime. We always send stats of this size, e.g. in |
| * convD2M. |
| * |
| * Note that the extended stat message has fixed data after the strings, but to |
| * get to this data, you have to jump through the string and their counts |
| * (u16s). The counts are part of the fixed length, but not the strings. Also |
| * note that the _AK version has an extra string. */ |
| #define STAT_NR_STRINGS_9P 4 |
| #define STAT_NR_STRINGS_AK 5 |
| #define STAT_FIX_LEN_9P (BIT16SZ + /* size */ \ |
| BIT16SZ + /* type */ \ |
| BIT32SZ + /* dev */ \ |
| QIDSZ + /* qid */ \ |
| BIT32SZ + /* mode */ \ |
| BIT32SZ + /* atime u32 */ \ |
| BIT32SZ + /* mtime u32 */ \ |
| BIT64SZ + /* length */ \ |
| STAT_NR_STRINGS_9P * BIT16SZ + /* string counts */ \ |
| 0) |
| #define __STAT_FIX_LEN_AK_NONSTRING ( \ |
| BIT32SZ + /* n_uid */ \ |
| BIT32SZ + /* n_gid */ \ |
| BIT32SZ + /* n_muid */ \ |
| 2 * BIT64SZ + /* atime */ \ |
| 2 * BIT64SZ + /* btime */ \ |
| 2 * BIT64SZ + /* ctime */ \ |
| 2 * BIT64SZ + /* mtime */ \ |
| 0) |
| #define STAT_FIX_LEN_AK (STAT_FIX_LEN_9P + \ |
| (STAT_NR_STRINGS_AK - STAT_NR_STRINGS_9P) * BIT16SZ + \ |
| __STAT_FIX_LEN_AK_NONSTRING + \ |
| 0) |
| |
| #define NOTAG (uint16_t)~0U /* Dummy tag */ |
| #define NOFID (uint32_t)~0U /* Dummy fid */ |
| #define IOHDRSZ 24 /* ample room for Twrite/Rread header (iounit) */ |
| |
| enum { |
| Tversion = 100, |
| Rversion, |
| Tauth = 102, |
| Rauth, |
| Tattach = 104, |
| Rattach, |
| Terror = 106, /* illegal */ |
| Rerror, |
| Tflush = 108, |
| Rflush, |
| Twalk = 110, |
| Rwalk, |
| Topen = 112, |
| Ropen, |
| Tcreate = 114, |
| Rcreate, |
| Tread = 116, |
| Rread, |
| Twrite = 118, |
| Rwrite, |
| Tclunk = 120, |
| Rclunk, |
| Tremove = 122, |
| Rremove, |
| Tstat = 124, |
| Rstat, |
| Twstat = 126, |
| Rwstat, |
| Tmax, |
| }; |
| |
| void init_empty_dir(struct dir *d); |
| unsigned int convM2S(uint8_t * unused_uint8_p_t, unsigned int unused_int, |
| struct fcall *); |
| unsigned int convS2M(struct fcall *, uint8_t * unused_uint8_p_t, unsigned int); |
| unsigned int sizeS2M(struct fcall *); |
| |
| unsigned int convM2kdirent(uint8_t * buf, unsigned int nbuf, struct kdirent *kd, |
| char *strs); |
| unsigned int convM2kstat(uint8_t * buf, unsigned int nbuf, struct kstat *ks); |
| |
| void statcheck(uint8_t *buf, size_t nbuf); |
| unsigned int convM2D(uint8_t * unused_uint8_p_t, unsigned int unused_int, |
| struct dir *, char *unused_char_p_t); |
| unsigned int convD2M(struct dir *, uint8_t * unused_uint8_p_t, unsigned int); |
| unsigned int sizeD2M(struct dir *); |
| |
| int read9pmsg(int unused_int, void *, unsigned int); |
| |
| struct ref { |
| spinlock_t l; |
| long ref; |
| }; |
| |
| struct rept { |
| spinlock_t l; |
| struct rendez r; |
| void *o; |
| int t; |
| int (*active) (void *); |
| int (*ck) (void *, int); |
| void (*f) (void *); /* called with VM acquire()'d */ |
| }; |
| |
| enum { |
| Nopin = -1 |
| }; |
| |
| struct talarm { |
| spinlock_t lock; |
| struct proc *list; |
| }; |
| |
| struct alarms { |
| qlock_t qlock; |
| struct proc *head; |
| }; |
| |
| /* |
| * Access types in namec & channel flags |
| */ |
| enum { |
| Aaccess, /* as in stat, wstat */ |
| Abind, /* for left-hand-side of bind */ |
| Atodir, /* as in chdir */ |
| Aopen, /* for i/o */ |
| Amount, /* to be mounted or mounted upon */ |
| Acreate, /* is to be created */ |
| Aremove, /* will be removed by caller */ |
| Arename, /* new_path of a rename */ |
| |
| /* internal chan flags, used by the kernel only */ |
| COPEN = 0x0001, /* for i/o */ |
| CMSG = 0x0002, /* the message channel for a mount */ |
| CFREE = 0x0004, /* not in use */ |
| CINTERNAL_FLAGS = (COPEN | CMSG | CFREE), |
| |
| /* chan/file flags, getable via fcntl/getfl and setably via open and |
| * sometimes fcntl/setfl. those that can't be set cause an error() in |
| * fd_setfl. */ |
| CEXTERNAL_FLAGS = ( |
| O_CLOEXEC | /* (prob should be on the FD, 9ns has it here) */ |
| O_REMCLO | /* remove on close (also, maybe should be on FD) */ |
| O_APPEND | /* append on write */ |
| O_NONBLOCK | /* don't block, can't be set via setfl */ |
| O_PATH | /* path open, just the name, no I/O */ |
| 0), |
| }; |
| |
| #define NS_IPCK_SHIFT 2 |
| #define NS_UDPCK_SHIFT 3 |
| #define NS_TCPCK_SHIFT 4 |
| #define NS_PKTCK_SHIFT 5 |
| #define NS_TSO_SHIFT 6 |
| #define NS_SHIFT_MAX 6 |
| |
| enum { |
| BFREE = (1 << 1), |
| Bipck = (1 << NS_IPCK_SHIFT), /* ip checksum (rx) */ |
| Budpck = (1 << NS_UDPCK_SHIFT), /* udp checksum (rx), needed (tx) */ |
| Btcpck = (1 << NS_TCPCK_SHIFT), /* tcp checksum (rx), needed (tx) */ |
| Bpktck = (1 << NS_PKTCK_SHIFT), /* packet checksum (rx, maybe) */ |
| Btso = (1 << NS_TSO_SHIFT), /* TSO desired (tx) */ |
| }; |
| #define BLOCK_META_FLAGS (Bipck | Budpck | Btcpck | Bpktck | Btso) |
| #define BLOCK_TRANS_TX_CSUM (Budpck | Btcpck) |
| #define BLOCK_RX_CSUM (Bipck | Budpck | Btcpck) |
| |
| struct extra_bdata { |
| uintptr_t base; |
| /* using u32s for packing reasons. this means no extras > 4GB */ |
| uint32_t off; |
| uint32_t len; |
| }; |
| |
| struct block { |
| struct block *next; |
| struct block *list; |
| uint8_t *rp; /* first unconsumed byte */ |
| uint8_t *wp; /* first empty byte */ |
| uint8_t *lim; /* 1 past the end of the buffer */ |
| uint8_t *base; /* start of the buffer */ |
| void (*free) (struct block *); |
| uint16_t flag; |
| uint16_t mss; /* TCP MSS for TSO */ |
| uint16_t network_offset; /* offset from start */ |
| uint16_t transport_offset; /* offset from start */ |
| uint16_t tx_csum_offset; /* offset from tx_offset to store csum */ |
| /* might want something to track the next free extra_data slot */ |
| size_t extra_len; |
| unsigned int nr_extra_bufs; |
| struct extra_bdata *extra_data; |
| }; |
| #define BLEN(s) ((s)->wp - (s)->rp + (s)->extra_len) |
| #define BHLEN(s) ((s)->wp - (s)->rp) |
| #define BALLOC(s) ((s)->lim - (s)->base + (s)->extra_len) |
| |
| struct chan { |
| spinlock_t lock; |
| struct kref ref; |
| struct chan *next; /* allocation */ |
| struct chan *link; |
| int64_t offset; /* in file */ |
| int type; /* ID for device type, e.g. #mnt, #kfs, #ip */ |
| uint32_t dev; /* device specific; can be an instance ID */ |
| uint16_t mode; /* read/write */ |
| int flag; |
| struct qid qid; |
| int fid; /* for devmnt */ |
| uint32_t iounit; /* chunk size for i/o; 0==default */ |
| struct mhead *umh; /* mount point that derived Chan */ |
| struct chan *umc; /* channel in union; for union read */ |
| qlock_t umqlock; /* serialize unionreads */ |
| int uri; /* union read index */ |
| int dri; /* devdirread index */ |
| uint32_t mountid; |
| struct mntcache *mcp; /* Mount cache pointer */ |
| struct mnt *mux; /* Mnt for clients using me for messages */ |
| union { |
| void *aux; |
| char tag[4]; /* for iproute */ |
| }; |
| /* mountpoint, as discovered during walk. |
| * Used for rename at present. |
| */ |
| struct chan *mountpoint; |
| struct chan *mchan; /* channel to mounted server */ |
| struct qid mqid; /* qid of root of mount point */ |
| struct cname *name; |
| /* hack for dir reads to try to get them right. */ |
| int ateof; |
| void *buf; |
| int bufused; |
| /* A lot of synthetic files need something generated at open time, which |
| * the user can read from (including offsets) while the underlying file |
| * changes. Hang that buffer here. */ |
| void *synth_buf; |
| }; |
| |
| extern struct chan *kern_slash; |
| |
| struct cname { |
| struct kref ref; |
| int alen; /* allocated length */ |
| int len; /* strlen(s) */ |
| char *s; |
| }; |
| |
| struct fs_file; |
| |
| struct dev { |
| char *name; |
| |
| void (*reset)(void); |
| void (*init)(void); |
| void (*shutdown)(void); |
| struct chan *(*attach)(char *muxattach); |
| struct walkqid *(*walk)(struct chan *, struct chan *, char **name, |
| unsigned int); |
| size_t (*stat)(struct chan *, uint8_t *, size_t); |
| struct chan *(*open)(struct chan *, int); |
| void (*create)(struct chan *, char *, int, uint32_t, char *); |
| void (*close)(struct chan *); |
| size_t (*read)(struct chan *, void *, size_t, off64_t); |
| struct block *(*bread)(struct chan *, size_t, off64_t); |
| size_t (*write)(struct chan *, void *, size_t, off64_t); |
| size_t (*bwrite)(struct chan *, struct block *, off64_t); |
| void (*remove)(struct chan *); |
| void (*rename)(struct chan *, struct chan *, const char *, int); |
| size_t (*wstat)(struct chan *, uint8_t *, size_t); |
| void (*power)(int); /* power mgt: power(1) → on, power (0) → off */ |
| // int (*config)( int unused_int, char *unused_char_p_t, DevConf*); |
| char *(*chaninfo)(struct chan *, char *, size_t); |
| int (*tapfd)(struct chan *, struct fd_tap *, int); |
| unsigned long (*chan_ctl)(struct chan *c, int op, unsigned long a1, |
| unsigned long a2, unsigned long a3, |
| unsigned long a4); |
| struct fs_file *(*mmap)(struct chan *, struct vm_region *, int, int); |
| /* we need to be aligned to 64 bytes for the linker tables. */ |
| } __attribute__ ((aligned(64))); |
| |
| struct dirtab { |
| char name[KNAMELEN]; |
| struct qid qid; |
| int64_t length; |
| int perm; |
| /* we need to be aligned to 64 bytes for the linker tables. */ |
| } __attribute__ ((aligned(64))); |
| |
| struct walkqid { |
| struct chan *clone; |
| int nqid; |
| struct qid qid[1]; |
| }; |
| |
| enum { |
| NSMAX = 1000, |
| NSLOG = 7, |
| NSCACHE = (1 << NSLOG), |
| }; |
| |
| struct mntwalk { /* state for /proc/#/ns */ |
| int cddone; |
| uint32_t id; |
| struct mhead *mh; |
| struct mount *cm; |
| }; |
| |
| struct mount { |
| uint32_t mountid; |
| struct mount *next; |
| struct mhead *head; |
| struct mount *copy; |
| struct mount *order; |
| struct chan *to; /* channel replacing channel */ |
| int mflag; |
| char *spec; |
| }; |
| |
| struct mhead { |
| struct kref ref; |
| struct rwlock lock; |
| struct chan *from; /* channel mounted upon */ |
| struct mount *mount; /* what's mounted upon it */ |
| struct mhead *hash; /* Hash chain */ |
| }; |
| |
| struct mnt { |
| spinlock_t lock; |
| /* references are counted using c->ref; channels on this mount point |
| * incref(c->mchan) == Mnt.c */ |
| struct chan *c; /* Channel to file service */ |
| struct kthread *rip; /* Reader in progress */ |
| struct mntrpc *queue; /* Queue of pending requests on chan */ |
| uint32_t id; /* Multiplexer id for channel check */ |
| struct mnt *list; /* Free list */ |
| int flags; /* cache */ |
| int msize; /* data + IOHDRSZ */ |
| char *version; /* 9P version */ |
| struct queue *q; /* input queue */ |
| }; |
| |
| enum { |
| RENDLOG = 5, |
| RENDHASH = 1 << RENDLOG, /* Hash to lookup rendezvous tags */ |
| MNTLOG = 5, |
| MNTHASH = 1 << MNTLOG, /* Hash to walk mount table */ |
| DELTAFD = 20, /* allocation quantum for process file descriptors */ |
| MAXNFD = 4000, /* max per process file descriptors */ |
| MAXKEY = 8, /* keys for signed modules */ |
| }; |
| #define MOUNTH(p,qid) ((p)->mnthash[(qid).path&((1<<MNTLOG)-1)]) |
| |
| struct mntparam { |
| struct chan *chan; |
| struct chan *authchan; |
| char *spec; |
| }; |
| |
| struct pgrp { |
| struct kref ref; /* also used as a lock when mounting */ |
| uint32_t pgrpid; |
| qlock_t debug; /* single access via devproc.c */ |
| struct rwlock ns; /* Namespace n read/one write lock */ |
| qlock_t nsh; |
| struct mhead *mnthash[MNTHASH]; |
| int progmode; |
| int nodevs; |
| int pin; |
| }; |
| |
| struct evalue { |
| char *var; |
| char *val; |
| int len; |
| struct qid qid; |
| struct evalue *next; |
| }; |
| |
| struct egrp { |
| struct kref ref; |
| qlock_t qlock; |
| struct evalue *entries; |
| uint32_t path; /* qid.path of next Evalue to be allocated */ |
| uint32_t vers; /* of Egrp */ |
| }; |
| |
| struct signerkey { |
| struct kref ref; |
| char *owner; |
| uint16_t footprint; |
| uint32_t expires; |
| void *alg; |
| void *pk; |
| void (*pkfree) (void *); |
| }; |
| |
| struct skeyset { |
| struct kref ref; |
| qlock_t qlock; |
| uint32_t flags; |
| char *devs; |
| int nkey; |
| struct signerkey *keys[MAXKEY]; |
| }; |
| |
| /* |
| * fasttick timer interrupts |
| */ |
| enum { |
| /* Mode */ |
| Trelative, /* timer programmed in ns from now */ |
| Tabsolute, /* timer programmed in ns since epoch */ |
| Tperiodic, /* periodic timer, period in ns */ |
| }; |
| |
| enum { |
| PRINTSIZE = 256, |
| NUMSIZE = 12, /* size of formatted number */ |
| MB = (1024 * 1024), |
| READSTR = 2000, /* temporary buffer size for device reads */ |
| }; |
| |
| extern struct dev devtab[]; |
| extern struct dev __devtabend[]; |
| |
| struct cmdbuf { |
| char *buf; |
| char **f; |
| int nf; |
| }; |
| |
| struct cmdtab { |
| int index; /* used by client to switch on result */ |
| char *cmd; /* command name */ |
| int narg; /* expected #args; 0 ==> variadic */ |
| }; |
| |
| /* queue state bits, all can be set in qopen (Qstarve is always set) */ |
| enum { |
| Qmsg = (1 << 1), /* message stream */ |
| Qclosed = (1 << 2), /* queue has been closed/hungup */ |
| Qcoalesce = (1 << 3), /* coalesce empty packets on read */ |
| Qkick = (1 << 4), /* always call kick() after qwrite */ |
| Qdropoverflow = (1 << 5), /* drop writes that would block */ |
| }; |
| |
| /* Per-process structs */ |
| #define NR_OPEN_FILES_DEFAULT 32 |
| #define NR_FILE_DESC_DEFAULT 32 |
| |
| /* Bitmask for file descriptors, big for when we exceed the initial small. We |
| * could just use the fd_array to check for openness instead of the bitmask, |
| * but eventually we might want to use the bitmasks for other things (like |
| * which files are close_on_exec. */ |
| |
| typedef struct fd_set { |
| uint8_t fds_bits[BYTES_FOR_BITMASK(NR_FILE_DESC_MAX)]; |
| } fd_set; |
| |
| |
| struct small_fd_set { |
| uint8_t fds_bits[BYTES_FOR_BITMASK(NR_FILE_DESC_DEFAULT)]; |
| }; |
| |
| /* Helper macros to manage fd_sets */ |
| #define FD_SET(n, p) ((p)->fds_bits[(n) / 8] |= (1 << ((n) & 7))) |
| #define FD_CLR(n, p) ((p)->fds_bits[(n) / 8] &= ~(1 << ((n) & 7))) |
| #define FD_ISSET(n, p) ((p)->fds_bits[(n) / 8] & (1 << ((n) & 7))) |
| #define FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p))) |
| |
| /* Describes an open file. We need this, since the FD flags are supposed to be |
| * per file descriptor, not per file (like the file status flags). */ |
| struct file_desc { |
| struct chan *fd_chan; |
| unsigned int fd_flags; |
| struct fd_tap *fd_tap; |
| }; |
| |
| /* All open files for a process */ |
| struct fd_table { |
| spinlock_t lock; |
| bool closed; |
| int max_files; /* max files ptd to by fd */ |
| int max_fdset; /* max of the current fd_set */ |
| int hint_min_fd; /* <= min available fd */ |
| struct file_desc *fd; /* initially pts to fd_array */ |
| struct fd_set *open_fds; /* init, pts to open_fds_init */ |
| struct small_fd_set open_fds_init; |
| struct file_desc fd_array[NR_OPEN_FILES_DEFAULT]; |
| }; |
| |
| ssize_t kread_file(struct file_or_chan *file, void *buf, size_t sz); |
| void *kread_whole_file(struct file_or_chan *file); |
| |
| /* Process-related File management functions */ |
| void *lookup_fd(struct fd_table *fdt, int fd, bool incref); |
| int insert_obj_fdt(struct fd_table *fdt, void *obj, int low_fd, int fd_flags, |
| bool must_use_low); |
| bool close_fd(struct fd_table *fdt, int fd); |
| void close_fdt(struct fd_table *open_files, bool cloexec); |
| void clone_fdt(struct fd_table *src, struct fd_table *dst); |
| |
| #define DEVDOTDOT -1 |
| |
| typedef int Devgen(struct chan *, char *unused_char_p_t, struct dirtab *, |
| int unused_int, int, struct dir *); |
| |
| /* inferno portfns.h. Not all these are needed. */ |
| #define FPinit() fpinit() /* remove this if math lib is linked */ |
| void FPrestore(void *); |
| void FPsave(void *); |
| struct cname *addelem(struct cname *, char *unused_char_p_t); |
| void addprog(struct proc *); |
| void addrootfile(char *unused_char_p_t, uint8_t * unused_uint8_p_t, uint32_t); |
| struct block *adjustblock(struct block *, int); |
| struct block *block_alloc(size_t, int); |
| int block_add_extd(struct block *b, unsigned int nr_bufs, int mem_flags); |
| int block_append_extra(struct block *b, uintptr_t base, uint32_t off, |
| uint32_t len, int mem_flags); |
| void block_copy_metadata(struct block *new_b, struct block *old_b); |
| void block_reset_metadata(struct block *b); |
| int anyhigher(void); |
| int anyready(void); |
| void _assert(char *unused_char_p_t); |
| struct block *bl2mem(uint8_t * unused_uint8_p_t, struct block *, int); |
| int blocklen(struct block *); |
| char *channame(struct chan *); |
| void cclose(struct chan *); |
| void chan_incref(struct chan *); |
| void chandevinit(void); |
| void chandevreset(void); |
| void chandevshutdown(void); |
| void chanfree(struct chan *); |
| void chanrec(struct mnt *); |
| void checkalarms(void); |
| void checkb(struct block *, char *unused_char_p_t); |
| struct chan *cclone(struct chan *); |
| void cclose(struct chan *); |
| void closeegrp(struct egrp *); |
| void closemount(struct mount *); |
| void closepgrp(struct pgrp *); |
| void closesigs(struct skeyset *); |
| void debugcmd(struct cmdbuf *cb); |
| struct mhead *newmhead(struct chan *from); |
| int cmount(struct chan *, struct chan *, int unused_int, char *unused_char_p_t); |
| void cnameclose(struct cname *); |
| struct block *concatblock(struct block *); |
| struct block *linearizeblock(struct block *b); |
| void confinit(void); |
| void cons_add_char(char c); |
| struct block *copyblock(struct block *b, int mem_flags); |
| struct chan *cunique(struct chan *); |
| struct chan *createdir(struct chan *, struct mhead *); |
| void cunmount(struct chan *, struct chan *); |
| void cursorenable(void); |
| void cursordisable(void); |
| int cursoron(int); |
| void cursoroff(int); |
| struct chan *devattach(const char *name, char *spec); |
| struct block *devbread(struct chan *, size_t, off64_t); |
| size_t devbwrite(struct chan *, struct block *, off64_t); |
| struct chan *devclone(struct chan *); |
| void devcreate(struct chan *, char *name, int mode, uint32_t perm, char *ext); |
| void devdir(struct chan *, struct qid, char *, int64_t, char *, long, |
| struct dir *); |
| long devdirread(struct chan *, char *, long, struct dirtab *, int, Devgen *); |
| Devgen devgen; |
| void devinit(void); |
| int devno(const char *name, int user); |
| void devpower(int); |
| struct dev *devbyname(char *unused_char_p_t); |
| struct chan *devopen(struct chan *, int unused_int, struct dirtab *, |
| int unused_int2, Devgen *); |
| void devpermcheck(char *unused_char_p_t, uint32_t, int); |
| void devremove(struct chan *); |
| void devreset(void); |
| void devshutdown(void); |
| size_t dev_make_stat(struct chan *c, struct dir *dir, uint8_t *dp, size_t n); |
| size_t devstat(struct chan *, uint8_t *db, size_t n, struct dirtab *, |
| int ntab, Devgen *); |
| struct walkqid *devwalk(struct chan *, struct chan *, char **unused_char_pp_t, |
| int unused_int, struct dirtab *, int unused_intw, |
| Devgen *); |
| size_t devwstat(struct chan *, uint8_t *, size_t); |
| char *devchaninfo(struct chan *chan, char *ret, size_t ret_l); |
| void disinit(void *); |
| void disfault(void *, char *unused_char_p_t); |
| int domount(struct chan **, struct mhead **); |
| void drawactive(int); |
| void drawcmap(void); |
| void dumpstack(void); |
| void egrpcpy(struct egrp *, struct egrp *); |
| int emptystr(char *unused_char_p_t); |
| int eqchan(struct chan *, struct chan *, int); |
| int eqqid(struct qid, struct qid); |
| |
| void errstr(char *unused_char_p_t, int); |
| void excinit(void); |
| void exit(int); |
| void reboot(void); |
| void halt(void); |
| int export(int unused_int, char *unused_char_p_t, int); |
| uint64_t fastticks(uint64_t *); |
| uint64_t fastticks2ns(uint64_t); |
| int findmount(struct chan **, struct mhead **, int unused_int, int, struct qid); |
| void free_block_extra(struct block *); |
| size_t freeb(struct block *b); |
| size_t freeblist(struct block *b); |
| void freeskey(struct signerkey *); |
| void getcolor(uint32_t, uint32_t *, uint32_t *, uint32_t *); |
| uint32_t getmalloctag(void *); |
| uint32_t getrealloctag(void *); |
| void printblock(struct block *b); |
| void ilock(spinlock_t *); |
| int iprint(char *unused_char_p_t, ...); |
| void isdir(struct chan *); |
| int islo(void); |
| void iunlock(spinlock_t *); |
| void ixsummary(void); |
| void kbdclock(void); |
| int kbdcr2nl(struct queue *, int); |
| int kbdputc(struct queue *, int); |
| void kbdrepeat(int); |
| void kproc(char *unused_char_p_t, void (*)(void *), void *, int); |
| void kprocchild(struct proc *, void (*)(void *), void *); |
| void (*kproftick) (uint32_t); |
| void ksetenv(char *unused_char_p_t, char *, int); |
| void kstrdup(char **cp, char *name); |
| |
| struct block *mem2bl(uint8_t * unused_uint8_p_t, int); |
| int memusehigh(void); |
| void microdelay(int); |
| uint64_t mk64fract(uint64_t, uint64_t); |
| void mkqid(struct qid *, int64_t, uint32_t, int); |
| void modinit(void); |
| struct chan *mntauth(struct chan *, char *unused_char_p_t); |
| long mntversion(struct chan *, char *unused_char_p_t, int unused_int, int); |
| void mountfree(struct mount *); |
| void mousetrack(int unused_int, int, int, int); |
| uint64_t ms2fastticks(uint32_t); |
| void mul64fract(uint64_t *, uint64_t, uint64_t); |
| void muxclose(struct mnt *); |
| struct chan *namec(char *unused_char_p_t, int unused_int, int, uint32_t, |
| void *ext); |
| struct chan *namec_from(struct chan *c, char *name, int amode, int omode, |
| uint32_t perm, void *ext); |
| struct chan *newchan(void); |
| struct egrp *newegrp(void); |
| struct mount *newmount(struct mhead *, struct chan *, int unused_int, |
| char *unused_char_p_t); |
| struct pgrp *newpgrp(void); |
| struct proc *newproc(void); |
| char *nextelem(char *unused_char_p_t, char *); |
| |
| struct cname *newcname(char *unused_char_p_t); |
| void notkilled(void); |
| uint32_t random_read(void *xp, uint32_t n); |
| uint32_t urandom_read(void *xp, uint32_t n); |
| uint64_t ns2fastticks(uint64_t); |
| int okaddr(uint32_t, uint32_t, int); |
| int omode_to_rwx(int); |
| int omode_to_9p_accmode(int open_flags); |
| int access_bits_to_omode(int access_bits); |
| struct block *packblock(struct block *); |
| struct block *padblock(struct block *, int); |
| |
| void pgrpcpy(struct pgrp *, struct pgrp *); |
| |
| int progfdprint(struct chan *, int unused_int, int, char *unused_char_p_t, |
| int i); |
| int pullblock(struct block **, int); |
| struct block *pullupblock(struct block *, int); |
| struct block *pullupqueue(struct queue *, int); |
| void putmhead(struct mhead *); |
| void putstrn(char *unused_char_p_t, int); |
| void qaddlist(struct queue *, struct block *); |
| struct block *qbread(struct queue *q, size_t len); |
| struct block *qbread_nonblock(struct queue *q, size_t len); |
| ssize_t qbwrite(struct queue *, struct block *); |
| ssize_t qbwrite_nonblock(struct queue *, struct block *); |
| ssize_t qibwrite(struct queue *q, struct block *b); |
| struct queue *qbypass(void (*)(void *, struct block *), void *); |
| int qcanread(struct queue *); |
| void qclose(struct queue *); |
| struct block *qcopy(struct queue *, int unused_int, uint32_t); |
| struct block *qclone(struct queue *q, int header_len, int len, |
| uint32_t offset); |
| struct block *blist_clone(struct block *blist, int header_len, int len, |
| uint32_t offset); |
| size_t qdiscard(struct queue *q, size_t len); |
| void qflush(struct queue *); |
| void qfree(struct queue *); |
| int qfull(struct queue *); |
| struct block *qget(struct queue *); |
| void qhangup(struct queue *, char *unused_char_p_t); |
| int qisclosed(struct queue *); |
| ssize_t qiwrite(struct queue *, void *, int); |
| int qlen(struct queue *); |
| size_t q_bytes_read(struct queue *q); |
| void qdropoverflow(struct queue *, bool); |
| void q_toggle_qmsg(struct queue *q, bool onoff); |
| void q_toggle_qcoalesce(struct queue *q, bool onoff); |
| struct queue *qopen(int unused_int, int, void (*)(void *), void *); |
| ssize_t qpass(struct queue *, struct block *); |
| ssize_t qpassnolim(struct queue *, struct block *); |
| void qputback(struct queue *, struct block *); |
| size_t qread(struct queue *q, void *va, size_t len); |
| size_t qread_nonblock(struct queue *q, void *va, size_t len); |
| void qreopen(struct queue *); |
| void qsetlimit(struct queue *, size_t); |
| size_t qgetlimit(struct queue *); |
| int qwindow(struct queue *); |
| ssize_t qwrite(struct queue *, void *, int); |
| ssize_t qwrite_nonblock(struct queue *, void *, int); |
| typedef void (*qio_wake_cb_t)(struct queue *q, void *data, int filter); |
| void qio_set_wake_cb(struct queue *q, qio_wake_cb_t func, void *data); |
| bool qreadable(struct queue *q); |
| bool qwritable(struct queue *q); |
| |
| void *realloc(void *, uint32_t); |
| int readmem(unsigned long offset, char *buf, unsigned long n, const void *mem, |
| size_t mem_len); |
| int readnum(unsigned long off, char *buf, unsigned long n, unsigned long val, |
| size_t size); |
| int readnum_hex(unsigned long off, char *buf, unsigned long n, |
| unsigned long val, size_t size); |
| int readstr(unsigned long offset, char *buf, unsigned long n, const char *str); |
| int readnum_int64_t(uint32_t, char *unused_char_p_t, uint32_t, int64_t, int); |
| unsigned long strtoul_from_ubuf(void *ubuf, size_t count, int base); |
| void ready(struct proc *); |
| void renameproguser(char *unused_char_p_t, char *); |
| void renameuser(char *unused_char_p_t, char *); |
| void resrcwait(char *unused_char_p_t); |
| struct proc *runproc(void); |
| void (*serwrite) (char *unused_char_p_t, int); |
| int setcolor(uint32_t, uint32_t, uint32_t, uint32_t); |
| |
| void setmalloctag(void *, uint32_t); |
| int setpri(int); |
| void setrealloctag(void *, uint32_t); |
| char *skipslash(char *unused_char_p_t); |
| void *smalloc(uint32_t); |
| int splhi(void); |
| int spllo(void); |
| void splx(int); |
| void splxpc(int); |
| void swiproc(struct proc *, int); |
| uint32_t _tas(uint32_t *); |
| uint32_t tk2ms(uint32_t); |
| #define TK2MS(x) ((x)*(1000/HZ)) |
| uint64_t tod2fastticks(int64_t); |
| int64_t todget(int64_t *); |
| void todfix(void); |
| void todsetfreq(int64_t); |
| void todinit(void); |
| void todset(int64_t, int64_t, int); |
| int tready(void *); |
| struct block *trimblock(struct block *, int unused_int, int); |
| int uartgetc(void); |
| void uartputc(int); |
| void uartputs(char *unused_char_p_t, int); |
| void unlock(spinlock_t *); |
| void userinit(void); |
| uint32_t userpc(void); |
| void validname(char *, int); |
| void validwstatname(char *); |
| void *xalloc(uint32_t); |
| void *xallocz(uint32_t, int); |
| void xfree(void *); |
| void xhole(uint32_t, uint32_t); |
| void xinit(void); |
| int xmerge(void *, void *); |
| void *xspanalloc(uint32_t, int unused_int, uint32_t); |
| void xsummary(void); |
| |
| void validaddr(void *, uint32_t, int); |
| void *vmemchr(void *, int unused_int, int); |
| void hnputv(void *, int64_t); |
| void hnputl(void *, uint32_t); |
| void hnputs(void *, uint16_t); |
| int64_t nhgetv(void *); |
| uint32_t nhgetl(void *); |
| uint16_t nhgets(void *); |
| |
| char *get_cur_genbuf(void); |
| |
| static inline const char *chan_dev_name(struct chan *c) |
| { |
| return devtab[c->type].name; |
| } |
| |
| /* hack for now. */ |
| #define NOW tsc2msec(read_tsc()) |
| #define seconds() tsc2sec(read_tsc()) |
| #define milliseconds() tsc2msec(read_tsc()) |
| |
| /* kern/drivers/dev/tab.c */ |
| void devtabinit(); |
| void devtabreset(); |
| |
| /* kern/src/ns/parse.c */ |
| struct cmdbuf *parsecmd(char *p, int n); |
| void cmderror(struct cmdbuf *cb, char *s); |
| struct cmdtab *lookupcmd(struct cmdbuf *cb, struct cmdtab *ctab, int nctab); |
| |
| /* kern/src/ns/sysfile.c */ |
| int newfd(struct chan *c, int low_fd, int oflags, bool must_use_low); |
| struct chan *fdtochan(struct fd_table *fdt, int fd, int mode, int chkmnt, |
| int iref); |
| long kchanio(void *vc, void *buf, int n, int mode); |
| int openmode(uint32_t o); |
| void fdclose(struct fd_table *fdt, int fd); |
| int syschdir(struct proc *target, char *path); |
| int sysfchdir(struct proc *target, int fd); |
| int grpclose(struct fd_table *fdt, int fd); |
| int sysclose(int fd); |
| int syscreate(char *path, int mode, uint32_t perm); |
| int sysdup(int old, int low_fd, bool must_use_low); |
| int sys_dup_to(struct proc *from_proc, unsigned int from_fd, |
| struct proc *to_proc, unsigned int to_fd); |
| int sysfstat(int fd, uint8_t*, int n); |
| int sysfstatakaros(int fd, struct kstat *); |
| char *sysfd2path(int fd); |
| char *sysgetcwd(void); |
| int sysfauth(int fd, char *aname); |
| int sysfversion(int fd, unsigned int msize, char *vers, unsigned int arglen); |
| int sysfwstat(int fd, uint8_t * buf, int n); |
| long bindmount(struct chan *c, char *old, int flag, char *spec); |
| int sysbind(char *new, char *old, int flags); |
| int syssymlink(char *new_path, char *old_path); |
| int sysmount(int fd, int afd, char *old, int flags, char *spec); |
| int sysunmount(char *old, char *new); |
| int sysopenat(int dirfd, char *path, int vfs_flags); |
| int sysopen(char *path, int vfs_flags); |
| long unionread(struct chan *c, void *va, long n); |
| void read_exactly_n(struct chan *c, void *vp, long n); |
| long sysread(int fd, void *va, long n); |
| long syspread(int fd, void *va, long n, int64_t off); |
| int sysremove(char *path); |
| int sysrename(char *from_path, char *to_path); |
| int64_t sysseek(int fd, int64_t off, int whence); |
| void validstat(uint8_t * s, int n, int slashok); |
| int sysstat(char *path, uint8_t*, int n); |
| int syslstat(char *path, uint8_t*, int n); |
| int sysstatakaros(char *path, struct kstat *, int flags); |
| long syswrite(int fd, void *va, long n); |
| long syspwrite(int fd, void *va, long n, int64_t off); |
| int syswstat(char *path, uint8_t * buf, int n); |
| struct dir *chandirstat(struct chan *c); |
| struct dir *sysdirstat(char *name); |
| struct dir *sysdirlstat(char *name); |
| struct dir *sysdirfstat(int fd); |
| int sysdirwstat(char *name, struct dir *dir); |
| int sysdirfwstat(int fd, struct dir *dir); |
| long sysdirread(int fd, struct kdirent **d); |
| int sysiounit(int fd); |
| void print_chaninfo(struct chan *ch); |
| int plan9setup(struct proc *new_proc, struct proc *parent, int flags); |
| int iseve(void); |
| int fd_getfl(int fd); |
| int fd_chan_ctl(int fd, int cmd, unsigned long arg1, unsigned long arg2, |
| unsigned long arg3, unsigned long arg4); |
| int fd_get_fd_flags(struct fd_table *fdt, int fd); |
| int fd_set_fd_flags(struct fd_table *fdt, int fd, int new_fl); |
| |
| /* kern/drivers/dev/srv.c */ |
| char *srvname(struct chan *c); |
| |
| /* kern/src/eipconv.c. Put them here or face real include hell. */ |
| void printqid(void (*putch) (int, void **), void **putdat, struct qid *q); |
| void printcname(void (*putch) (int, void **), void **putdat, struct cname *c); |
| void printchan(void (*putch) (int, void **), void **putdat, struct chan *c); |
| |
| /* kern/src/ns/util.c */ |
| bool caller_is_username(char *uid); |
| bool caller_has_perms(char *fileuid, uint32_t perm, int omode); |
| bool caller_has_dir_perms(struct dir *dir, int omode); |
| void dir_perm_check(struct dir *dir, int omode); |
| |
| static inline int abs(int a) |
| { |
| if (a < 0) |
| return -a; |
| return a; |
| } |
| |
| extern struct username eve; |
| extern unsigned int qiomaxatomic; |
| |
| /* special sections */ |
| #define __devtab __attribute__((__section__(".devtab"))) |
| |
| #define DEVVARS_ENTRY(name, fmt) \ |
| struct dirtab __attribute__((__section__("devvars"))) __devvars_##name = \ |
| {#name "!" fmt, \ |
| {(uint64_t)&(name), 0, QTFILE}, \ |
| sizeof((name)), \ |
| 0444} |