enum {
	/* affects on-disk structure */
	BLOCK = 4096,
	RBLOCK = BLOCK - 1,
	SUPERMAGIC = 0x6E0DE51C,
	SUPERBLK = 0,
	
	NAMELEN = 256,
	NDIRECT = 15,
	NINDIRECT = 4,
	
	ROOTQID = 1,
	DUMPROOTQID = 2,
	
	/* affects just run-time behaviour */
	SYNCINTERVAL = 10000,
	FREELISTLEN = 256,
	BUFHASHBITS = 8,
	BUFHASH = (1<<BUFHASHBITS)-1,
	NWORKERS = 5,
	EXCLDUR = 300,

	NOUID = (short)0x8000,
	USERLEN = 64,
};

typedef struct Fs Fs;
typedef struct Buf Buf;
typedef struct Dev Dev;
typedef struct BufReq BufReq;
typedef struct ThrData ThrData;
typedef struct Superblock Superblock;
typedef struct Dentry Dentry;
typedef struct Chan Chan;
typedef struct FLoc FLoc;
typedef struct Loc Loc;
typedef struct User User;

#pragma incomplete struct User
#pragma varargck type "T" int
#pragma varargck type "T" uint
enum {
	TRAW,
	TSUPERBLOCK,
	TDENTRY,
	TINDIR,
	TREF,
	TDONTCARE = -1,
};

struct Superblock {
	ulong magic;
	uvlong size;
	uvlong fstart;
	uvlong fend;
	uvlong root;
	uvlong qidpath;
};

enum {
	DALLOC = 1<<15,
	DGONE = 1<<14,
};

struct Dentry {
	char name[NAMELEN];
	short uid;
	short muid;
	short gid;
	ushort mode;
	Qid;
	uvlong size; /* bytes for files and blocks for dirs */
	uvlong db[NDIRECT];
	uvlong ib[NINDIRECT];
	vlong atime;
	vlong mtime;
};

enum {
	DENTRYSIZ = NAMELEN + 4 * sizeof(ushort) + 13 + (3 + NDIRECT + NINDIRECT) * sizeof(uvlong),
	DEPERBLK = RBLOCK / DENTRYSIZ,
	/* Given any opportunity to make a breaking change to hjfs,
	 * make this 12 an 8. Indirect offsets to blocks used to
	 * hold an incrementing  4 byte generation number. That
	 * design has changed.
	 */
	OFFPERBLK = RBLOCK / 12,
	REFSIZ = 3,
	REFPERBLK = RBLOCK / REFSIZ,
	REFSENTINEL = (1 << 8*REFSIZ) - 1,
};

struct BufReq {
	Dev *d;
	uvlong off;
	int nodata;
	Channel *resp;
	BufReq *next;
};

enum {
	BWRITE = 1, /* used only for the worker */
	BWRIM = 2, /* write immediately after putbuf */
	BDELWRI = 4, /* write delayed */
};

struct Buf {
	uchar op, type;
	union {
		uchar data[RBLOCK];
		Superblock sb;
		Dentry de[DEPERBLK];
		uvlong offs[OFFPERBLK];
		ulong refs[REFPERBLK];
	};

	/* do not use anything below (for the bufproc only) */
	uchar busy;
	char *error;
	Buf *dnext, *dprev;
	Buf *fnext, *fprev;
	BufReq;
	BufReq *last;
	ulong callerpc; /* debugging */
	
	Buf *wnext, *wprev;
};

struct ThrData {
	Channel *resp;
};

struct Dev {
	char *name;
	int size;
	Buf buf[BUFHASH+1]; /* doubly-linked list */
	Dev *next;
	int fd;
	Rendez workr;
	QLock workl;
	Buf work;
};

extern Dev *devs;

struct FLoc {
	uvlong blk;
	int deind;
	Qid;
};

enum {
	LGONE = 1,
	LDUMPED = 2,
};

struct Loc {
	FLoc;
	int ref, flags;
	Loc *next, *child;
	Loc *cnext, *cprev;
	Loc *gnext, *gprev;
	
	QLock ex;
	Chan *exlock;
	ulong lwrite;
};

enum {
	FSNOAUTH = 1,
	FSNOPERM = 2,
	FSCHOWN = 4,
};

struct Fs {
	RWLock;
	Dev *d;
	int flags;
	uvlong root, fstart;
	
	Channel *freelist;
	Loc *rootloc, *dumprootloc;
	QLock loctree;

	User *udata;
	int nudata;
	RWLock udatal;
};

enum {
	CHREAD = 1,
	CHWRITE = 2,
	CHRCLOSE = 4,

	CHFDUMP = 1,
	CHFNOLOCK = 2,
	CHFRO = 4,
	CHFNOPERM = 8,
	
	CHWBUSY = 1,
	CHWCLUNK = 2,
};


struct Chan {
	Fs *fs;
	Loc *loc;
	uchar open;
	uchar flags;
	ushort uid;

	/* dir walk */
	uvlong dwloff;
	uvlong dwblk;
	int dwind;
	
	/* workers */
	void *freq, *lreq;
	Chan *qnext, *qprev;
	int wflags;
};

extern QLock chanqu;
extern Rendez chanre;
extern Chan readych;

extern char Eio[];
extern char Enotadir[];
extern char Enoent[];
extern char Einval[];
extern char Eperm[];
extern char Eexists[];
extern char Elocked[];
extern char Ebadspec[];

enum { /* getblk modes */
	GBREAD = 0,
	GBWRITE = 1,
	GBCREATE = 2,
	GBOVERWR = 3,
};

#define HOWMANY(a) (((a)+(RBLOCK-1))/RBLOCK)
