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 143 144 145 146
|
// 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);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
static int apfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
#else
static int apfs_dentry_revalidate(struct inode *dir, const struct qstr *name, struct dentry *dentry, unsigned int flags)
#endif
{
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,
};
|