-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
namei.c
142 lines (121 loc) · 3.7 KB
/
namei.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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
*/
#include <linux/namei.h>
#include "apfs.h"
#include "unicode.h"
static struct dentry *apfs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
struct inode *inode = NULL;
u64 ino = 0;
int err;
if (dentry->d_name.len > APFS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
err = apfs_inode_by_name(dir, &dentry->d_name, &ino);
if (err && err != -ENODATA) {
apfs_err(dir->i_sb, "inode lookup by name failed");
return ERR_PTR(err);
}
if (!err) {
inode = apfs_iget(dir->i_sb, ino);
if (IS_ERR(inode)) {
apfs_err(dir->i_sb, "iget failed");
return ERR_CAST(inode);
}
}
return d_splice_alias(inode, dentry);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
static int apfs_symlink(struct inode *dir, struct dentry *dentry,
const char *symname)
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
static int apfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, const char *symname)
#else
static int apfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const char *symname)
#endif
{
/* Symlink permissions don't mean anything and their value is fixed */
return apfs_mkany(dir, dentry, S_IFLNK | 0x1ed, 0 /* rdev */, symname);
}
const struct inode_operations apfs_dir_inode_operations = {
.create = apfs_create,
.lookup = apfs_lookup,
.link = apfs_link,
.unlink = apfs_unlink,
.symlink = apfs_symlink,
.mkdir = apfs_mkdir,
.rmdir = apfs_rmdir,
.mknod = apfs_mknod,
.rename = apfs_rename,
.getattr = apfs_getattr,
.listxattr = apfs_listxattr,
.setattr = apfs_setattr,
.update_time = apfs_update_time,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
.fileattr_get = apfs_fileattr_get,
.fileattr_set = apfs_fileattr_set,
#endif
};
const struct inode_operations apfs_special_inode_operations = {
.getattr = apfs_getattr,
.listxattr = apfs_listxattr,
.setattr = apfs_setattr,
.update_time = apfs_update_time,
};
static int apfs_dentry_hash(const struct dentry *dir, struct qstr *child)
{
struct apfs_unicursor cursor;
unsigned long hash;
bool case_fold = apfs_is_case_insensitive(dir->d_sb);
if (!apfs_is_normalization_insensitive(dir->d_sb))
return 0;
apfs_init_unicursor(&cursor, child->name, child->len);
hash = init_name_hash(dir);
while (1) {
int i;
unicode_t utf32;
utf32 = apfs_normalize_next(&cursor, case_fold);
if (!utf32)
break;
/* Hash the unicode character one byte at a time */
for (i = 0; i < 4; ++i) {
hash = partial_name_hash((u8)utf32, hash);
utf32 = utf32 >> 8;
}
}
child->hash = end_name_hash(hash);
/* TODO: return error instead of truncating invalid UTF-8? */
return 0;
}
static int apfs_dentry_compare(const struct dentry *dentry, unsigned int len,
const char *str, const struct qstr *name)
{
return apfs_filename_cmp(dentry->d_sb, name->name, name->len, str, len);
}
static int apfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
{
struct super_block *sb = dentry->d_sb;
if (flags & LOOKUP_RCU)
return -ECHILD;
/*
* If we want to create a link with a name that normalizes to the same
* as an existing negative dentry, then we first need to invalidate the
* dentry; otherwise it would keep the existing name.
*/
if (d_really_is_positive(dentry))
return 1;
if (!apfs_is_case_insensitive(sb) && !apfs_is_normalization_insensitive(sb))
return 1;
if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
return 0;
return 1;
}
const struct dentry_operations apfs_dentry_operations = {
.d_revalidate = apfs_dentry_revalidate,
.d_hash = apfs_dentry_hash,
.d_compare = apfs_dentry_compare,
};