netatalk.io

[TN#009] Netatalk and ZFS nbmand property

Author
Ralph Böhme
Published on
April 17, 2012

Running the Netatalk test-suite with a volume on a ZFS set with ZFS property nbmand set to on causes nearly all tests to fail. The reason is (and it’s something Oracle hides well under the covers): it modifies POSIX semantics up to a point that POSIX compliant applications fail left and right.

We’ve written and attached a small C program which demonstrates the effect. It opens a file in read/write mode, then forks a child process and in the child process tries to rename the file.

Expected result on a POSIX conforming platform: success.
Actual result with nbmand=on: permission denied.

Usage:
$ gcc -o nbmand-demo nbmand-demo.c
$ cd /path/zfs-set/with/nbmand=off
$ touch file
$ /path/to/nbmand-demo file
… success …
$ cd /path/zfs-set/with/nbmand=on
$ touch file
$ /path/to/nbmand-demo file
… failure …

Therefor it is necessary to turn nbmand to off on any ZFS dataset you use for Netatalk AFP volumes.

nbmand-demo.c

#define _FILE_OFFSET_BITS 64

#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    const char *file;
    struct flock fl;
    int fd;
    pid_t pid;

    if (argc != 2) {
        printf("usage: %s FILE\n", argv[0]);
        exit(1);
    }
    file = argv[1];

    printf("Press  to open file \"%s\": ", file);
    getchar();

    if ((fd = open(file, O_RDWR)) == -1) {
        perror("open");
        exit(1);
    }

    if ((pid = fork()) == -1) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {
        /* Child */
        printf("Press  to rename file \"%s\" -> \"tmp\": ", file);
        getchar();

        if (rename(file, "tmp") == -1) {
            perror("rename");
        } else {
            printf("renamed.\n");
            rename("tmp", file);
        }

        printf("Press  to exit");
        getchar();
        exit(0);
    }

    /* Parent */
    if (waitpid(pid, NULL, 0) != pid) {
        perror("waitpid");
        exit(1);
    }

    close(fd);

    return 0;
}