-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
symlink.c
80 lines (70 loc) · 1.9 KB
/
symlink.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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;
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
char *target = NULL;
int err;
int size;
down_read(&nxi->nx_big_sem);
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 */
apfs_err(sb, "symlink size read failed");
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) {
apfs_err(sb, "symlink read failed");
err = size;
goto fail;
}
if (size == 0 || *(target + size - 1) != 0) {
/* Target path must be NULL-terminated */
apfs_err(sb, "bad link target in inode 0x%llx", apfs_ino(inode));
err = -EFSCORRUPTED;
goto fail;
}
up_read(&nxi->nx_big_sem);
set_delayed_call(done, kfree_link, target);
return target;
fail:
kfree(target);
up_read(&nxi->nx_big_sem);
return ERR_PTR(err);
}
const struct inode_operations apfs_symlink_inode_operations = {
.get_link = apfs_get_link,
.getattr = apfs_getattr,
.listxattr = apfs_listxattr,
.update_time = apfs_update_time,
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) /* Now this is the default */
.readlink = generic_readlink,
#endif
};