netatalk  4.4.0
Free and Open Source Apple Filing Protocol (AFP) Server
Loading...
Searching...
No Matches
directory.c File Reference
#include <assert.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <utime.h>
#include <bstrlib.h>
#include <atalk/adouble.h>
#include <atalk/afp.h>
#include <atalk/cnid.h>
#include <atalk/errchk.h>
#include <atalk/fce_api.h>
#include <atalk/globals.h>
#include <atalk/logger.h>
#include <atalk/netatalk_conf.h>
#include <atalk/unix.h>
#include <atalk/util.h>
#include <atalk/uuid.h>
#include <atalk/vfs.h>
#include "desktop.h"
#include "dircache.h"
#include "directory.h"
#include "file.h"
#include "filedir.h"
#include "fork.h"
#include "hash.h"
#include "mangle.h"
#include "unix.h"
#include "volume.h"

Macros

#define DIRLOOKUP_LOG_FMT   "dirlookup_internal(did:%u)"

Functions

static int netatalk_mkdir (const struct vol *vol, const char *name)
static int deletedir (const struct vol *vol, int dirfd, char *dir)
static int copydir (struct vol *vol, struct dir *ddir, int dirfd, char *src, char *dst)
static int diroffcnt (struct dir *dir, struct stat *st)
static int invisible_dots (const struct vol *vol, const char *name)
static int set_dir_errors (struct path *path, const char *where, int err)
static int cname_mtouname (const struct vol *vol, struct dir *dir, struct path *ret, int toUTF8)
 Convert name in client encoding to server encoding.
static struct pathpath_from_dir (struct vol *vol, struct dir *dir, struct path *ret)
 Build struct path from struct dir.
int get_afp_errno (const int param)
static struct dirdirlookup_internal (const struct vol *vol, cnid_t did, int retry, int strict)
 Internal CNID (Directory ID) resolution with retry control.
static struct dirdirlookup_internal_retry (const struct vol *vol, cnid_t did, int strict, bstring *fullpath_ptr, char **upath_ptr)
 Retry helper for dirlookup_internal on ENOENT.
struct dirdirlookup (const struct vol *vol, cnid_t did)
 Public CNID (Directory ID) resolution (supports files and directories).
struct dirdirlookup_strict (const struct vol *vol, cnid_t did)
 Strict DID resolution with inode validation.
struct dirdir_new (const char *m_name, const char *u_name, const struct vol *vol, cnid_t pdid, cnid_t did, bstring path, struct stat *st)
 Construct struct dir.
void dir_free (struct dir *dir)
 Free a struct dir and all its members.
struct dirdir_add (struct vol *vol, const struct dir *dir, struct path *path, int len)
 Create struct dir from struct path.
void dir_free_invalid_q (void)
 Free the queue with invalid struct dirs.
int dir_remove (const struct vol *vol, struct dir *dir)
 Remove a file/directory from dircache with automatic curdir recovery.
struct pathcname (struct vol *vol, struct dir *dir, char **cpath)
 Resolve a catalog node name path.
int movecwd (const struct vol *vol, struct dir *dir)
 chdir() to dir
int check_access (const AFPObj *obj, struct vol *vol, char *path, int mode)
int file_access (const AFPObj *obj, struct vol *vol, struct path *path, int mode)
void setdiroffcnt (struct dir *dir, struct stat *st, uint32_t count)
int dirreenumerate (struct dir *dir, struct stat *st)
int getdirparams (const AFPObj *obj, const struct vol *vol, uint16_t bitmap, struct path *s_path, struct dir *dir, char *buf, size_t *buflen)
int path_error (struct path *path, int error)
int afp_setdirparams (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
int setdirparams (struct vol *vol, struct path *path, uint16_t d_bitmap, char *buf)
int afp_syncdir (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
int afp_createdir (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
int renamedir (struct vol *vol, int dirfd, char *src, char *dst, struct dir *newparent, char *newname)
 Rename a directory.
int deletecurdir (struct vol *vol)
int afp_mapid (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
int afp_mapname (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
int afp_closedir (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
int afp_opendir (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)

Variables

int afp_errno
struct dir rootParent
struct dircurdir = &rootParent
struct path Cur_Path
q_tinvalid_dircache_entries

Macro Definition Documentation

◆ DIRLOOKUP_LOG_FMT

#define DIRLOOKUP_LOG_FMT   "dirlookup_internal(did:%u)"

Function Documentation

◆ afp_closedir()

int afp_closedir ( AFPObj * obj,
char * ibuf,
size_t ibuflen,
char * rbuf,
size_t * rbuflen )

◆ afp_createdir()

int afp_createdir ( AFPObj * obj,
char * ibuf,
size_t ibuflen,
char * rbuf,
size_t * rbuflen )

◆ afp_mapid()

int afp_mapid ( AFPObj * obj,
char * ibuf,
size_t ibuflen,
char * rbuf,
size_t * rbuflen )

◆ afp_mapname()

int afp_mapname ( AFPObj * obj,
char * ibuf,
size_t ibuflen,
char * rbuf,
size_t * rbuflen )

◆ afp_opendir()

int afp_opendir ( AFPObj * obj,
char * ibuf,
size_t ibuflen,
char * rbuf,
size_t * rbuflen )

◆ afp_setdirparams()

int afp_setdirparams ( AFPObj * obj,
char * ibuf,
size_t ibuflen,
char * rbuf,
size_t * rbuflen )

◆ afp_syncdir()

int afp_syncdir ( AFPObj * obj,
char * ibuf,
size_t ibuflen,
char * rbuf,
size_t * rbuflen )

◆ check_access()

int check_access ( const AFPObj * obj,
struct vol * vol,
char * path,
int mode )

We can't use unix file's perm to support Apple's inherited protection modes. If we aren't the file's owner we can't change its perms when moving it and smb nfs,... don't even try.

◆ cname()

struct path * cname ( struct vol * vol,
struct dir * dir,
char ** cpath )

Resolve a catalog node name path.

  1. Evaluate path type
  2. Move to start dir, if we can't, it might be e.g. because of EACCES, build path from dirname, so e.g. getdirparams has sth it can chew on. curdir is dir parent then. All this is done in path_from_dir().
  3. Parse next cnode name in path, cases:
  4. single "\0" -> do nothing
  5. two or more consecutive "\0" -> chdir("..") one or more times
  6. cnode name -> copy it to path.m_name
  7. Get unix name from mac name
  8. Special handling of request with did 1
  9. stat the cnode name
  10. If it's not there, it's probably an afp_createfile|dir, return with curdir = dir parent, struct path = dirname
  11. If it's there and it's a file, it must should be the last element of the requested path. Return with curdir = cnode name parent dir, struct path = filename
  12. Treat symlinks like files, don't follow them
  13. If it's a dir:
  14. Search the dircache for it
  15. If it's not in the cache, create a struct dir for it and add it to the cache
  16. chdir into the dir and
  17. set m_name to the mac equivalent of "."
  18. goto 3

◆ cname_mtouname()

int cname_mtouname ( const struct vol * vol,
struct dir * dir,
struct path * ret,
int toUTF8 )
static

Convert name in client encoding to server encoding.

Convert ret->m_name to ret->u_name from client encoding to server encoding. This only gets called from cname().

Returns
0 on success, -1 on error
Note
If the passed ret->m_name is mangled, we'll demangle it

◆ copydir()

int copydir ( struct vol * vol,
struct dir * ddir,
int dirfd,
char * src,
char * dst )
static

do a recursive copy.

◆ deletecurdir()

int deletecurdir ( struct vol * vol)

delete an empty directory

◆ deletedir()

int deletedir ( const struct vol * vol,
int dirfd,
char * dir )
static

◆ dir_add()

struct dir * dir_add ( struct vol * vol,
const struct dir * dir,
struct path * path,
int len )

Create struct dir from struct path.

Create a new struct dir from struct path. Then add it to the cache.

  1. Open adouble file, get CNID from it.
  2. Search the database, hinting with the CNID from (1).
  3. Build fullpath and create struct dir.
  4. Add it to the cache.
Parameters
[in]volpointer to struct vol, possibly modified in callee
[in]dirpointer to parent directory
[in,out]pathpointer to struct path with valid path->u_name
[in]lenstrlen of path->u_name
Returns
Pointer to new struct dir or NULL on error.
Note
Function also assigns path->m_name from path->u_name.

◆ dir_free()

void dir_free ( struct dir * dir)

Free a struct dir and all its members.

Parameters
dir(rw) pointer to struct dir

◆ dir_free_invalid_q()

void dir_free_invalid_q ( void )

Free the queue with invalid struct dirs.

Note
This gets called at the end of every AFP func.

◆ dir_new()

struct dir * dir_new ( const char * m_name,
const char * u_name,
const struct vol * vol,
cnid_t pdid,
cnid_t did,
bstring path,
struct stat * st )

Construct struct dir.

Construct struct dir from parameters.

Parameters
[in]m_namedirectory name in UTF8-dec
[in]u_namedirectory name in server side encoding
[in]volpointer to struct vol
[in]pdidParent CNID
[in]didCNID
[in]pathFull unix path to object
[in]ststruct stat of object
Returns
pointer to new struct dir or NULL on error
Note
Most of the time mac name and unix name are the same.

◆ dir_remove()

int dir_remove ( const struct vol * vol,
struct dir * dir )

Remove a file/directory from dircache with automatic curdir recovery.

This function centralizes global curdir safety for all callers. When removing a cache entry that is curdir, it attempts curdir recovery via CNID database and falls back to volume root if recovery fails.

  1. Check the dir
  2. Detect if removing curdir and save DID
  3. Remove from cache and queue for deallocation
  4. Set curdir=NULL if removing curdir (safer than dangling pointer)
  5. Attempt recovery via dirlookup(saved_did)
  6. If recovery fails, fallback to vol->v_root or rootParent
  7. Mark entry invalid
Parameters
[in]volvolume pointer
[in,out]dirdirectory/file entry to remove from cache
Returns
0 on success, -1 if curdir was removed and recovery failed (curdir guaranteed non-NULL on return)

◆ dirlookup()

struct dir * dirlookup ( const struct vol * vol,
cnid_t did )

Public CNID (Directory ID) resolution (supports files and directories).

Resolves a CNID (Directory ID) to its cached entry. This function supports both files and directories since DID can refer to either type. If cache hit, returns cache entry, if cache miss, returns CNID database record and updates cache

Parameters
[in]volpointer to struct vol
[in]didDID to resolve
Returns
pointer to struct dir (may have DIRF_ISFILE flag for files)

◆ dirlookup_internal()

struct dir * dirlookup_internal ( const struct vol * vol,
cnid_t did,
int retry,
int strict )
static

Internal CNID (Directory ID) resolution with retry control.

Resolve a CNID (Directory ID), allocate a struct dir for it.

Algorithm:

  1. Check for special DIDs 0 (invalid), 1 (root parent), and 2 (root).
  2. Search dircache:
    • If found and valid, return cached entry
    • If strict mode, validate inode before use
  3. On cache miss, query CNID database for path component
  4. Recurse to build parent chain (terminates at root or cache hit)
  5. Build fullpath and stat to verify existence
  6. Create new struct dir and populate
  7. Add new entry to cache

On ENOENT during stat (step 5), if retry=1:

  • Clean stale cache entries
  • Retry once via CNID (prevents TOCTOU issues)
Parameters
[in]volpointer to struct vol
[in]didCNID to resolve
[in]retry1 = allow one retry on ENOENT, 0 = no retry
[in]strict1 = strict dircache lookup (validate with stat+inode), 0 = optimistic
Returns
pointer to struct dir, or NULL with afp_errno set

◆ dirlookup_internal_retry()

struct dir * dirlookup_internal_retry ( const struct vol * vol,
cnid_t did,
int strict,
bstring * fullpath_ptr,
char ** upath_ptr )
static

Retry helper for dirlookup_internal on ENOENT.

When stat() fails with ENOENT, this function cleans stale cache entries and retries the lookup via CNID database. This handles race conditions where directories are renamed/moved by external processes between cache and stat.

Parameters
[in]volVolume
[in]didTarget DID that failed stat
[in]strictValidation mode (0=initial target, 1=parent recursion)
[in,out]fullpath_ptrPointer to fullpath to cleanup
[in,out]upath_ptrPointer to upath to cleanup
Returns
Retried struct dir from CNID, or NULL on failure

◆ dirlookup_strict()

struct dir * dirlookup_strict ( const struct vol * vol,
cnid_t did )

Strict DID resolution with inode validation.

Like dirlookup(), but performs additional validation to ensure the cached directory entry matches the actual filesystem state. This prevents race conditions where a directory has been renamed/moved but the cache entry still points to the old path/has become stale.

Use this for destructive operations (delete, rename, move) where operating on the wrong object could cause data loss.

Parameters
[in]volpointer to struct vol
[in]didDID to resolve
Returns
pointer to validated struct dir, or NULL with afp_errno set

◆ diroffcnt()

int diroffcnt ( struct dir * dir,
struct stat * st )
static

is our cached offspring count valid?

◆ dirreenumerate()

int dirreenumerate ( struct dir * dir,
struct stat * st )

◆ file_access()

int file_access ( const AFPObj * obj,
struct vol * vol,
struct path * path,
int mode )

◆ get_afp_errno()

int get_afp_errno ( const int param)

◆ getdirparams()

int getdirparams ( const AFPObj * obj,
const struct vol * vol,
uint16_t bitmap,
struct path * s_path,
struct dir * dir,
char * buf,
size_t * buflen )

◆ invisible_dots()

int invisible_dots ( const struct vol * vol,
const char * name )
static

◆ movecwd()

int movecwd ( const struct vol * vol,
struct dir * dir )

chdir() to dir

Parameters
[in]volpointer to struct vol
[in]dirpointer to struct dir
Returns
0 on success, -1 on error with afp_errno set appropriately

◆ netatalk_mkdir()

int netatalk_mkdir ( const struct vol * vol,
const char * name )
static

◆ path_error()

int path_error ( struct path * path,
int error )

◆ path_from_dir()

struct path * path_from_dir ( struct vol * vol,
struct dir * dir,
struct path * ret )
static

Build struct path from struct dir.

The final movecwd in cname failed, possibly with EPERM or ENOENT. We:

  1. move cwd into parent dir (we're often already there, but not always)
  2. set struct path to the dirname
  3. in case of AFPERR_ACCESS: the dir is there, we just can't chdir into it AFPERR_NOOBJ: the dir was there when we stated it in cname, so we have a race
    1. indicate there's no dir for this path
    2. remove the dir

◆ renamedir()

int renamedir ( struct vol * vol,
int dirfd,
char * src,
char * dst,
struct dir * newparent,
char * newname )

Rename a directory.

Parameters
volvolume
dirfd-1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
srcold unix filename (not a pathname)
dstnew unix filename (not a pathname)
newparentcurdir
newnamenew mac name

◆ set_dir_errors()

int set_dir_errors ( struct path * path,
const char * where,
int err )
static

◆ setdiroffcnt()

void setdiroffcnt ( struct dir * dir,
struct stat * st,
uint32_t count )

◆ setdirparams()

int setdirparams ( struct vol * vol,
struct path * path,
uint16_t d_bitmap,
char * buf )
Note
assume path == '\0' e.g. it's a directory in canonical form

Variable Documentation

◆ afp_errno

int afp_errno

◆ Cur_Path

struct path Cur_Path
Initial value:
= {
0,
"",
".",
0,
NULL,
0,
0,
{0}
}

◆ curdir

struct dir* curdir = &rootParent

◆ invalid_dircache_entries

q_t* invalid_dircache_entries

◆ rootParent

struct dir rootParent
Initial value:
= {
NULL, NULL, NULL, NULL,
NULL, 0, 0, 0,
0, 0, 0, 0,
0, 0
}