symlink(2)
๐ symlink(2) โ bestie, we're making new names for files ๐
okay so this is the System Calls Manual page for symlink(2), and it's literally one of the most iconic little syscalls out there. it's in section 2 (system calls, iykyk) and it's giving "i make a nickname for a file and the kernel is fine with it." let's get into it ๐ฆ
โจ NAME โจ
symlink, symlinkat โ make a new name for a file ๐
๐ LIBRARY ๐
Standard C library (libc, -lc). yeah bestie, you gotta link the libc. it's free real estate.
๐ธ SYNOPSIS ๐ธ
#include <unistd.h>
int symlink(const char *target, const char *linkpath);
#include <fcntl.h> /* Definition of AT_* constants */
#include <unistd.h>
int symlinkat(const char *target, int newdirfd, const char *linkpath);
๐ง Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
symlink():
_XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
|| /* glibc <= 2.19: */ _BSD_SOURCE
symlinkat():
Since glibc 2.10:
_POSIX_C_SOURCE >= 200809L
Before glibc 2.10:
_ATFILE_SOURCE
not the macro requirement lore ๐ yeah, if you're on old glibc you gotta define the right thing or the compiler will simply Not Know Her.
๐ DESCRIPTION ๐
symlink() creates a symbolic link named linkpath which contains the string target. that's it. that's the whole vibe. you hand it a string, it hands you a link. ๐ซถ
Symbolic links are interpreted at run time as if the contents of the link had been substituted into the path being followed to find a file or directory. so it's not a copy, it's likeโฆ a read the room moment where the kernel reads target and goes "okay we're going there instead."
Symbolic links may contain .. path components, which (if used at the start of the link) refer to the parent directories of that in which the link resides. so .. at the front? that's the parents of wherever the link lives. periodt.
A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent one; the latter case is known as a dangling link. dangling links are lowkey iconic, no cap โ they're just links with main character energy pointing at nothing. ๐
The permissions of a symbolic link are irrelevant; the ownership is ignored when following the link (except when the protected_symlinks feature is enabled, as explained in proc(5)), but is checked when removal or renaming of the link is requested and the link is in a directory with the sticky bit (S_ISVTX) set. so: perms don't matter when you follow it, ownership doesn't matter when you follow it either (unless protected_symlinks is on), but ownership does get checked when you try to remove or rename it while it's chillin' in a sticky-bit directory.
If linkpath exists, it will not be overwritten. nope. not even a little. the existing file is safe, bestie, this call is not about to body it.
๐ฆ symlinkat()
The symlinkat() system call operates in exactly the same way as symlink(), except for the differences described here. same girl, different font.
If the pathname given in linkpath is relative, then it is interpreted relative to the directory referred to by the file descriptor newdirfd (rather than relative to the current working directory of the calling process, as is done by symlink() for a relative pathname). so you get to pick your starting point instead of the default one.
If linkpath is relative and newdirfd is the special value AT_FDCWD, then linkpath is interpreted relative to the current working directory of the calling process (like symlink()). so AT_FDCWD is basically the "ok do it the normal way" button. ๐
If linkpath is absolute, then newdirfd is ignored. absolute path? newdirfd is simply not invited to the party.
See openat(2) for an explanation of the need for symlinkat(). ^^ go read that one if you want the why, bestie.
โญ RETURN VALUE โญ
On success, zero is returned. On error, -1 is returned, and errno is set to indicate the error. classic. 0 means slay, -1 means check errno immediately. ๐
๐ฅ ERRORS ๐ฅ
EACCESโ Write access to the directory containinglinkpathis denied, or one of the directories in the path prefix oflinkpathdid not allow search permission. (See alsopath_resolution(7).)EBADFโ (symlinkat())linkpathis relative butnewdirfdis neitherAT_FDCWDnor a valid file descriptor.EDQUOTโ The user's quota of resources on the filesystem has been exhausted. The resources could be inodes or disk blocks, depending on the filesystem implementation.EEXISTโlinkpathalready exists.EFAULTโtargetorlinkpathpoints outside your accessible address space.EIOโ An I/O error occurred.ELOOPโ Too many symbolic links were encountered in resolvinglinkpath.ENAMETOOLONGโtargetorlinkpathwas too long.ENOENTโ A directory component inlinkpathdoes not exist or is a dangling symbolic link, ortargetorlinkpathis an empty string.ENOENTโ (symlinkat())linkpathis a relative pathname andnewdirfdrefers to a directory that has been deleted.ENOMEMโ Insufficient kernel memory was available.ENOSPCโ The device containing the file has no room for the new directory entry.ENOTDIRโ A component used as a directory inlinkpathis not, in fact, a directory.ENOTDIRโ (symlinkat())linkpathis relative andnewdirfdis a file descriptor referring to a file other than a directory.EPERMโ The filesystem containinglinkpathdoes not support the creation of symbolic links.EROFSโlinkpathis on a read-only filesystem.
note howENOENTandENOTDIReach show up twice โ that's not a typo bestie, one is the plain version and one is thesymlinkat()-specific situation. read both, they're different girls. ๐
๐ STANDARDS ๐
POSIX.1-2024. she's standardized and she's proud. โจ
๐ HISTORY ๐
symlink()
SVr4,4.3BSD,POSIX.1-2001.
symlinkat()
POSIX.1-2008. Linux 2.6.16, glibc 2.4.
๐ซ glibc notes
On older kernels where symlinkat() is unavailable, the glibc wrapper function falls back to the use of symlink(). When linkpath is a relative pathname, glibc constructs a pathname based on the symbolic link in /proc/self/fd that corresponds to the newdirfd argument. so glibc is out here doing improv to fake it on ancient kernels. respect the hustle. ๐ซถ
๐ NOTES ๐
No checking of target is done. fr fr, zero validation, the kernel just trusts you. ๐ญ
Deleting the name referred to by a symbolic link will actually delete the file (unless it also has other hard links). If this behavior is not desired, use link(2).
โ ๏ธ okay this part is important: if youunlinkwhat the symlink points at, you're deleting the actual file, not just the link (unless it's got other hard links holding it down). if that's not what you want, uselink(2)instead. pls don't learn this the hard way. ๐
๐ SEE ALSO ๐
ln(1), namei(1), lchown(2), link(2), lstat(2), open(2), readlink(2), rename(2), unlink(2), path_resolution(7), symlink(7)
and that's the whole page, bestie! ๐ this one comes to you from Linux man-pages 6.20, dated 2026-02-08. go make some links, stay hydrated, and remember: target is a string, not a promise. ๐โจ๐ฆ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 1992, Drew Eckhardt <drew@cs.colorado.edu>
- Copyright 2006-2014, Michael Kerrisk <mtk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms