Dev Docs Shared Volumes

Shared Volumes

Audited against Netatalk d63155e13 on 2026-10-10. This describes the current source checkout; older releases may differ.

Configuration and runtime ownership

Volume definitions are loaded by load_afp_conf_vols() and constructed by creatvol() in libatalk/util/netatalk_conf.c. AFP mount, parameter, flush, and close handlers live in etc/afpd/volume.c. include/atalk/volume.h defines struct vol, volume flags, and lv_flags_t.

A volume definition is not an open volume. Configuration creates its name, path, policy, storage settings, and UUID; afp_openvol() initializes the session’s runtime handles and marks the volume open. Each worker has its own struct vol and CNID connection, even when it shares the same persistent database with other workers.

flowchart LR
    conf[afp.conf section] --> load[load_afp_conf_vols and creatvol]
    load --> advertised[Authorized volume list]
    advertised --> mount[afp_openvol]
    mount --> open[Open volume and CNID handle]
    open --> close[afp_closevol / close_all_vol]

See Configuration for option lookup and CNID Database for persistent identity.

Static volumes, presets, and Homes

[Global]
vol preset = defaults

[defaults]
ea = sys
unix priv = yes

[Shared Documents]
path = /srv/documents
valid users = @staff

[Homes]
basedir regex = /home
home name = $u's home

[Global] and [Homes] are special sections. Presets supply defaults; ordinary volume sections supply a path and optionally a display name. Spaces in display names are supported. Paths using substitutions are expanded by volxlate().

Homes volume creation matches the logged-in user’s resolved home path against basedir regex; the optional Homes path selects a subdirectory inside the home. It is not a generic filesystem glob engine. Static sections with $u are user-dependent definitions, not directory-discovery wildcards.

creatvol() skips duplicate or nested volume paths and logs the reason, including checks against potential Homes volumes. These checks matter when a configured section fails to appear. Single-user mode excludes Homes volumes.

Access control

Option Effect
valid users, invalid users Allow/deny named users and @group entries
hosts allow, hosts deny Client address/network restrictions
read only Make the volume read-only
rolist, rwlist User/group-specific write policy
unix priv, acls Advertise/use supported Unix privileges and ACL behavior
umask, file perm, directory perm Creation mode policy; octal values
preexec, postexec, preexec close Volume lifecycle hooks

These policies do not grant Unix permissions that the serving identity lacks. force user and force group are global identity settings, not arbitrary per-volume overrides. Match behavior is implemented by accessvol() and hostaccessvol(); do not assume shell-style * or @* access-list patterns.

For mode/ACL conversion, follow etc/afpd/unix.c, etc/afpd/acls.c, and libatalk/acl/unix.c. chmod request selects preserve, ignore, or simple behavior. Storage capabilities and protocol capability bits are distinct from the volume’s open/closed state.

AppleDouble and extended attributes

The ea option selects generic EA handling and associated metadata format. Selection is implemented in creatvol() and initvol_vfs().

Selection Behavior
Unset Auto-detect native EA support and select a fallback
sys Native EA operations with Netatalk metadata format
samba Native EA operations and Samba-compatible metadata conventions
ad AppleDouble-backed EAs and legacy v2 metadata
none Disable generic EA support

AppleDouble metadata/resource-fork storage is separate from generic AFP EAs. Legacy v2 uses .AppleDouble files. EA mode uses metadata xattrs and platform-dependent resource-fork storage. See libatalk/adouble/ad_open.c and Data Flow.

The VFS table is statically selected from built-in implementations; there is no general dynamic VFS plugin loader here. VFS_MODULES_MAX is 3. Current files include libatalk/vfs/vfs.c, libatalk/vfs/ea_sys.c, libatalk/vfs/ea_ad.c, and libatalk/vfs/acl.c; the interface is in include/atalk/vfs.h.

Changing ea on a populated volume does not migrate existing metadata. In particular, ea = samba selects a format and does not itself apply multi-protocol coherency policy.

Multi-protocol access

Use multi protocol = yes for a volume modified through other services or local tools:

[Shared]
path = /srv/share
multi protocol = yes
ea = samba

The parser’s apply_multiprotocol_defaults() applies these behaviors:

Setting Default change
strict locking Enable process-wide POSIX data-I/O locking unless explicitly configured
dircache validation freq Use 1 process-wide unless a usable explicit setting is present
Resource-fork cache Exclude this volume unless the global budget was explicitly set
Solaris share reservations Report explicitly weakened reservation policy where supported

The first two settings are process-wide, so loading one such volume affects the worker’s other volumes too. Resource-fork admission is checked per volume in etc/afpd/fork.c. Explicit overrides are honored and can produce coherency warnings.

This mode does not create cross-host cache hints or guarantee compatibility with another service’s locking implementation. Internal hints relay only among the local AFP parent’s children. See Caching Architecture.

CNID path and UUID

SQLite is the default; MySQL is optional. cnid scheme can be inherited from a preset or Global section. Removed cnid server, cnid listen, and vol dbnest settings are logged as obsolete.

A volume-level vol dbpath selects that volume’s directory. A Global vol dbpath without substitutions has the volume name appended. The default is the configured state directory under CNID/<volume name>/.

volume uuid is used for persistent identity and backend table naming, not only Time Machine advertisements. If unset or empty, the parser obtains a UUID through the volume UUID registry. A missing/unwritable registry can therefore prevent SQLite opening. The implementation is in libatalk/util/netatalk_conf.c and libatalk/cnid/sqlite/cnid_sqlite.c.

Time Machine, Spotlight, and icons

[Backups]
path = /srv/backups
time machine = yes
vol size limit = 1048576

[Searchable]
path = /srv/searchable
spotlight = yes
spotlight backend = cnid
legacy icon = globe

vol size limit is in MiB and controls reported volume capacity; it is not a filesystem quota or per-user backup limit. Time Machine capability flags and discovery records are prepared by etc/afpd/volume.c and the controller’s Zeroconf implementation. Client behavior depends on the client’s AFP support.

spotlight backend chooses a compiled cnid, localsearch, or xapian backend; cnid is the default. Do not use undocumented per-volume indexing intervals or filter expressions as though the parser accepts them. Search implementation is in etc/spotlight.

legacy icon synthesizes the volume-root Icon\r file. A real file takes precedence; write operations can materialize it. See Virtual Icon.

Reload and close

LV_ALL loads definitions without ordinary user authorization filtering; LV_FORCE forces loading past the modification-time check. On reload, matching existing volumes are retained with their options. Removed volumes are marked/deleted according to their open state and close vol policy. An existing session is not guaranteed to pick up changed storage or access settings for an already-loaded volume.

Volume close releases runtime handles and purges cache state, including deferred cleanup and parent-directory descriptor slots. Follow afp_closevol()/close_all_vol() in etc/afpd/volume.c. Restart or reconnect when changed options require fresh volume construction.

Footnotes

Generated from the Netatalk GitHub Wiki

Last updated 2026-10-21