|~|_|~|_|~| |~|_|~|_|~|
\ / \ /
| | | |
| _ |~|_|~|_|~|_|~|_|~| _ |
| | | | | | | |
| ~ | /~~~~~~~\ | ~ |
| | |~~~~|~~~~| | |
| | |====|====| | |
| | |~~~~|~~~~| | |
/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\
< We design castles, YOU build them! >
\_______________________________________/
Sifs is network filesystem, which once tried to be simple. It requires stream connection with flow control, such as TCP. For now it will not survive server reboot.
Client and server talk in packets. All numbers are passed in network byte order. No lengths include padding, lengths include any headers.
Each packet starts with header (defined below), packets are padded so that total length is 4*n. Header layout is:
u32 reqsize - size of packet including header u32 reqhandle - handle of request, this is used to match request with reply u32 command - MSB distingushes between request and reply
Some commands will contain structure called extension block, which
is variable-sized. (Its type will be used as xblock in
following text). Extension blocks contains entries key=val; in key's
low 16 bit, there's length of whole xblock.
u32 (.......key....)(....length....) => xblock is length bytes long length bytes of data padding so that this part is n*4 bytes long
Some commands will need to pass around strings. Strings will be passed in following format:
u16 length (including itself) bytes string (not NULL-terminated!) padding so that total length of string is n*4 bytes
After normal command, packet with corresponding reqhandle, command
set to SFS_OK or SFS_ERROR is returned.
To save confusion with national alphabets etc, all names passed through this thing are in UTF-8. Character #0 is path separator. Some other characters may be unsupported on some servers. At least 0-9a-z_- should be supported. There may also be a limit on name length; but it should be big enough (at least 64 characters).
Information about strange directories (such as '.', '..' on UNIX) is never passed around here: they mey not be stated, they do not appear on readdir listing etc.
Every time path is passed, it is relative to some handle. Assume that handle is corresponding to directory '/foo'. Then, combination of handle and empty string is reference to '/foo'. Combination of handle and 'bar' is reference to '/foo/bar'. No leading nor trainling #0's, please.
There are three kinds of handles here:
header (command = SFS_OPEN) u32 flags u32 dirhandle u32 version string path (relative to dirhandle) xblock
Flags are bitfield, defined values are:
SFS_O_READ - open file for reading
SFS_O_WRITE - open file for writing (this
two can be used together)
SFS_O_DIRECTORY - we want to operate on directory
(directories may not be opened for writing)
SFS_O_EXISTS - specified path must already exist
SFS_O_NOTEXISTS - specified path must not
exist (note that these two can not be used together)
Some more values may be used if SFS_O_WRITE is used:
SFS_O_TRUNC - truncate file if it already exists
SFS_O_APPEND - append to the end of file if already
exists
SFS_O_SYNC - we want writes to be synchronous (not
sure if this should be here)
If command is successfull, it returns packet:
header (command = SFS_OPEN_OK) u32 handle u32 length (length == -1 means length has no sense) u32 version u32 flags
File versions are passed around here so that caching on client side
is possible. Flags can contain one of SFS_C_FLUSHCACHE
(which means that you must forget any cached data you have about that
file) and SFS_C_UNCACHEABLE (you may not attempt to cache
this file).
header (command = SFS_CLOSE) u32 handle (any) xblock
This command takes single parameter, handle you do not need anymore.
header (command = SFS_READ) u32 handle, position, length
Note that this protocol has nothing like seek. It is not needed, as position is passed all the time. If file is unseekable (character device), 0 is passed as position.
Data a returned in packet with command code set to
SFS_READDATA. Less than length bytes may be returned
(even 0), indicating end of file. 0 bytes will be also returned if you
attempt to read past end of file.
header (command = SFS_FORCE_READ) u32 handle, position, length string path
Force read just reads - not caring if someone else has file open for writing etc. You can get almost anything in this case. Path is relative to given handle, reply is same as in case of read.
header (command = SFS_READ) u32 handle, position bytes data
Note that length does not need to be passed as it is clear from reqsize.
header (command = SFS_UNLINK) u32 dirhandle string path xblock
This operation has single argument, path of file to unlink relative to dirhandle. Unlink will also happily unlink empty directory. Unlinking of files/directories that are in use is undefined.
header (command = SFS_MAKETEMPLATE) string fields xblock
This function creates template for later use in readdir/wstat. You should close template when you stop using it.
Each field is a byte, specifying what you want readdir to return (or wstat to set). Bytes must be sorted, and you'll get fields in that order. For every field you specify here, you get data as specified in following table.
| # | Symbolic name | Description | Return |
| 10 | SFS_D_NAME | Name of file | string name |
| 1 | SFS_D_SIZE | Size of file | u32 filesize |
| 8 | SFS_D_TYPE | Type of file | u16 filetype |
| 2 | SFS_D_CTIME | Creation time | u32 seconds from 1.1.1970 |
| 3 | SFS_D_ATIME | Last access time | u32 seconds from 1.1.1970 |
| 4 | SFS_D_MTIME | Last modification time | u32 seconds from 1.1.1970 |
| 5 | SFS_D_ID | File identification (if available) | u32 major, minor, inode |
| 7 | SFS_D_LINKCNT | Number of references to this file | u16 links |
| 6 | SFS_D_UNIXPERMS | Ownership of file and its permissions, unix-style | u32 owner, group, permissions |
| 9 | SFS_D_SL_NAME | Name of file this link points to (if softllink) or empty | string name |
File types are SFS_T_FILE, SFS_T_DIR, SFS_T_SYMLINK,
SFS_T_CHARDEV, SFS_T_BLKDEV, SFS_T_FIFO, SFS_T_SOCKET. We
could count seconds from something else than 1.1.1977, what about
18.5.1977 ;-)
MakeTemplate responds with following packet:
header (command = SFS_NEWTEMPLATE) u32 template string fields xblock
Save template, it is your ticket for new syscalls. In fields, you can read what server is going to tell you. Not everything you asked may be there: for example server may be unable to provide that information.
header (command = SFS_READDIR) u32 dirhandle u32 readdirhandle (or 0 if first read on this directory) u32 template u16 numentries string path xblock
Read entries from directory, template says which entries we want. Num is max in number of directory entries you want to see. Because filehandle can be used in place of dirhandle (here), readdir with filehandle and numentries = 1 is somehow similar to what unix calls stat.
For each readdir (when no error is there), you get following packet:
header (command = SFS_DIRENTRIES) u32 readdir handle (or 0 if end of list) u16 numentries bytes record defined by template used
header (command = SFS_STAT) u32 dirhandle or filehandle u32 template string path xblock
Give info about file/directory, template says which entries we want. (For files, it is somehow similar to readdir with count = 1). It returns following packet (unless error is detected):
header (command = SFS_STATDATA) bytes record defined by template used
header (command = SFS_WSTAT) u32 dirhandle or filehandle u32 template string path bytes statdata
Format of statdata is defined by template used.
header (command = SFS_RENAME) u32 h1: dirhandle or filehandle source u32 h2: dirhandle or filehandle destination string source path (relative to h1) string destination path (relative to h2) xblock
header (command = SFS_COPYFILE) u32 h1: dirhandle or filehandle source u32 h2: dirhandle or filehandle destination string source path (relative to h1) string destination path (relative to h2) xblock
header (command = SFS_NOP) xblock
This command will probably be used for testing if the other side is alive: TCP will detect if other side is dead iff it has any unsent data. So sending NOP every ten minutes is enough for detection of dead clients etc. Nothing is sent back after this command.
header (command = SFS_HOLD) u32 handle string path (relative to handle) xblock
Command hold means you are interested in given file/directory. You get its handle, and you'll be informed using lost command when it changes. If it is changed, you get notification, but your handle is valid, anyway. It can even be passed to hold, again, if you want to know about next change. You should close this handle when you stop using it.
If command is successfull, it returns packet:
header (command = SFS_HOLDING) u32 handle xblock
If object is changed, you get packet:
header (command = SFS_LOST) u32 handle xblock
Please consider following sequence: hold( /tmp ) (SFS_OK returned) open( /tmp/delme ) (and because /tmp/delme did not exists, it is effectively create) (SFS_LOST returned, which must come before SFS_OK - directory was modified before operation was completed, after all...).
header (command = SFS_OK) xblock
Operation completed successfully. This is normally returned after each command (if not stated otherwise in command's description.
header (command = SFS_ERROR) u32 code xblock
If there is a error performing certain operation, this packet is returned. Code is one of:
Would be nice to describe read-only variant of this.
How is mkdir done? SFS_O_DIRECTORY with SFS_O_CREATE?
Pavel Machek
pavel@atrey.karlin.mff.cuni.cz