UNLINK(2)

okay so. you're in section 2 of the system calls manual (that's the System Calls Manual bestie, kernel territory πŸ‘‘) and the page is called UNLINK. two functions, one glow-up: unlink and unlinkat. it's giving "delete but make it a syscall." let's GO πŸ¦‹


✨ NAME ✨

unlink, unlinkat β€” remove directory entry

πŸ“š LIBRARY πŸ“š

Standard C Library (libc, -lc) β€” yeah yeah, link the C library, you know the drill πŸŽ€

🧚 SYNOPSIS 🧚

#include <unistd.h>

int
unlink(const char *path);

int
unlinkat(int fd, const char *path, int flag);

🌸 DESCRIPTION 🌸

so the unlink() system call removes the link named by path from its directory, and then decrements the link count of the file that was referenced by that link. no cap. and here's the tea: if that decrement drops the link count of the file all the way down to zero, and literally no process has the file open? then all the resources associated with that file get reclaimed. bye girl πŸ’€

but if one or more processes still have the file open when the last link gets removed, the link is removed, but the actual removal of the file is delayed until all references to it have been closed. it's lowkey just vibing in limbo until everyone lets go 🫢

also β€” huge β€” the path argument may not be a directory. not the move, babe.

now unlinkat(). it's equivalent to unlink() or rmdir(), EXCEPT when path specifies a relative path. in that case, the directory entry to be removed is determined relative to the directory associated with the file descriptor fd β€” instead of relative to the current working directory. main character energy: it's like saying "not from here, from there" πŸ’…

the values for flag are constructed by a bitwise-inclusive OR of flags from this list, defined in <fcntl.h>:

AT_REMOVEDIR

remove the directory entry specified by fd and path as a directory, not a normal file. okay so it's rmdir mode, got it.


and if unlinkat() gets passed the special value AT_FDCWD in the fd parameter, the current working directory is used, and the behavior is identical to a call to unlink or rmdir respectively β€” depending on whether or not the AT_REMOVEDIR bit is set in flag. cute, right? ✨

πŸŽ€ RETURN VALUES πŸŽ€

the unlink() function returns the value 0 if successful πŸ₯³ otherwise the value -1 is returned and the global variable errno is set to indicate the error. classic.

πŸ’€ ERRORS πŸ’€

the unlink() succeeds unless:

  • [ENOTDIR] β€” a component of the path prefix is not a directory. 🚫
  • [EISDIR] β€” the named file is a directory. girl that's a folder.
  • [ENAMETOOLONG] β€” a component of a pathname exceeded 255 characters, or an entire path name exceeded 1023 characters. that's so long babe, chill.
  • [ENOENT] β€” the named file does not exist. it's not there. it never was. πŸ™ˆ
  • [EACCES] β€” search permission is denied for a component of the path prefix.
  • [EACCES] β€” write permission is denied on the directory containing the link to be removed.
  • [ELOOP] β€” too many symbolic links were encountered in translating the pathname. infinite loop vibes, absolutely not πŸŒ€
  • [EPERM] β€” the named file is a directory.
  • [EPERM] β€” the named file has its immutable, undeletable or append-only flag set. see the chflags(2) manual page for more info, iykyk.
  • [EPERM] β€” the parent directory of the named file has its immutable or append-only flag set.
  • [EPERM] β€” the directory containing the file is marked sticky, and neither the containing directory nor the file to be removed are owned by the effective user ID. sticky directory says hands OFF βœ‹
  • [EIO] β€” an I/O error occurred while deleting the directory entry or deallocating the inode. yikes.
  • [EINTEGRITY] β€” corrupted data was detected while reading from the file system. that's cursed, fr fr.
  • [EROFS] β€” the named file resides on a read-only file system. read. only. no touchy.
  • [EFAULT] β€” the path argument points outside the process's allocated address space. that pointer is somewhere it should not be πŸ‘€
  • [ENOSPC] β€” on file systems supporting copy-on-write or snapshots, there was not enough free space to record metadata for the delete operation of the file. deleting needs space, the plot twist πŸ“

and in addition to the errors returned by the unlink(), the unlinkat() may fail if:

  • [EBADF] β€” the path argument does not specify an absolute path and the fd argument is neither AT_FDCWD nor a valid file descriptor open for searching.
  • [ENOTEMPTY] β€” the flag parameter has the AT_REMOVEDIR bit set and the path argument names a directory that is not an empty directory, or there are hard links to the directory other than dot or a single entry in dot-dot. empty it out first bestie, i don't make the rules.
  • [ENOTDIR] β€” the flag parameter has the AT_REMOVEDIR bit set and path does not name a directory.
  • [EINVAL] β€” the value of the flag argument is not valid. that flag? fake. not real. πŸ’€
  • [ENOTDIR] β€” the path argument is not an absolute path and fd is neither AT_FDCWD nor a file descriptor associated with a directory.
ok real talk, the list is long but it's all "you asked for something the kernel can't give you." read the bracket, fix the thing, move on. periodt. πŸ‘‘

πŸ‘€ SEE ALSO πŸ‘€

chflags(2), close(2), link(2), rmdir(2), symlink(7)

πŸ“œ STANDARDS πŸ“œ

the unlinkat() system call follows The Open Group Extended API Set 2 specification. she's certified ✨

πŸ•°οΈ HISTORY πŸ•°οΈ

the unlink() function appeared in Version 1 AT&T UNIX. yes, Version 1. ancient. iconic. a true original πŸ‘‘

the unlinkat() system call appeared in FreeBSD 8.0.

and here's a lil lore drop: the unlink() system call traditionally allows the super-user to unlink directories, which can damage the file system integrity. 😭 this implementation no longer permits it. so if you were hoping to speedrun filesystem corruption as root β€” no. absolutely not. the door is closed, we don't do that anymore πŸ™…β€β™€οΈ


k bye bestie, go delete something responsibly 🫢 this page is from the Debian crew, dated March 30, 2020 β€” mwah πŸ’‹

πŸ“¦ where this page comes from