CHANGES
-----------------------

1.2
- Added automatic service discovery inside the current LAN segment.
  Requested by aegis. The notation is $ fizz mount local:/servicename
- Added ephemeral port mapping service. Now servers can specify a service
  name with the SERVICE/K argument, and clients can mount with the new
  notation hostname:/service . This should even coexist with the previous
  way to explicitly serve and mount by port number, and it doesn't require
  a protocol change either. Fitz instances negotitate a port-mapping master
  among each other, and an exiting master transfers this duty to one of the
  others. The port mapping service runs on port 17710, but if you don't
  specify SERVICE for a server or don't mount with hostname:/service, Fitz
  still works without and behaves exactly like before.

1.1
- Amiga client: Added packet types EXAMINE_NEXT, FH_FROM_LOCK, EXAMINE_FH
- Amiga client: bug fixed in rename - was too strict on renaming files with
  held shared lock
- unix client under macOS: added FUSE option noappledouble

1.0r1
- Fixed a bug in the memory management, which could let FreeMem(0,0) happen.
- Amiga D bit could be misinterpreted with the last protocol change, fixed:
  A file with D bit unset could not be deleted even with "delete force".
- No longer calls AvailMem with MEMF_LARGEST (speed impact should have been
  small, as this wasn't called frequently)

1.0
- v8 clients and servers now also announce the capability "ino", which
  allows a mapping between inodes and fib_DiskKey over the wire. All v8
  clients and servers, Amiga and Unix, implement it. This should allow
  tools like GNU Make for Amiga to track filesystem identities even for
  recursive implicit rules - which is something many filesystems on the
  Amiga struggle.

1.0rc3
- Breaking the Amiga client could send it to a "reconnect loop" from
  which there was no exit - hopefully corrected
- Amiga client and server now honour an ENV variable TZOFFS.
- streamlined build and documentation

1.0rc2
- Amiga client: Added BUFS/N/K argument (default 32768)
- Protocol v8 (min v7): reserved opcodes for symlinks (OP_READLINK/SYMLINK),
  advisory locks (OP_FLOCK), extended attributes (OP_GETXATTR/LISTXATTR/
  REMOVEXATTR), and hard links (OP_LINK). New capabilities: symlink, ino,
  flock, xattr, hardlink. FITZ_STAT_RESP_SIZE_INO=37 (ino+nlink appended
  to STAT and READDIR entries when FITZ_CAP_INO negotiated).
- Unix server: new LINKS/S argument enables symlink capability
  (FITZ_CAP_SYMLINK); without it the server follows symlinks transparently
  but does not surface them to clients, replicating previous behavior.
- Unix server+client: symlinks implemented (OP_READLINK, OP_SYMLINK); gated
  on FITZ_CAP_SYMLINK, Unix-Unix only. Amiga clients and servers unaffected.
- Unix server+client: advisory flock implemented (OP_FLOCK); gated on
  FITZ_CAP_FLOCK with FUSE_CAP_FLOCK_LOCKS. Amiga clients and servers
  unaffected.
- Unix server: MODE_OLDFILE upgrade (O_RDONLY -> O_RDWR) is now gated on
  absence of FITZ_CAP_UNIX, making it Amiga-only as intended. Unix-Unix
  opens now preserve the requested access mode.
- Unix client: chown now returns EPERM instead of silently succeeding.
- Unix client: pipelined async writes and read-ahead (WRITE_WINDOW and
  RA_WINDOW = 8 per open file); errors tracked per file handle and returned
  on the next write or flush for writes, or immediately for reads.
- fitz-test: 7 new Unix-only tests (symlink_basic, symlink_readdir,
  symlink_follow, symlink_dangling, symlink_to_dir, chown_eperm, flock_basic);
  25 tests total on Unix.

1.0rc1
- Major source code cleanup; reduced binary size

1.0pre7
- TZOFFS is auto-negotiated for Amiga clients, also with Amiga servers,
  assuming that if TZOFFS is not specified for an Amiga server, it's
  running in the same local time as the connecting Amiga client.
  The general idea is still that servers run in UTC - except for Amiga
  servers which were not given the TZOFFS argument. And this cannot fix
  the lacking notion of DST in Amiga locale, so you will probably want
  to use TZOFFS anyway when connecting to a Unix server. Unix client now
  warns when it mounts a local-timezone server, and 'query' reports the
  negotiated timezone offset.
- improved memory management, greatly reduced stacksize especially in
  amiga server
- build fix for Unix server realpath() via define _XOPEN_SOURCE 700
- argparse list macros replaced by proper functions, and list headers now
  use two NULL nodes, giving correct aliasing for C99 6.5p7
- amiga_client: fix for ACTION_FINDINPUT: now using FITZ_NF_RDONLY
- unix client: when decoding stat, returns st_uid, st_gid, blocks and
  blksize, giving reasonable sizes for du and more useful info.

1.0pre6
- Project renamed from fizz to fitz due to possible name conflict.
  Please delete C:fizz and fizz-serve, fizz-mount, fizz from /usr/local/bin
- Unix now built with -O1 due to possible issue with clang -O2, see Makefile

1.0pre5
- added full support for macOS with macFUSE for mounting.
- minor build fixes: no longer depends on GNU make, no longer uses pkgconfig
- Server is now _POSIX_C_SOURCE 200809L with a few platform-specific
  wrappers and #ifdefs. 

1.0pre4
- Not all used errnos are POSIX, five errno values differ between Linux and
  BSD/macOS (ENOTEMPTY, ENAMETOOLONG, EAGAIN, EDEADLK, ENOSYS). These are now
  encoded as fixed protocol constants (Linux values) at every send/receive
  boundary, so all platform combinations interoperate correctly.

1.0pre3
- Short: Now registers a device again. Removal of left out icons on the
  Workbench is now working. Fixed signal handling in server, which could 
  exit the server when clients were still connected.
- amiga-client: DLT_DEVICE entry is now always registered alongside the
  DLT_VOLUME node. Without it Workbench did not poll the fitz device and
  left-out icons were never put away.
- amiga-client: on CTRL-C with open locks, the active lock chain is linked
  into dol_LockList, dol_Task is nulled on both the volume and device node,
  and DISKREMOVED is sent via input.device. Workbench responds by scanning
  left-out icons whose volume has no handler and puts them away, releasing
  their locks so the handler can exit cleanly.
- amiga-client: on connection loss, volume_remove() pulls the volume from
  the DosList (triggering the "please insert volume" requester) and
  volume_restore() re-adds it on reconnect, dismissing the requester.
- amiga-server: second CTRL-C while waiting for clients now force-quits,
  consistent with the Unix server.

1.0pre2
- added CIPHER argument to servers. CIPHER used reported in server.
- volume node handling (DISKREMOVED/DISKINSERTED) added.

1.0pre1
- Protocol v7 is now the minimum version. All clients and servers should
  be upgraded. Improved documentation.

0.9r3
- SECRET/K allows to specify a binary blob for the secret up to 1024 bytes
- Added BLAKE2-MAC authentication and SPECK64 stream encryption.
- Amiga server: Skips redundant Seeks, lazy read buffer allocation
- server: writing amiga.flags or amiga.comment xattrs on a file that has
  no owner-write permission (e.g. FIBF_WRITE set) failed silently with
  EACCES. Server now temporarily adds S_IWUSR before the xattr loop and
  restores the original mode afterwards.
- amiga-client: when do_rpc() hit a socket error and conn_dead() managed
  to reconnect, the triggering RPC still returned -EIO, which surfaced as
  a "not a DOS disk" requester. do_rpc() now retries the full RPC once
  after a successful reconnect, making server restarts transparent.
- added CRC32 option and CTRL-C to amiga-comparetree tool.
- Server should now work on MacOSX. Minor fixes in docs and messages

0.9
- Fitz default port changed to 17711 (from 7777)
- Amiga: bsdsocket.library version requirement relaxed to v3 (untested),
  code refactoring and cleanup, server and client free of globals. 
- protocol v6: dynamic payload negotiation. OP_WRITE ACK extended to
  written(4) max_payload(4); clients track max_write and chunk writes
  accordingly. get_max_payload(conn_t *) stub on both servers controls the
  limit for both reads and writes. Clients start at FITZ_V6_INIT_MAX_WRITE
  (16KiB) and adjust from ACKs. v6 is a hard requirement: servers reject
  pre-v6 clients at cap exchange with a descriptive message= key; v6
  clients still degrade gracefully when connecting to older servers.
- get_max_payload() now also caps OP_READ response size server-side (both
  servers). Reads silently return fewer bytes than requested when the limit
  is below the client's maxsize; clients already handle partial reads with
  follow-up requests. get_max_payload() is now the single knob controlling
  memory exposure for both transfer directions.
- server rejection at cap exchange now includes a message= key with a
  human-readable reason. New clients print it verbatim; old clients fall
  back to their existing generic message. Version mismatch and cap-need
  failure produce distinct messages. Accepted connections log proto vN
  alongside the negotiated caps.
- IFACE got a [:port] option, error is raised if PORT and IFACE mismatch.
- Amiga server and client using memory pools.
- Amiga DEVNAME argument renamed to VOLNAME.
- Amiga server: child processes inherit parent's stack size; should be fine,
  our stack is pretty lean, only bsdsocket is the unknown
- server, fitz-mount: Unix CLI argument parsing rewritten with argparse
  (Amiga ReadArgs-style templates). Interface structure is now equivalent
  on both platforms. FUSE pass-through options captured via FUSEARGS/F.
- fitz (POSIX shell dispatcher): "query HOST[:PORT]" subcommand added,
  dispatches to fitz-mount with a dummy mountpoint and QUERY keyword.
  "version" subcommand prints version from fitz-serve. Clean usage
  shown on no-argument invocation; "query" with missing host gives a
  specific error. Version string sourced from binary, not hardcoded.
- fitz-mount, amiga-client: cap negotiation rejection now reports the
  failing constraint.
- fitz query / cmd_query now print the negotiated capability set in addition
  to the server's offer.
- reconnect failure suppresses the generic "giving up" message when the
  failure is a cap rejection (message already printed by try_connect).
- capability model simplified: deny= removed from the protocol and all
  clients/servers (redundant with need= for any real use case, and
  semantically ambiguous as a connection gate with implicit server-side
  redirects). nocase (strncasecmp-based, locale-dependent) removed from the
  Unix server offer. New capability amiga-case: deterministic Latin-1/ASCII
  case folding; latin1_lower() and latin1_ncasecmp() added to protocol.h.
  Unix server now offers case and amiga-case only. Amiga server now offers
  amiga-case only (nocase was redundant there; AmigaOS does the matching).
  Amiga client defaults to use=amiga-case. Clients support use= and need= only.
- Amiga D (Deletable) bit, which is a server extended attribute, should now
  be honoured in the Amiga client.
- assuming no v1/v2 clients are left in the field, pruned some code from
  clients that refers to pre-v3 protocol 
- clients, server: more verbose with regard to their offered capabilities
  and those negotiated when clients connect
- Amiga client: improved shutdown path, now reports held locks, tells the
  user to close applications and put away left out icons on the share
- capability management refactored and streamlined into a unified parser
- amiga-client: LOCKING: the prefix conflict rule (exclusive lock on a
  directory blocked all access to entries inside it) was wrong for AmigaDOS
  semantics. CreateDir() returns an exclusive lock; holding it while copying
  files into the new directory triggered ERROR_OBJECT_IN_USE for every file
  open, breaking "copy all clone". Removed the rule; exact and descendant
  checks remain.
- amiga-comparetree: comment_eq treated fib_Comment as a BCPL string (length
  byte at [0]) but dos.library converts it to a C string before returning from
  ExNext(), same as fib_FileName. Fixed to use strlen/memcmp from byte 0.
- amiga-server: mysnprintf (kprintf.asm) returned strlen+1 instead of strlen
  (the null character was counted). safe_path used the return value with a
  `>= outsz` overflow check, which fired when a non-rootcolon path (e.g.
  "SYS:T/filename") exactly filled the output buffer -- one byte tighter than
  a volume-root path because of the extra "/" separator. Result: OP_STAT
  returned EACCES for every file in a shared subdirectory while readdir still
  worked (the root path "/" never fills the buffer). Fixed by adding
  `subq.l #1,d0` in kprintf.asm so mysnprintf returns strlen, matching C99
  snprintf semantics.
- server: amiga-comment and amiga-flags capability added to the Unix server.
  Comment stored as xattr "amiga.comment" (raw bytes, max 79), flags as
  "amiga.flags" (1 byte, same bit layout as the wire protocol). Platform
  wrappers cover Linux (lgetxattr/lsetxattr/lremovexattr, "user." prefix
  added internally), macOS (getxattr/setxattr/removexattr with XATTR_NOFOLLOW,
  same "user." prefix), FreeBSD (extattr_get_link/extattr_set_link/
  extattr_delete_link with EXTATTR_NAMESPACE_USER, bare name); other
  platforms compile with silent no-ops. h_setxattr handler added; h_stat
  and h_readdir append xattr blocks when the client has negotiated the caps.
  Removing a comment or zeroing flags removes the xattr from the filesystem.
  At startup the server probes the root directory with a get of a non-existent
  attribute; if the filesystem returns ENOTSUP/EOPNOTSUPP the xattr caps are
  silently omitted from the offer so clients never negotiate them.
- server: new -v / --verbose flag; logs each xattr get, set, and remove
  operation to stderr with path, key, return value, and errno.
- amiga-comparetree: new standalone Amiga CLI test tool. Takes DIRA/A DIRB/A
  QUIET/S; walks both trees recursively and compares type, size, DateStamp,
  protection bits, and fib_Comment for every entry. Reports entries present in
  only one tree. Returns RETURN_OK (identical), RETURN_WARN (differences
  found), or RETURN_ERROR (I/O error). Useful for verifying round-trip copies
  via fitz.
- protocol v5: amiga-comment and amiga-flags capabilities. When both sides
  negotiate amiga-comment, STAT and READDIR responses append an xattr block
  after the fixed fields. Format: count(1) [klen(1) key(klen) vlen(1)
  val(vlen)]*count. Key "amiga.comment" carries fib_Comment as raw bytes
  (max 79). Key "amiga.flags" carries SPAD+H protection bits as one byte,
  active-high: bit0=delete-protected, bit1=archived, bit2=pure,
  bit3=script, bit4=hold. OP_SETXATTR (opcode 16) writes xattr entries:
  xattr_block_len(2) xattr_block path(remaining) -> empty.
  Amiga server offers amiga-comment and amiga-flags. Amiga client defaults
  to use=iso-8859-1,nocase,amiga-comment,amiga-flags. ACTION_SET_COMMENT
  is now forwarded to the server via OP_SETXATTR. amiga-comment and
  amiga-flags are silently inactive against servers that do not offer them.
- protocol v4: capability exchange. Wire: caplen(2,BE) text(caplen),
  newline-delimited key=value pairs (offer=, need=, use=, deny=,
  next=start/disconnect). Client sends first; server responds. Server
  rejects if any cap in client need= is absent from its offer=.
  Capability names: unix (Unix/POSIX server; real POSIX permission bits
  including owner/group/other), amiga (AmigaOS server; synthesised
  permissions, inherently case-insensitive), iso-8859-1 (Latin-1
  filenames), case (case-sensitive path matching), nocase (case-insensitive
  path matching). Unix server offers unix,case,nocase and implements
  nocase path resolution when requested (nocase_resolve, resolve_last=0
  for create/mkdir). Amiga server offers amiga,iso-8859-1,nocase.
  Clients: USE/NEED/DENY args (Amiga) and --use/--need/--deny options
  (FUSE); default use=iso-8859-1,nocase,amiga-comment,amiga-flags on
  Amiga client, use=unix,case on FUSE client (applied when none of
  use/need/deny are specified).
  Backward compat: FITZ_PROTO_MIN_VERSION=3; v4/v5 clients connect to v3
  servers without cap exchange, falling back to case-sensitive mode.
- "fitz query HOST[:PORT]" (Amiga) and "fitz-mount --query HOST[:PORT]"
  (FUSE): connect, print server's protocol version and offered
  capabilities, then exit without mounting.
- amiga-client: VOLUME_ONLY #ifdef (commented out by default) skips the
  DLT_DEVICE entry and registers only a DLT_VOLUME, matching ch_nfsmount
  behaviour. Device name deduplication now applies to both explicit and
  default names; sequence is FITZ, FITZ2, FITZ3, ... (not FITZ1).
- updated systemd service file

0.8
- Unix Signal handling: SIGINT (first) gracefully stops accepting, waits
  for active clients; SIGHUP: same as first SIGINT; SIGTERM forcedly
  exits immediately regardless of active clients; SIGINT (second) same
  as SIGTERM. A self-pipe (g_wakefd) lets the forced path wake the
  pthread_cond_timedwait in the shutdown loop immediately rather than
  waiting up to 5 seconds for the next poll cycle. The wakeup_thread
  reads one byte from the pipe and signals the condvar (all signal
  handlers are async-signal-safe (write to a pipe is POSIX safe))
- added systemv and systemd init scripts and configs in etc/
- extended test suite
- some reformatting, Amiga client refactored to use no globals,
  smaller binary despite extended functionality
- Amiga client dirbuf (per NetLock) no longer capped to a constant, but
  to a fraction of the available memory. (currently this is merely for
  directory traversal, buffer goes away with its dirlock).
- protocol v3 now supports OP_STATFS; amiga client: some heuristic to
  scale blocksize and blocks on large drives into somewhat useful range
- SO_KEEPALIVE added to servers
- Amiga client: Added experimental LOCKING option, to enforce full Amiga
  locking semantics locally, per client instance. No locking goes over
  the wire protocol and so isn't enforced between remote clients yet.
- amiga-client: NetLock.path and OpenFile.path are now heap-allocated
  (trailing bytes of the same AllocVec block as the struct) instead of
  fixed char[512] arrays. Removes the 512-byte path cap and eliminates
  the wasted space for short paths. A global PATHBUF_SIZE (4096) scratch
  buffer (g_pathbuf, heap-allocated at mount time) replaces all per-call
  stack path buffers. Request payload buffers sized to the actual path
  length instead of the former fixed MAX_PATH upper bound.
- amiga-server, server: per-connection file handle table is now a
  heap-allocated array starting at 8 slots, doubled on exhaustion.
  Removes the hard MAX_FH=64 cap; the OS file descriptor limit becomes
  the effective ceiling, with EMFILE returned when that is reached.
- amiga-server: per-handler path buffers (path + safe_path output) are
  now heap-allocated to the actual payload length instead of fixed
  char[512] stack arrays. Removes the 512-byte path cap from all server
  ops (stat, readdir, open, create, mkdir, unlink, rename, truncate,
  chmod, setmtime).

0.7
- protocol: version bumped to 2. Clients now accept server_version >=
  FITZ_PROTO_VERSION instead of requiring an exact match. This allows
  future additive versions without breaking already-deployed clients
- protocol: new OP_CHMOD (opcode 13). Wire format: mode(4) path -> empty.
- all: partial permission mapping between Unix mode bits and AmigaDOS
  fib_Protection bits. Owner r/w/x are mapped via FIBF_READ/WRITE/EXECUTE
  (all active-low). FIBF_DELETE is always left clear (deletable); group
  and other Unix bits are not mapped. Details in DESIGN.
- amiga-server: STAT and READDIR now derive mode from fib_Protection
  instead of always sending hardcoded 0755/0644.
- amiga-client: fill_fib() now converts received Unix mode to fib_Protection
  so protection bits are visible in directory listings and Protect command.
- fitz-mount, amiga-client: nfs_chmod()/ACTION_SET_PROTECT now forward to
  the server via OP_CHMOD. Silently accepted (no-op) against v1 servers.
- amiga-client: build_path() fixed for AmigaDOS parent-navigation: a
  leading '/' in a name is resolved as one level up per slash. Fixes
  "cd /", "/foo" paths, and "cd //" (grandparent) in shell and programs.
- amiga-client: build_path() treats a lone '.' component as current
  directory (no-op) so "cd ." works instead of returning an error.
- amiga-server: SetFileSize() returns the new file size on success (not
  DOSTRUE), so the failure check must be < 0, not !result. Using !result
  treated a successful truncate-to-zero (return 0) as an error. Fixed in
  both h_open (NF_TRUNC path) and h_truncate. This caused overwrite and
  trunc_zero to fail when the Amiga server was used as the backend.
- all: new OP_SETMTIME (opcode 14). Wire: mtime_sec(8) path -> empty.
  POSIX server uses utimensat() (atime unchanged). Amiga server uses
  SetFileDate(). FUSE client wires nfs_utimens() (was a no-op). Amiga
  client implements ACTION_SET_DATE via OP_SETMTIME; silently succeeds
  against v1 servers. Timestamps go through the same TZOFFS conversion
  as STAT/READDIR, so copy tools that preserve dates will round-trip
  correctly.
- amiga-client: ACTION_FLUSH now drains the async write pipeline and
  propagates any deferred write error, instead of silently succeeding.
- amiga-client: ACTION_INHIBIT now returns DOSFALSE/ERROR_ACTION_NOT_KNOWN
  instead of DOSTRUE, so callers know the volume cannot be inhibited.
- amiga-server, amiga-client: new TZOFFS/N argument (seconds, signed).
  AmigaDOS datestamps are local time; the protocol carries UTC. Without
  TZOFFS, file timestamps appear off by the TZ+DST difference. Set to
  the same value on both sides (e.g. TZOFFS 7200 for CEST). POSIX
  server and FUSE client are not affected (they work in UTC throughout).
- amiga-client: ACTION_FINDINPUT now requests NF_RDWR from the server
  instead of NF_RDONLY. AmigaOS FFS does not enforce open mode per
  filehandle; software that writes to a FINDINPUT handle (e.g. backup
  tools that re-open an archive to patch the header) worked against the
  Amiga server but failed against the POSIX server with a deferred EBADF
  surfacing at ACTION_END time.

0.6
- FreeBSD build fixes for stricter POSIX_SOURCE, FUSE_CAP_ATOMIC_0_TRUNC 
  not supported (our nfs_truncate implements it anyway)
- unix-client, amiga-client: writes are fire-and-forget; the ACK is
  collected asynchronously while the next write is already in flight.
  Eliminates one full round-trip latency per write on sequential writes.
  flush/fsync drain all in-flight writes and return any deferred error.
- amiga-client: TCP_NODELAY and SO_RCVBUF=256KB set on the socket.
  Default receive buffer (4-8 KB on most stacks) caused repeated TCP
  window stalls when receiving large read responses over fast links.
  Big read throughput gain on A3000/68060 over 100 Mbit/s Ethernet.
- amiga-client: ACTION_EXAMINE_OBJECT on the root lock now returns the
  volume name in fib_FileName. Previously returned empty string, causing
  applications that use NameFromLock to fail to open files on the mount.
- amiga-client: async write pipeline was unbounded; server ACKs (12 bytes
  each) accumulated in the TCP receive buffer without ever being drained
  during pure-write workloads (drain_async_writes is only reached via
  do_rpc, which is not called on a sequential write stream). Once the
  buffer filled the server blocked sending the next ACK, stopped reading
  new data, and the client blocked in send - permanent deadlock at a
  deterministic byte offset. Fixed by capping g_async_inflight at
  ASYNC_WRITE_MAX (4); drain_async_writes is called before sending when
  the limit is reached.
- amiga-client: device name passed to "fitz mount" is now stripped of a
  trailing colon so "DH0:" and "DH0" both work.
- server: h_readdir now silently skips entries whose names contain
  protocol-invalid characters (':', '\', control bytes) or are '.'/'..'.
  Such names appeared in POSIX directory listings as '?????????' because
  getattr rejected them via safe_path(); on the Amiga they appeared
  correctly but were unopenable. Now they are simply not served.
- fitz-mount: --omithidden flag hides dot-files from directory listings
  while keeping them fully accessible by path.
- amiga-client: OMITHIDDEN/S argument to "fitz mount" does the same.
- fitz-mount: automatic reconnect after server disconnect. Retries every
  5 s for up to 60 s; ops block during reconnect and resume transparently.
  Open file handles return EBADF (stale server fh) after reconnect.
  If all retries fail, ops return EIO as before.
- amiga-client: same reconnect behaviour, handled inline in conn_dead()
  using Delay(); the current DOS packet fails with an error, subsequent
  ones resume normally after a successful reconnect.
- fitz-serve, amiga-server: client disconnect messages now always printed
  (previously only during shutdown), including the client IP address.
- fitz-serve, amiga-server: new --interface/INTERFACE option to bind to a
  specific IP address instead of all interfaces. Binding to 127.0.0.1
  restricts access to localhost; binding to an internal NIC address limits
  exposure on multi-homed hosts.
