Subject: Re: LINK a symbolic link?

From: Peter Staubach <[email-masked]>
Date: Tue, 27 Nov 2001 14:21:51 -0700 (MST)

> 
> > It is unfortunate,
> > but the UNIX link(2) system will allow a link to a directory.
> 
> The FreeBSD and GNU/Linux manpages for link(2) both claim you'll get
> EPERM if you try to link to a directory (probably so that every
> directory has one parent--no other types of entries are similarly
> restricted), and FreeBSD's /bin/ln reports "is a directory".  Does
> POSIX specify this one way or the other, or is it a SysV feature?

Do you know what happens if you try the link(2) as root?  Typically,
linking to directories has been restricted to being root.

I believe that the ability to link to directories was required before
the mkdir(2) system call was introduced.  Directories were created by
doing a mknod(2) followed by two link(2) operations to create the "."
and ".." entries.  The functionality was not removed or disabled when
mkdir(2) was introduced.

		ps

---

From: Guy Harris <[email-masked]>
Date: Tue, 27 Nov 2001 13:38:35 -0800 (PST)

> Do you know what happens if you try the link(2) as root?

EPERM, at least in FreeBSD 4.3 and, on ext2 file systems, on Linux 2.4.9
kernel systems.  (It probably does so in at least some other releases,
and possibly in other BSDs, as well.)

The code in the FreeBSD 4.3 kernel for "link()" is:

/*
 * Make a hard file link.
 */
#ifndef _SYS_SYSPROTO_H_
struct link_args {
	char	*path;
	char	*link;
};
#endif
/* ARGSUSED */
int
link(p, uap)
	struct proc *p;
	register struct link_args /* {
		syscallarg(char *) path;
		syscallarg(char *) link;
	} */ *uap;
{
	register struct vnode *vp;
	struct nameidata nd;
	int error;

	bwillwrite();
	NDINIT(&nd, LOOKUP, FOLLOW|NOOBJ, UIO_USERSPACE, SCARG(uap, path), p);
	if ((error = namei(&nd)) != 0)
		return (error);
	NDFREE(&nd, NDF_ONLY_PNBUF);
	vp = nd.ni_vp;
	if (vp->v_type == VDIR)
		error = EPERM;		/* POSIX */
	else {
		...

so it doesn't *care* whether you're root or not.

I'm not sure why they say /* POSIX */ there, as my copy of ISO/IEC
9945-1, ANSI/IEEE Std 1003.1, Second edition 1996-07-12, says

	5.3.4 Link to a File

	Function: link()

	5.3.4.1 Synopsis

	int link(const char *existing, const char *new);

	5.3.4.2 Description

		...

	The I<existing> argument shall not name a directory unless the
	user has appropriate privileges and the implementation supports
	using link() on directories.

unless it means that they used to return EISDIR and changed it to return
EPERM as per

	5.3.4.4 Errors

		...

	[EPERM]	The file named by I<existing> is a directory, and either
		the calling process does not have appropriate privileges
		or the implementation prohibits using link() on
		directories.

In the case of FreeBSD, the implementation appears to just prohibit
using "link()" on directories, no matter what privileges the calling
process has.

In the case of the Linux 2.4.9 kernel, the "link()" system call code
doesn't prohibit making hard links to directories - however, the ext2
file system code does:

static int ext2_link (struct dentry * old_dentry, struct inode * dir,
	struct dentry *dentry)
{
	struct inode *inode = old_dentry->d_inode;

	if (S_ISDIR(inode->i_mode))
		return -EPERM;

	...

"nfs_link()", in the NFS client code, doesn't, nor do "nfs2_proc_link()"
or "nfs3_proc_link()", so I suspect Linux will let you make hard links
to directories on an NFS server *if* the server allows it (unlike
FreeBSD, which forbids it at the system call level).

> and ".." entries.

Yes, and it's also needed if you move or rename a directory.

> The functionality was not removed or disabled when mkdir(2) was
> introduced.

Well, not in some OSes.  In others, it appears it *was* eventually
removed (or, in the case of Linux, perhaps it was never implemented in
the first place), as "mkdir()" and "rename()" rendered it unnecessary.