File: symlink.c

package info (click to toggle)
linux-apfs-rw 0.3.13-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,196 kB
  • sloc: ansic: 20,083; makefile: 24; sh: 6
file content (80 lines) | stat: -rw-r--r-- 1,941 bytes parent folder | download | duplicates (2)
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
};