Skip to content

Dev Docs Dev Guidelines

Andy Lemin edited this page Aug 17, 2025 · 5 revisions

WIP - ALL LINKS IN THIS WIKI STRUCTURE ARE CURRENTLY BROKEN DURING WIKI MIGRATION

THESE ARE COMMUNITY DOCS

Information on this page currently either duplicates or conflicts with the official Developer-Notes. This page needs to be cleaned up to accurately supplement the main Dev Notes page

Netatalk Development Guidelines

Overview This document outlines the coding standards, best practices, and development procedures for contributing to the Netatalk project. Following these guidelines ensures code consistency, maintainability, and compatibility across the project.

Code Standards

Language and Compiler Requirements
C Standard: C11 with GNU extensions
Compiler: GCC 4.9+ or Clang 3.5+
Target Platforms: Linux, BSD variants, macOS, Solaris
Architecture Support: x86, x86_64, ARM, ARM64, SPARC
Implementation Files
configure.ac - Autotools configuration with compiler and platform detection
meson.build - Main Meson build configuration with compiler requirements
include/atalk/compat.h - Cross-platform compatibility definitions
libatalk/compat/ - Platform-specific compatibility implementations

Coding Style

Naming Conventions
// Function names: lowercase with underscores
int afp_login_user(struct AFPObj *obj, char *username);

// Variable names: lowercase with underscores
struct vol *current_volume;
int connection_count;

// Constants: uppercase with underscores
#define AFP_MAX_USERNAME_LEN    256
#define CNID_INVALID           0

// Structure names: lowercase with underscores
struct cnid_database {
    DB_ENV *env;
    DB *cnid_db;
    DB *devino_db;
};

// Enum values: uppercase with prefix
enum afp_result {
    AFP_OK = 0,
    AFP_ERROR = -1,
    AFP_ACCESS_DENIED = -5000
};

Code Formatting

// Function definitions
static int process_afp_command(struct AFPObj *obj, 
                              char *ibuf, size_t ibuflen,
                              char *rbuf, size_t *rbuflen)
{
    EC_INIT;
    int command;
    int result = AFP_OK;
    
    // Extract command from buffer
    if (ibuflen < 1) {
        LOG(log_error, "Invalid AFP command buffer length");
        EC_FAIL;
    }
    
    command = *ibuf;
    
    // Process command based on type
    switch (command) {
        case AFP_LOGIN:
            EC_ZERO(afp_login(obj, ibuf + 1, ibuflen - 1, rbuf, rbuflen));
            break;
            
        case AFP_LOGOUT:
            EC_ZERO(afp_logout(obj));
            break;
            
        default:
            LOG(log_warning, "Unknown AFP command: %d", command);
            result = AFP_CALL_NOT_SUPPORTED;
            break;
    }
    
    EC_STATUS(result);
    
EC_CLEANUP:
    EC_EXIT;
}

// Control structures
if (condition) {
    // Single statement or block
    do_something();
} else if (other_condition) {
    // Multiple statements
    do_this();
    do_that();
} else {
    // Default case
    do_default();
}

// Loop structures
for (int i = 0; i < count; i++) {
    process_item(items[i]);
}

while (condition) {
    if (check_exit_condition()) {
        break;
    }
    process_data();
}

Header File Organization

#ifndef _ATALK_EXAMPLE_H
#define _ATALK_EXAMPLE_H 1

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

// System includes
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

// Project includes
#include <atalk/adouble.h>
#include <atalk/cnid.h>
#include <atalk/logger.h>

// Forward declarations
struct afp_obj;
struct vol;

// Constants
#define EXAMPLE_MAX_SIZE    1024
#define EXAMPLE_DEFAULT     "default_value"

// Type definitions
typedef struct example_data {
    int         ed_id;
    char       *ed_name;
    size_t      ed_size;
} example_data_t;

// Function prototypes
extern int example_init(struct afp_obj *obj);
extern int example_process(struct vol *vol, const char *path);
extern void example_cleanup(void);

#endif /* _ATALK_EXAMPLE_H */
Error Handling
Netatalk uses a standardized error handling system based on macros defined in <atalk/errchk.h>.

Implementation Files

include/atalk/errchk.h - Core error checking macro definitions
libatalk/util/fault.c - Error handling utilities and stack trace generation
libatalk/util/logger.c - Error logging integration with error checking
Error Checking Macros
// Basic error checking template
int example_function(const char *filename)
{
    EC_INIT;
    FILE *file = NULL;
    char *buffer = NULL;
    
    // Check parameters
    EC_NULL_LOG(filename);
    
    // Open file with error checking
    EC_NULL(file = fopen(filename, "r"));
    
    // Allocate memory with error checking
    EC_NULL(buffer = malloc(BUFFER_SIZE));
    
    // Function call with zero return check
    EC_ZERO(process_file_data(file, buffer));
    
    // Set successful return status
    EC_STATUS(0);
    
EC_CLEANUP:
    // Cleanup resources
    if (file != NULL) {
        fclose(file);
    }
    if (buffer != NULL) {
        free(buffer);
    }
    
    EC_EXIT;
}

Available Error Checking Macros

EC_INIT: Initialize error handling (must be first)
EC_NULL(expr): Check for NULL pointer
EC_NULL_LOG(expr): Check for NULL with logging
EC_ZERO(expr): Check for zero return value
EC_ZERO_LOG(expr): Check for zero with logging
EC_NEG1(expr): Check for -1 return value
EC_FALSE(expr): Check for false return value
EC_STATUS(val): Set return status value
EC_FAIL: Jump to cleanup with error
EC_EXIT: Return with current status (must be last)

Logging

Use the centralized logging system for all debug and error output.

Implementation Files
include/atalk/logger.h - Logging system interface and level definitions
libatalk/util/logger.c - Core logging implementation with file/syslog support
include/atalk/debug.h - Debug logging macros and conditional compilation
etc/afpd/main.c - AFP daemon logging initialization
etc/netatalk/netatalk.c - Master daemon logging setup
Log Levels
// Log level definitions
typedef enum {
    log_none = 0,
    log_severe,     // System errors, crashes
    log_error,      // Serious errors affecting operation
    log_warning,    // Warnings, recoverable errors
    log_note,       // Important information
    log_info,       // General information
    log_debug,      // Debug information
    log_debug6,     // Verbose debug
    log_debug7,     // Very verbose debug
    log_debug8,     // Extremely verbose debug
    log_debug9,     // Maximum verbosity
    log_maxdebug
} log_severity_t;
Logging Examples
// Error conditions
LOG(log_error, "Failed to open database: %s", strerror(errno));

// Warnings
LOG(log_warning, "Volume '%s' not found, using default", volume_name);

// Information
LOG(log_info, "Client %s connected from %s", username, client_ip);

// Debug information
LOG(log_debug, "Processing AFP command %d, length %zu", cmd, len);

// Debug with detailed information
LOG(log_debug6, "CNID lookup: did=%u name='%s' -> cnid=%u", 
    did, filename, cnid);

Memory Management

Dynamic Memory Allocation
Implementation Files
libatalk/util/talloc.c - Memory allocation wrapper with debugging support
include/atalk/talloc.h - Memory management interface definitions
libatalk/util/memcheck.c - Memory leak detection and validation utilities
// Always check allocation results
char *buffer = malloc(size);
if (buffer == NULL) {
    LOG(log_error, "Memory allocation failed");
    return -1;
}

// Use calloc for zero-initialized memory
struct connection *conn = calloc(1, sizeof(struct connection));
if (conn == NULL) {
    LOG(log_error, "Failed to allocate connection structure");
    return NULL;
}

// Always free allocated memory
free(buffer);
buffer = NULL;  // Prevent double-free
String Handling
// Use safe string functions
char dest[MAXPATHLEN];
strlcpy(dest, source, sizeof(dest));
strlcat(dest, suffix, sizeof(dest));

// For dynamic strings
char *result = NULL;
if (asprintf(&result, "User: %s, Volume: %s", username, volname) == -1) {
    LOG(log_error, "String allocation failed");
    return -1;
}
// Remember to free(result) later

// String length checking
if (strnlen(input, MAX_INPUT_LEN) >= MAX_INPUT_LEN) {
    LOG(log_error, "Input string too long");
    return -1;
}

Build System (Meson)

Project Structure
Implementation Files
meson.build - Root build configuration with project structure
meson_options.txt - Build option definitions and defaults
subprojects/ - Dependency subproject definitions
test/meson.build - Test suite build configuration
etc/meson.build - Daemon executables build rules
libatalk/meson.build - Core library build configuration
include/meson.build - Header installation rules
# meson.build example for new component
component_sources = [
    'component.c',
    'component_utils.c',
    'component_config.c'
]

component_deps = [
    atalk_dep,      # Core Netatalk library
    crypto_dep,     # Cryptographic functions
    db_dep          # Database dependencies
]

component_lib = static_library(
    'component',
    component_sources,
    dependencies: component_deps,
    include_directories: [
        include_directories('.'),
        include_directories('../include')
    ],
    c_args: [
        '-DCOMPONENT_VERSION="@0@"'.format(meson.project_version())
    ]
)

Configuration Options

# Adding new configuration option
option('with-component',
    type: 'boolean',
    value: true,
    description: 'Enable component functionality'
)

# In main meson.build
if get_option('with-component')
    component_enabled = true
    cdata.set('HAVE_COMPONENT', 1)
else
    component_enabled = false
endif
Testing Integration
# Unit test example
test_component = executable(
    'test_component',
    ['test_component.c'],
    dependencies: [component_dep, unity_dep],
    include_directories: test_includes
)

test('component_tests', test_component)

Testing Guidelines

Unit Testing
Implementation Files
test/ - Main test suite directory with unit and integration tests
test/testsuite.c - Test framework initialization and runner
test/afpd_test.c - AFP daemon unit tests
test/cnid_test.c - CNID system unit tests
include/atalk/test_framework.h - Test macros and assertion definitions
libatalk/util/test_util.c - Testing utility functions
test/meson.build - Test build configuration and execution rules
// Test framework integration
#include <atalk/test_framework.h>

// Test case structure
static int test_cnid_operations(void) {
    struct cnid_db *db = NULL;
    cnid_t cnid;
    
    // Setup
    TEST_ASSERT_NOT_NULL(db = cnid_open("/tmp/test_db"));
    
    // Test CNID creation
    cnid = cnid_add(db, &test_stat, DIRDID_ROOT, "testfile", 8, NULL);
    TEST_ASSERT_NOT_EQUAL(cnid, CNID_INVALID);
    
    // Test CNID lookup
    cnid_t lookup_cnid = cnid_get(db, DIRDID_ROOT, "testfile", 8);
    TEST_ASSERT_EQUAL(cnid, lookup_cnid);
    
    // Cleanup
    cnid_close(db);
    return 0;
}

// Test registration
void register_cnid_tests(void) {
    TEST_REGISTER(test_cnid_operations);
}
Integration Testing
// Integration test example
static int test_afp_login_flow(void) {
    struct AFPObj obj;
    char login_buf[256];
    char reply_buf[256];
    size_t reply_len = sizeof(reply_buf);
    int ret;
    
    // Initialize AFP object
    memset(&obj, 0, sizeof(obj));
    TEST_ASSERT_ZERO(afp_options_parse(&obj, test_config));
    
    // Simulate login request
    build_login_request(login_buf, "testuser", "testpass");
    
    // Process login
    ret = afp_login(&obj, login_buf, sizeof(login_buf), 
                   reply_buf, &reply_len);
    
    TEST_ASSERT_EQUAL(ret, AFP_OK);
    TEST_ASSERT_NOT_NULL(obj.username);
    
    // Cleanup
    afp_options_free(&obj);
    return 0;
}

Documentation

Code Documentation
Implementation Files
doc/ - Documentation root directory with all user and developer docs
doc/developer/ - Developer-focused documentation and guides
doc/manpages/ - Manual pages for commands and configuration files
include/atalk/ - Header files with inline API documentation
CONTRIBUTORS - Contributor guidelines and recognition
README.md - Project overview and quick start guide
/*!
 * @brief Process AFP directory enumeration command
 * 
 * This function handles the FPEnumerate AFP command, which retrieves
 * directory contents for display in Mac Finder windows.
 * 
 * @param obj     AFP session object
 * @param ibuf    Input buffer containing enumeration parameters
 * @param ibuflen Length of input buffer
 * @param rbuf    Output buffer for directory entries
 * @param rbuflen Pointer to output buffer length
 * 
 * @return AFP error code:
 *         - AFP_OK on success
 *         - AFPERR_NOOBJ if directory not found
 *         - AFPERR_ACCESS if access denied
 *         - AFPERR_PARAM if invalid parameters
 * 
 * @note The function respects volume access permissions and applies
 *       appropriate filename encoding conversion.
 * 
 * @see afp_openvol(), afp_closedir()
 */
int afp_enumerate(AFPObj *obj, char *ibuf, size_t ibuflen,
                  char *rbuf, size_t *rbuflen)
{
    // Implementation...
}

Function Documentation Standards

Purpose: Brief description of what the function does
Parameters: Description of each parameter and its constraints
Return Value: Possible return values and their meanings
Side Effects: Any global state changes or external effects
Thread Safety: Concurrent access considerations
Examples: Usage examples for complex functions
Git Workflow
Branch Management
Implementation Files
.gitignore - Git ignore patterns for build artifacts and temporary files
.github/ - GitHub workflow and issue template configurations
CONTRIBUTING.md - Contribution guidelines and pull request process
ChangeLog - Historical change log with version history
# Main branches
main                    # Stable release branch
develop                 # Development integration branch

# Feature branches
feature/spotlight-v2    # New feature development
bugfix/cnid-corruption  # Bug fix branches
hotfix/security-patch   # Critical fixes for releases
Commit Messages
component: brief description of change

Longer explanation of what this commit does and why it was necessary.
Include any breaking changes, migration notes, or special considerations.

- Specific change 1
- Specific change 2
- Reference to issue #123

Signed-off-by: Developer Name <[email protected]>
Pull Request Process
Create Feature Branch: git checkout -b feature/your-feature main
Implement Changes: Follow coding guidelines and add tests
Update Documentation: Update relevant docs and man pages
Test Thoroughly: Run unit tests and integration tests
Submit PR: Create pull request with detailed description
Code Review: Address reviewer feedback
CI Validation: Ensure all CI checks pass
Merge: Squash and merge to main or development branches
Platform Compatibility
Portability Guidelines
// Use feature detection instead of platform checks
#ifdef HAVE_SENDFILE
    // Use sendfile for efficient file transfers
    ret = sendfile(outfd, infd, NULL, count);
#else
    // Fallback to read/write loop
    ret = manual_copy(outfd, infd, count);
#endif

// Handle platform differences in data types
#if defined(__FreeBSD__) || defined(__NetBSD__)
    typedef off_t file_offset_t;
#elif defined(__linux__)
    typedef loff_t file_offset_t;
#else
    typedef off_t file_offset_t;
#endif

Endianness Handling

// Use standard byte order functions
#include <sys/types.h>
#include <netinet/in.h>

// Network to host byte order
uint32_t network_value = ntohl(wire_value);
uint16_t host_port = ntohs(wire_port);

// Host to network byte order
uint32_t wire_value = htonl(host_value);
uint16_t wire_port = htons(host_port);
Security Considerations
Input Validation
Implementation Files
libatalk/util/string_check.c - String validation and sanitization functions
include/atalk/security.h - Security-related function prototypes
etc/afpd/auth.c - Authentication input validation
libatalk/unicode/utf8.c - UTF-8 validation and normalization
libatalk/util/fault.c - Security-focused error handling

// Always validate input parameters
static int validate_path_component(const char *name, size_t len) {
    if (name == NULL || len == 0) {
        return -1;
    }
    
    // Check for path traversal attempts
    if (strstr(name, "..") != NULL) {
        LOG(log_warning, "Path traversal attempt detected: %s", name);
        return -1;
    }
    
    // Check for invalid characters
    if (strchr(name, '/') != NULL || strchr(name, '\\') != NULL) {
        LOG(log_warning, "Invalid path characters: %s", name);
        return -1;
    }
    
    return 0;
}

Buffer Management

// Use bounded string operations
char buffer[MAXPATHLEN];
int ret = snprintf(buffer, sizeof(buffer), "%s/%s", basedir, filename);
if (ret >= sizeof(buffer)) {
    LOG(log_error, "Path too long: %s/%s", basedir, filename);
    return -1;
}

// Validate buffer lengths
if (input_len > sizeof(internal_buffer) - 1) {
    LOG(log_error, "Input buffer too large: %zu", input_len);
    return AFPERR_PARAM;
}

Privilege Management

Implementation Files
etc/netatalk/netatalk.c - Master daemon privilege dropping implementation
libatalk/util/server_child.c - Child process privilege management
etc/afpd/main.c - AFP daemon privilege initialization
include/atalk/server_child.h - Process privilege management interface
// Drop privileges when appropriate
static int drop_privileges(uid_t uid, gid_t gid) {
    // Set supplementary groups
    if (setgroups(0, NULL) != 0) {
        LOG(log_error, "Failed to clear supplementary groups");
        return -1;
    }
    
    // Set group ID
    if (setgid(gid) != 0) {
        LOG(log_error, "Failed to set group ID to %d", gid);
        return -1;
    }
    
    // Set user ID
    if (setuid(uid) != 0) {
        LOG(log_error, "Failed to set user ID to %d", uid);
        return -1;
    }
    
    return 0;
}

Performance Guidelines

Optimization Strategies
Minimize System Calls: Batch operations where possible
Use Efficient Data Structures: Choose appropriate algorithms
Cache Frequently Used Data: Implement intelligent caching
Avoid Unnecessary Memory Allocation: Reuse buffers when safe
Profile Before Optimizing: Use tools like gprof and valgrind
Memory Usage
// Prefer stack allocation for small, temporary data
char temp_buffer[256];  // Stack allocated

// Use dynamic allocation for large or variable-sized data
size_t buffer_size = calculate_needed_size();
char *large_buffer = malloc(buffer_size);
Following these guidelines helps maintain code quality, ensures compatibility across platforms, and facilitates collaboration among developers working on the Netatalk project.
Clone this wiki locally