namei.c 13.1 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7 8 9 10 11 12
/*
 *  linux/fs/affs/namei.c
 *
 *  (c) 1996  Hans-Joachim Widmaier - Rewritten
 *
 *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
 *
 *  (C) 1991  Linus Torvalds - minix filesystem
 */

#include "affs.h"
13
#include <linux/exportfs.h>
Linus Torvalds's avatar
Linus Torvalds committed
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

typedef int (*toupper_t)(int);

/* Simple toupper() for DOS\1 */

static int
affs_toupper(int ch)
{
	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
}

/* International toupper() for DOS\3 ("international") */

static int
affs_intl_toupper(int ch)
{
	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
		&& ch <= 0xFE && ch != 0xF7) ?
		ch - ('a' - 'A') : ch;
}

static inline toupper_t
affs_get_toupper(struct super_block *sb)
{
38 39
	return affs_test_opt(AFFS_SB(sb)->s_flags, SF_INTL) ?
	       affs_intl_toupper : affs_toupper;
Linus Torvalds's avatar
Linus Torvalds committed
40 41 42 43 44 45
}

/*
 * Note: the dentry argument is the parent dentry.
 */
static inline int
46
__affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr, toupper_t toupper, bool notruncate)
Linus Torvalds's avatar
Linus Torvalds committed
47 48 49
{
	const u8 *name = qstr->name;
	unsigned long hash;
50 51
	int retval;
	u32 len;
Linus Torvalds's avatar
Linus Torvalds committed
52

53 54 55
	retval = affs_check_name(qstr->name, qstr->len, notruncate);
	if (retval)
		return retval;
Linus Torvalds's avatar
Linus Torvalds committed
56

57
	hash = init_name_hash(dentry);
58
	len = min(qstr->len, AFFSNAMEMAX);
59
	for (; len > 0; name++, len--)
Linus Torvalds's avatar
Linus Torvalds committed
60 61 62 63 64 65 66
		hash = partial_name_hash(toupper(*name), hash);
	qstr->hash = end_name_hash(hash);

	return 0;
}

static int
67
affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
Linus Torvalds's avatar
Linus Torvalds committed
68
{
69
	return __affs_hash_dentry(dentry, qstr, affs_toupper,
70 71
				  affs_nofilenametruncate(dentry));

Linus Torvalds's avatar
Linus Torvalds committed
72
}
73

Linus Torvalds's avatar
Linus Torvalds committed
74
static int
75
affs_intl_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
Linus Torvalds's avatar
Linus Torvalds committed
76
{
77
	return __affs_hash_dentry(dentry, qstr, affs_intl_toupper,
78 79
				  affs_nofilenametruncate(dentry));

Linus Torvalds's avatar
Linus Torvalds committed
80 81
}

82
static inline int __affs_compare_dentry(unsigned int len,
83 84
		const char *str, const struct qstr *name, toupper_t toupper,
		bool notruncate)
Linus Torvalds's avatar
Linus Torvalds committed
85
{
86 87
	const u8 *aname = str;
	const u8 *bname = name->name;
Linus Torvalds's avatar
Linus Torvalds committed
88

89 90 91
	/*
	 * 'str' is the name of an already existing dentry, so the name
	 * must be valid. 'name' must be validated first.
Linus Torvalds's avatar
Linus Torvalds committed
92 93
	 */

94
	if (affs_check_name(name->name, name->len, notruncate))
Linus Torvalds's avatar
Linus Torvalds committed
95 96
		return 1;

97 98
	/*
	 * If the names are longer than the allowed 30 chars,
Linus Torvalds's avatar
Linus Torvalds committed
99 100
	 * the excess is ignored, so their length may differ.
	 */
101 102
	if (len >= AFFSNAMEMAX) {
		if (name->len < AFFSNAMEMAX)
Linus Torvalds's avatar
Linus Torvalds committed
103
			return 1;
104
		len = AFFSNAMEMAX;
105
	} else if (len != name->len)
Linus Torvalds's avatar
Linus Torvalds committed
106 107 108 109 110 111 112 113 114 115
		return 1;

	for (; len > 0; len--)
		if (toupper(*aname++) != toupper(*bname++))
			return 1;

	return 0;
}

static int
116
affs_compare_dentry(const struct dentry *dentry,
117
		unsigned int len, const char *str, const struct qstr *name)
Linus Torvalds's avatar
Linus Torvalds committed
118
{
119 120

	return __affs_compare_dentry(len, str, name, affs_toupper,
121
				     affs_nofilenametruncate(dentry));
Linus Torvalds's avatar
Linus Torvalds committed
122
}
123

Linus Torvalds's avatar
Linus Torvalds committed
124
static int
125
affs_intl_compare_dentry(const struct dentry *dentry,
126
		unsigned int len, const char *str, const struct qstr *name)
Linus Torvalds's avatar
Linus Torvalds committed
127
{
128
	return __affs_compare_dentry(len, str, name, affs_intl_toupper,
129
				     affs_nofilenametruncate(dentry));
130

Linus Torvalds's avatar
Linus Torvalds committed
131 132 133 134 135 136 137 138 139 140 141 142
}

/*
 * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
 */

static inline int
affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
{
	const u8 *name = dentry->d_name.name;
	int len = dentry->d_name.len;

143 144
	if (len >= AFFSNAMEMAX) {
		if (*name2 < AFFSNAMEMAX)
Linus Torvalds's avatar
Linus Torvalds committed
145
			return 0;
146
		len = AFFSNAMEMAX;
Linus Torvalds's avatar
Linus Torvalds committed
147 148 149 150 151 152 153 154 155 156 157 158 159
	} else if (len != *name2)
		return 0;

	for (name2++; len > 0; len--)
		if (toupper(*name++) != toupper(*name2++))
			return 0;
	return 1;
}

int
affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
{
	toupper_t toupper = affs_get_toupper(sb);
160
	u32 hash;
Linus Torvalds's avatar
Linus Torvalds committed
161

162
	hash = len = min(len, AFFSNAMEMAX);
Linus Torvalds's avatar
Linus Torvalds committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176
	for (; len > 0; len--)
		hash = (hash * 13 + toupper(*name++)) & 0x7ff;

	return hash % AFFS_SB(sb)->s_hashsize;
}

static struct buffer_head *
affs_find_entry(struct inode *dir, struct dentry *dentry)
{
	struct super_block *sb = dir->i_sb;
	struct buffer_head *bh;
	toupper_t toupper = affs_get_toupper(sb);
	u32 key;

Al Viro's avatar
Al Viro committed
177
	pr_debug("%s(\"%pd\")\n", __func__, dentry);
Linus Torvalds's avatar
Linus Torvalds committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

	bh = affs_bread(sb, dir->i_ino);
	if (!bh)
		return ERR_PTR(-EIO);

	key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);

	for (;;) {
		affs_brelse(bh);
		if (key == 0)
			return NULL;
		bh = affs_bread(sb, key);
		if (!bh)
			return ERR_PTR(-EIO);
		if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
			return bh;
		key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
	}
}

struct dentry *
199
affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
Linus Torvalds's avatar
Linus Torvalds committed
200 201 202 203
{
	struct super_block *sb = dir->i_sb;
	struct buffer_head *bh;
	struct inode *inode = NULL;
204
	struct dentry *res;
Linus Torvalds's avatar
Linus Torvalds committed
205

Al Viro's avatar
Al Viro committed
206
	pr_debug("%s(\"%pd\")\n", __func__, dentry);
Linus Torvalds's avatar
Linus Torvalds committed
207 208 209

	affs_lock_dir(dir);
	bh = affs_find_entry(dir, dentry);
210 211
	if (IS_ERR(bh)) {
		affs_unlock_dir(dir);
212
		return ERR_CAST(bh);
213
	}
Linus Torvalds's avatar
Linus Torvalds committed
214 215 216 217 218 219 220 221 222 223 224 225
	if (bh) {
		u32 ino = bh->b_blocknr;

		/* store the real header ino in d_fsdata for faster lookups */
		dentry->d_fsdata = (void *)(long)ino;
		switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
		//link to dirs disabled
		//case ST_LINKDIR:
		case ST_LINKFILE:
			ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
		}
		affs_brelse(bh);
226
		inode = affs_iget(sb, ino);
Linus Torvalds's avatar
Linus Torvalds committed
227
	}
228 229 230
	res = d_splice_alias(inode, dentry);
	if (!IS_ERR_OR_NULL(res))
		res->d_fsdata = dentry->d_fsdata;
231
	affs_unlock_dir(dir);
232
	return res;
Linus Torvalds's avatar
Linus Torvalds committed
233 234 235 236 237
}

int
affs_unlink(struct inode *dir, struct dentry *dentry)
{
238
	pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino,
239
		 d_inode(dentry)->i_ino, dentry);
Linus Torvalds's avatar
Linus Torvalds committed
240 241 242 243 244

	return affs_remove_header(dentry);
}

int
Al Viro's avatar
Al Viro committed
245
affs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
Linus Torvalds's avatar
Linus Torvalds committed
246 247 248 249 250
{
	struct super_block *sb = dir->i_sb;
	struct inode	*inode;
	int		 error;

Al Viro's avatar
Al Viro committed
251 252
	pr_debug("%s(%lu,\"%pd\",0%ho)\n",
		 __func__, dir->i_ino, dentry, mode);
Linus Torvalds's avatar
Linus Torvalds committed
253 254 255 256 257 258

	inode = affs_new_inode(dir);
	if (!inode)
		return -ENOSPC;

	inode->i_mode = mode;
259
	affs_mode_to_prot(inode);
Linus Torvalds's avatar
Linus Torvalds committed
260 261 262 263
	mark_inode_dirty(inode);

	inode->i_op = &affs_file_inode_operations;
	inode->i_fop = &affs_file_operations;
264
	inode->i_mapping->a_ops = affs_test_opt(AFFS_SB(sb)->s_flags, SF_OFS) ?
265
				  &affs_aops_ofs : &affs_aops;
Linus Torvalds's avatar
Linus Torvalds committed
266 267
	error = affs_add_entry(dir, inode, dentry, ST_FILE);
	if (error) {
268
		clear_nlink(inode);
Linus Torvalds's avatar
Linus Torvalds committed
269 270 271 272 273 274 275
		iput(inode);
		return error;
	}
	return 0;
}

int
276
affs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
Linus Torvalds's avatar
Linus Torvalds committed
277 278 279 280
{
	struct inode		*inode;
	int			 error;

Al Viro's avatar
Al Viro committed
281 282
	pr_debug("%s(%lu,\"%pd\",0%ho)\n",
		 __func__, dir->i_ino, dentry, mode);
Linus Torvalds's avatar
Linus Torvalds committed
283 284 285 286 287 288

	inode = affs_new_inode(dir);
	if (!inode)
		return -ENOSPC;

	inode->i_mode = S_IFDIR | mode;
289
	affs_mode_to_prot(inode);
Linus Torvalds's avatar
Linus Torvalds committed
290 291 292 293 294 295

	inode->i_op = &affs_dir_inode_operations;
	inode->i_fop = &affs_dir_operations;

	error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
	if (error) {
296
		clear_nlink(inode);
Linus Torvalds's avatar
Linus Torvalds committed
297 298 299 300 301 302 303 304 305 306
		mark_inode_dirty(inode);
		iput(inode);
		return error;
	}
	return 0;
}

int
affs_rmdir(struct inode *dir, struct dentry *dentry)
{
307
	pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino,
308
		 d_inode(dentry)->i_ino, dentry);
Linus Torvalds's avatar
Linus Torvalds committed
309 310 311 312 313 314 315 316 317 318 319 320 321 322

	return affs_remove_header(dentry);
}

int
affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
	struct super_block	*sb = dir->i_sb;
	struct buffer_head	*bh;
	struct inode		*inode;
	char			*p;
	int			 i, maxlen, error;
	char			 c, lc;

Al Viro's avatar
Al Viro committed
323 324
	pr_debug("%s(%lu,\"%pd\" -> \"%s\")\n",
		 __func__, dir->i_ino, dentry, symname);
Linus Torvalds's avatar
Linus Torvalds committed
325 326 327 328 329 330 331

	maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1;
	inode  = affs_new_inode(dir);
	if (!inode)
		return -ENOSPC;

	inode->i_op = &affs_symlink_inode_operations;
332
	inode_nohighmem(inode);
Linus Torvalds's avatar
Linus Torvalds committed
333 334
	inode->i_data.a_ops = &affs_symlink_aops;
	inode->i_mode = S_IFLNK | 0777;
335
	affs_mode_to_prot(inode);
Linus Torvalds's avatar
Linus Torvalds committed
336 337 338 339 340 341 342 343 344

	error = -EIO;
	bh = affs_bread(sb, inode->i_ino);
	if (!bh)
		goto err;
	i  = 0;
	p  = (char *)AFFS_HEAD(bh)->table;
	lc = '/';
	if (*symname == '/') {
345
		struct affs_sb_info *sbi = AFFS_SB(sb);
Linus Torvalds's avatar
Linus Torvalds committed
346 347
		while (*symname == '/')
			symname++;
348 349 350 351
		spin_lock(&sbi->symlink_lock);
		while (sbi->s_volume[i])	/* Cannot overflow */
			*p++ = sbi->s_volume[i++];
		spin_unlock(&sbi->symlink_lock);
Linus Torvalds's avatar
Linus Torvalds committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	}
	while (i < maxlen && (c = *symname++)) {
		if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
			*p++ = '/';
			i++;
			symname += 2;
			lc = '/';
		} else if (c == '.' && lc == '/' && *symname == '/') {
			symname++;
			lc = '/';
		} else {
			*p++ = c;
			lc   = c;
			i++;
		}
		if (lc == '/')
			while (*symname == '/')
				symname++;
	}
	*p = 0;
372
	inode->i_size = i + 1;
Linus Torvalds's avatar
Linus Torvalds committed
373 374 375 376 377 378 379 380 381 382 383
	mark_buffer_dirty_inode(bh, inode);
	affs_brelse(bh);
	mark_inode_dirty(inode);

	error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
	if (error)
		goto err;

	return 0;

err:
384
	clear_nlink(inode);
Linus Torvalds's avatar
Linus Torvalds committed
385 386 387 388 389 390 391 392
	mark_inode_dirty(inode);
	iput(inode);
	return error;
}

int
affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
393
	struct inode *inode = d_inode(old_dentry);
Linus Torvalds's avatar
Linus Torvalds committed
394

395
	pr_debug("%s(%lu, %lu, \"%pd\")\n", __func__, inode->i_ino, dir->i_ino,
Al Viro's avatar
Al Viro committed
396
		 dentry);
Linus Torvalds's avatar
Linus Torvalds committed
397 398 399 400

	return affs_add_entry(dir, inode, dentry, ST_LINKFILE);
}

401
static int
Linus Torvalds's avatar
Linus Torvalds committed
402
affs_rename(struct inode *old_dir, struct dentry *old_dentry,
403
	    struct inode *new_dir, struct dentry *new_dentry)
Linus Torvalds's avatar
Linus Torvalds committed
404 405 406 407 408
{
	struct super_block *sb = old_dir->i_sb;
	struct buffer_head *bh = NULL;
	int retval;

409 410 411 412
	retval = affs_check_name(new_dentry->d_name.name,
				 new_dentry->d_name.len,
				 affs_nofilenametruncate(old_dentry));

Linus Torvalds's avatar
Linus Torvalds committed
413 414 415 416
	if (retval)
		return retval;

	/* Unlink destination if it already exists */
417
	if (d_really_is_positive(new_dentry)) {
Linus Torvalds's avatar
Linus Torvalds committed
418 419 420 421 422
		retval = affs_remove_header(new_dentry);
		if (retval)
			return retval;
	}

423
	bh = affs_bread(sb, d_inode(old_dentry)->i_ino);
Linus Torvalds's avatar
Linus Torvalds committed
424
	if (!bh)
425
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446

	/* Remove header from its parent directory. */
	affs_lock_dir(old_dir);
	retval = affs_remove_hash(old_dir, bh);
	affs_unlock_dir(old_dir);
	if (retval)
		goto done;

	/* And insert it into the new directory with the new name. */
	affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
	affs_fix_checksum(sb, bh);
	affs_lock_dir(new_dir);
	retval = affs_insert_hash(new_dir, bh);
	affs_unlock_dir(new_dir);
	/* TODO: move it back to old_dir, if error? */

done:
	mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
	affs_brelse(bh);
	return retval;
}
447

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
static int
affs_xrename(struct inode *old_dir, struct dentry *old_dentry,
	     struct inode *new_dir, struct dentry *new_dentry)
{

	struct super_block *sb = old_dir->i_sb;
	struct buffer_head *bh_old = NULL;
	struct buffer_head *bh_new = NULL;
	int retval;

	bh_old = affs_bread(sb, d_inode(old_dentry)->i_ino);
	if (!bh_old)
		return -EIO;

	bh_new = affs_bread(sb, d_inode(new_dentry)->i_ino);
	if (!bh_new)
		return -EIO;

	/* Remove old header from its parent directory. */
	affs_lock_dir(old_dir);
	retval = affs_remove_hash(old_dir, bh_old);
	affs_unlock_dir(old_dir);
	if (retval)
		goto done;

	/* Remove new header from its parent directory. */
	affs_lock_dir(new_dir);
	retval = affs_remove_hash(new_dir, bh_new);
	affs_unlock_dir(new_dir);
	if (retval)
		goto done;

	/* Insert old into the new directory with the new name. */
	affs_copy_name(AFFS_TAIL(sb, bh_old)->name, new_dentry);
	affs_fix_checksum(sb, bh_old);
	affs_lock_dir(new_dir);
	retval = affs_insert_hash(new_dir, bh_old);
	affs_unlock_dir(new_dir);

	/* Insert new into the old directory with the old name. */
	affs_copy_name(AFFS_TAIL(sb, bh_new)->name, old_dentry);
	affs_fix_checksum(sb, bh_new);
	affs_lock_dir(old_dir);
	retval = affs_insert_hash(old_dir, bh_new);
	affs_unlock_dir(old_dir);
done:
	mark_buffer_dirty_inode(bh_old, new_dir);
	mark_buffer_dirty_inode(bh_new, old_dir);
	affs_brelse(bh_old);
	affs_brelse(bh_new);
	return retval;
}

501 502 503 504 505
int affs_rename2(struct inode *old_dir, struct dentry *old_dentry,
			struct inode *new_dir, struct dentry *new_dentry,
			unsigned int flags)
{

506
	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
507 508 509 510 511
		return -EINVAL;

	pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__,
		 old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry);

512 513 514
	if (flags & RENAME_EXCHANGE)
		return affs_xrename(old_dir, old_dentry, new_dir, new_dentry);

515 516 517
	return affs_rename(old_dir, old_dentry, new_dir, new_dentry);
}

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
static struct dentry *affs_get_parent(struct dentry *child)
{
	struct inode *parent;
	struct buffer_head *bh;

	bh = affs_bread(child->d_sb, d_inode(child)->i_ino);
	if (!bh)
		return ERR_PTR(-EIO);

	parent = affs_iget(child->d_sb,
			   be32_to_cpu(AFFS_TAIL(child->d_sb, bh)->parent));
	brelse(bh);
	if (IS_ERR(parent))
		return ERR_CAST(parent);

	return d_obtain_alias(parent);
}

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
static struct inode *affs_nfs_get_inode(struct super_block *sb, u64 ino,
					u32 generation)
{
	struct inode *inode;

	if (!affs_validblock(sb, ino))
		return ERR_PTR(-ESTALE);

	inode = affs_iget(sb, ino);
	if (IS_ERR(inode))
		return ERR_CAST(inode);

	return inode;
}

static struct dentry *affs_fh_to_dentry(struct super_block *sb, struct fid *fid,
					int fh_len, int fh_type)
{
	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
				    affs_nfs_get_inode);
}

static struct dentry *affs_fh_to_parent(struct super_block *sb, struct fid *fid,
					int fh_len, int fh_type)
{
	return generic_fh_to_parent(sb, fid, fh_len, fh_type,
				    affs_nfs_get_inode);
}

const struct export_operations affs_export_ops = {
	.fh_to_dentry = affs_fh_to_dentry,
	.fh_to_parent = affs_fh_to_parent,
568
	.get_parent = affs_get_parent,
569
};
570 571 572 573 574 575 576 577 578 579

const struct dentry_operations affs_dentry_operations = {
	.d_hash		= affs_hash_dentry,
	.d_compare	= affs_compare_dentry,
};

const struct dentry_operations affs_intl_dentry_operations = {
	.d_hash		= affs_intl_hash_dentry,
	.d_compare	= affs_intl_compare_dentry,
};