#include <errno.h>#include <limits.h>#include <poll.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <sys/types.h>#include <sys/un.h>#include <time.h>#include <unistd.h>#include <atalk/dsi.h>#include <atalk/errchk.h>#include <atalk/globals.h>#include <atalk/logger.h>#include <atalk/server_child.h>#include <atalk/server_ipc.h>#include <atalk/util.h>Data Structures | |
| struct | ipc_header |
| struct | hint_entry |
Macros | |
| #define | HINT_RATE_LIMIT 1000 /* Max hints/second per child (attack guard) */ |
| #define | HINT_RESET_RATE_LIMIT 8 |
| #define | RESET_SEEN_SIZE 8 |
| #define | HINT_RESET_WAIT_BUDGET 4 |
| #define | HINT_RESET_WAIT_MS 20 |
| #define | RATE_TRACK_SIZE 256 |
Typedefs | |
| typedef struct ipc_header | ipc_header_t |
Functions | |
| static int | rate_slot (pid_t child_pid) |
| static int | check_and_increment_rate (pid_t child_pid) |
| static int | check_and_increment_reset_rate (pid_t child_pid) |
| static int | reset_already_broadcast (uint32_t tag) |
| Whether this volume's reset has just been broadcast. | |
| static void | reset_broadcast_forget (uint32_t tag) |
| Make a volume's reset broadcastable again. | |
| static int | hint_write_batch (int fd, const char *buf, int len, int has_reset, int *waits_left) |
| Write a sibling's hint batch, waiting once if it carries a reset. | |
| static int | serialize_hint (char *buf, const struct hint_entry *e) |
| Serialize a hint_entry to IPC wire format. | |
| static int | ipc_kill_token (struct ipc_header *ipc, server_child_t *children) |
| Pass afp_socket to old disconnected session if one has a matching token. | |
| static int | ipc_get_session (struct ipc_header *ipc, server_child_t *children) |
| static int | ipc_login_done (const struct ipc_header *ipc, server_child_t *children) |
| static int | ipc_set_session_token (const struct ipc_header *ipc, server_child_t *children) |
| static int | ipc_set_state (struct ipc_header *ipc, server_child_t *children) |
| static int | ipc_set_volumes (struct ipc_header *ipc, server_child_t *children) |
| static int | ipc_relay_cache_hint (struct ipc_header *ipc, server_child_t *children) |
| Buffer a dircache hint for batched relay to siblings. | |
| int | ipc_server_read (server_child_t *children, int fd) |
| Read a IPC message from a child. | |
| int | ipc_child_write (AFPObj *obj, uint16_t command, size_t len, void *msg) |
| int | ipc_child_state (AFPObj *obj, uint16_t state) |
| int | hint_buf_count (void) |
| Return current number of buffered hints. | |
| void | hint_flush_pending (server_child_t *children) |
| Flush all buffered hints to sibling children. | |
| unsigned long long | ipc_get_hints_sent (void) |
| unsigned long long | ipc_get_hints_dropped (void) |
| static const char * | cache_hint_name (uint8_t event) |
| int | ipc_send_cache_hint (const AFPObj *obj, uint16_t vid, cnid_t cnid, uint8_t event) |
| Send a dircache invalidation hint from child to parent. | |
Variables | ||
| static char * | ipc_cmd_str [] | |
| struct { | ||
| struct hint_entry entries [HINT_BUF_SIZE] | ||
| int count | ||
| } | hint_buf | |
| struct { | ||
| pid_t pid | ||
| time_t window_start | ||
| int count_in_window | ||
| int resets_in_window | ||
| } | rate_track [RATE_TRACK_SIZE] | |
| static unsigned long long | hints_batched = 0 | |
| static unsigned long long | hints_rate_dropped = 0 | |
| static unsigned long long | flush_count = 0 | |
| struct { | ||
| uint32_t tag | ||
| time_t when | ||
| } | reset_seen [RESET_SEEN_SIZE] | |
| static unsigned long long | hints_sent = 0 | |
| static unsigned long long | hints_dropped = 0 | |
IPC over socketpair between parent and children.
| #define HINT_RATE_LIMIT 1000 /* Max hints/second per child (attack guard) */ |
| #define HINT_RESET_RATE_LIMIT 8 |
| #define HINT_RESET_WAIT_BUDGET 4 |
| #define HINT_RESET_WAIT_MS 20 |
| #define RATE_TRACK_SIZE 256 |
| #define RESET_SEEN_SIZE 8 |
| typedef struct ipc_header ipc_header_t |
|
static |
|
static |
|
static |
| int hint_buf_count | ( | void | ) |
Return current number of buffered hints.
Used by main event loop to decide poll timeout:
| void hint_flush_pending | ( | server_child_t * | children | ) |
Flush all buffered hints to sibling children.
Called from the parent main event loop when:
Iterates the child table directly:
Performs priority sorting, PIPE_BUF-safe chunked writes.
While this function writes to child pipes, new IPC messages from children accumulate in the kernel socket buffer and are read on the next poll() iteration.
|
static |
Write a sibling's hint batch, waiting once if it carries a reset.
A full sibling pipe drops a best-effort batch: those hints only cost a lookup. A batch carrying a reset waits briefly for pipe space instead, since the sibling would otherwise keep resolving recycled CNIDs. Waits are budgeted per second across flushes so backed-up siblings cannot stall the master.
| [in] | fd | sibling's hint pipe |
| [in] | buf | serialized batch |
| [in] | len | bytes to write |
| [in] | has_reset | batch contains a CACHE_HINT_VOLUME_RESET |
| [in,out] | waits_left | this second's remaining waits, spent here |
| int ipc_child_state | ( | AFPObj * | obj, |
| uint16_t | state | ||
| ) |
| int ipc_child_write | ( | AFPObj * | obj, |
| uint16_t | command, | ||
| size_t | len, | ||
| void * | msg | ||
| ) |
| unsigned long long ipc_get_hints_dropped | ( | void | ) |
| unsigned long long ipc_get_hints_sent | ( | void | ) |
|
static |
|
static |
Pass afp_socket to old disconnected session if one has a matching token.
|
static |
|
static |
Buffer a dircache hint for batched relay to siblings.
Appends to hint_buf array. Single-threaded — no lock needed. If buffer is full, the hint is dropped (caller should flush first).
Send a dircache invalidation hint from child to parent.
Called directly from AFP command handlers that modify dircache state. Independent of the external FCE system — always active when IPC is available.
Uses a direct non-blocking write() to the IPC socketpair instead of ipc_child_write()/writet() — this ensures the AFP command handler is never blocked waiting for IPC buffer space. If the kernel socket buffer is full (parent not draining fast enough), the hint is silently dropped. Hints are best-effort optimizations, and the dircache validation mechanism & graceful fail-on-use detection makes drops safe.
CACHE_HINT_VOLUME_RESET is the exception: a sibling that misses it keeps resolving CNIDs that the wipe has recycled onto other files, so it waits briefly for pipe space and reports failure to the caller.
The 22-byte message (14-byte IPC header + 8-byte payload) is well under the kernel socket buffer size, so partial writes cannot occur when space is available.
| [in] | obj | AFPObj with ipc_fd |
| [in] | vid | Volume ID (network byte order, matches vol->v_vid) |
| [in] | cnid | CNID of affected file/dir (network byte order) |
| [in] | event | Hint type: one of the CACHE_HINT_* values |
| int ipc_server_read | ( | server_child_t * | children, |
| int | fd | ||
| ) |
Read a IPC message from a child.
This is using an fd with non-blocking IO, so EAGAIN is not an error
| [in,out] | children | pointer to our structure with all childs |
| [in] | fd | IPC socket with child |
|
static |
|
static |
|
static |
|
static |
|
static |
Whether this volume's reset has just been broadcast.
One notification per volume is all a sibling needs, and each one costs a forced flush to every session, so a repeat inside the same second is dropped. Records the tag when it is new.
| [in] | tag | cnid_volume_tag() of the reset volume, network byte order |
|
static |
Make a volume's reset broadcastable again.
A sibling that missed a reset batch has no other redelivery path, so a delivery failure must not leave the tag deduped against the next duplicate.
| [in] | tag | cnid_volume_tag() of the reset volume, network byte order |
|
static |
Serialize a hint_entry to IPC wire format.
Writes IPC_HEADERLEN + sizeof(ipc_cache_hint_payload) = 22 bytes to the output buffer. The header PID/UID fields are set to the source child's PID (for receiver logging) with UID 0.
| [out] | buf | Output buffer (must have ≥ 22 bytes available) |
| [in] | e | Hint entry to serialize |
| int count |
| int count_in_window |
| struct hint_entry entries[HINT_BUF_SIZE] |
|
static |
| struct { ... } hint_buf |
|
static |
|
static |
|
static |
|
static |
|
static |
| pid_t pid |
| struct { ... } rate_track[RATE_TRACK_SIZE] |
| struct { ... } reset_seen[RESET_SEEN_SIZE] |
| int resets_in_window |
| uint32_t tag |
| time_t when |
| time_t window_start |