2021-11-16 18:36:02 -03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2021-03-31 17:16:24 -03:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2018 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
|
#include "apfs.h"
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_get_link - Follow a symbolic link
|
|
|
|
|
* @dentry: dentry for the link
|
|
|
|
|
* @inode: inode for the link
|
|
|
|
|
* @done: delayed call to free the returned buffer after use
|
|
|
|
|
*
|
|
|
|
|
* Returns a pointer to a buffer containing the target path, or an appropriate
|
|
|
|
|
* error pointer in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
static const char *apfs_get_link(struct dentry *dentry, struct inode *inode,
|
|
|
|
|
struct delayed_call *done)
|
|
|
|
|
{
|
|
|
|
|
struct super_block *sb = inode->i_sb;
|
2021-04-01 22:45:25 -03:00
|
|
|
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
2021-03-31 17:16:24 -03:00
|
|
|
char *target = NULL;
|
|
|
|
|
int err;
|
|
|
|
|
int size;
|
|
|
|
|
|
2021-04-01 22:45:25 -03:00
|
|
|
down_read(&nxi->nx_big_sem);
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
if (!dentry) {
|
|
|
|
|
err = -ECHILD;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size = __apfs_xattr_get(inode, APFS_XATTR_NAME_SYMLINK,
|
|
|
|
|
NULL /* buffer */, 0 /* size */);
|
|
|
|
|
if (size < 0) { /* TODO: return a better error code */
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(sb, "symlink size read failed");
|
2021-03-31 17:16:24 -03:00
|
|
|
err = size;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
target = kmalloc(size, GFP_KERNEL);
|
|
|
|
|
if (!target) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size = __apfs_xattr_get(inode, APFS_XATTR_NAME_SYMLINK, target, size);
|
|
|
|
|
if (size < 0) {
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(sb, "symlink read failed");
|
2021-03-31 17:16:24 -03:00
|
|
|
err = size;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
if (size == 0 || *(target + size - 1) != 0) {
|
|
|
|
|
/* Target path must be NULL-terminated */
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(sb, "bad link target in inode 0x%llx", apfs_ino(inode));
|
2021-03-31 17:16:24 -03:00
|
|
|
err = -EFSCORRUPTED;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 22:45:25 -03:00
|
|
|
up_read(&nxi->nx_big_sem);
|
2021-03-31 17:16:24 -03:00
|
|
|
set_delayed_call(done, kfree_link, target);
|
|
|
|
|
return target;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
kfree(target);
|
2021-04-01 22:45:25 -03:00
|
|
|
up_read(&nxi->nx_big_sem);
|
2021-03-31 17:16:24 -03:00
|
|
|
return ERR_PTR(err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const struct inode_operations apfs_symlink_inode_operations = {
|
|
|
|
|
.get_link = apfs_get_link,
|
|
|
|
|
.getattr = apfs_getattr,
|
|
|
|
|
.listxattr = apfs_listxattr,
|
2021-07-21 17:02:18 -03:00
|
|
|
.update_time = apfs_update_time,
|
2021-07-30 17:04:16 -03:00
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) /* Now this is the default */
|
|
|
|
|
.readlink = generic_readlink,
|
|
|
|
|
#endif
|
2021-03-31 17:16:24 -03:00
|
|
|
};
|