#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/grinternal.h>

int
gr_handle_follow_link(const struct inode *parent,
		      const struct inode *inode,
		      const struct dentry *dentry, const struct vfsmount *mnt)
{
#ifdef CONFIG_GRKERNSEC_LINK
	if (grsec_enable_link && S_ISLNK(inode->i_mode) &&
	    (parent->i_mode & S_ISVTX) && (parent->i_uid != inode->i_uid) &&
	    (parent->i_mode & S_IWOTH) && (current->fsuid != inode->i_uid)) {
		security_alert(GR_SYMLINK_MSG,
			       gr_to_filename(dentry, mnt),
			       inode->i_uid, inode->i_gid, DEFAULTSECARGS);
		return -EACCES;
	}
#endif
	return 0;
}

int
gr_handle_hardlink(const struct dentry *dentry,
		   const struct vfsmount *mnt,
		   struct inode *inode, const int mode, const char *to)
{
#ifdef CONFIG_GRKERNSEC_LINK
	if (grsec_enable_link && current->fsuid != inode->i_uid &&
	    (!S_ISREG(mode) || (mode & S_ISUID) ||
	     ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) ||
	     (permission(inode, MAY_READ | MAY_WRITE))) &&
	    !capable(CAP_FOWNER) && current->uid) {
		security_alert(GR_HARDLINK_MSG, gr_to_filename(dentry, mnt),
			       inode->i_uid, inode->i_gid, to, DEFAULTSECARGS);
		return -EPERM;
	}
#endif
	return 0;
}
