cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

catalog.c (14470B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *  linux/fs/hfsplus/catalog.c
      4 *
      5 * Copyright (C) 2001
      6 * Brad Boyer (flar@allandria.com)
      7 * (C) 2003 Ardis Technologies <roman@ardistech.com>
      8 *
      9 * Handling of catalog records
     10 */
     11
     12
     13#include "hfsplus_fs.h"
     14#include "hfsplus_raw.h"
     15
     16int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
     17			     const hfsplus_btree_key *k2)
     18{
     19	__be32 k1p, k2p;
     20
     21	k1p = k1->cat.parent;
     22	k2p = k2->cat.parent;
     23	if (k1p != k2p)
     24		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
     25
     26	return hfsplus_strcasecmp(&k1->cat.name, &k2->cat.name);
     27}
     28
     29int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
     30			    const hfsplus_btree_key *k2)
     31{
     32	__be32 k1p, k2p;
     33
     34	k1p = k1->cat.parent;
     35	k2p = k2->cat.parent;
     36	if (k1p != k2p)
     37		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
     38
     39	return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
     40}
     41
     42/* Generates key for catalog file/folders record. */
     43int hfsplus_cat_build_key(struct super_block *sb,
     44		hfsplus_btree_key *key, u32 parent, const struct qstr *str)
     45{
     46	int len, err;
     47
     48	key->cat.parent = cpu_to_be32(parent);
     49	err = hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN,
     50			str->name, str->len);
     51	if (unlikely(err < 0))
     52		return err;
     53
     54	len = be16_to_cpu(key->cat.name.length);
     55	key->key_len = cpu_to_be16(6 + 2 * len);
     56	return 0;
     57}
     58
     59/* Generates key for catalog thread record. */
     60void hfsplus_cat_build_key_with_cnid(struct super_block *sb,
     61			hfsplus_btree_key *key, u32 parent)
     62{
     63	key->cat.parent = cpu_to_be32(parent);
     64	key->cat.name.length = 0;
     65	key->key_len = cpu_to_be16(6);
     66}
     67
     68static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent,
     69				      struct hfsplus_unistr *name)
     70{
     71	int ustrlen;
     72
     73	ustrlen = be16_to_cpu(name->length);
     74	key->cat.parent = cpu_to_be32(parent);
     75	key->cat.name.length = cpu_to_be16(ustrlen);
     76	ustrlen *= 2;
     77	memcpy(key->cat.name.unicode, name->unicode, ustrlen);
     78	key->key_len = cpu_to_be16(6 + ustrlen);
     79}
     80
     81void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms)
     82{
     83	if (inode->i_flags & S_IMMUTABLE)
     84		perms->rootflags |= HFSPLUS_FLG_IMMUTABLE;
     85	else
     86		perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
     87	if (inode->i_flags & S_APPEND)
     88		perms->rootflags |= HFSPLUS_FLG_APPEND;
     89	else
     90		perms->rootflags &= ~HFSPLUS_FLG_APPEND;
     91
     92	perms->userflags = HFSPLUS_I(inode)->userflags;
     93	perms->mode = cpu_to_be16(inode->i_mode);
     94	perms->owner = cpu_to_be32(i_uid_read(inode));
     95	perms->group = cpu_to_be32(i_gid_read(inode));
     96
     97	if (S_ISREG(inode->i_mode))
     98		perms->dev = cpu_to_be32(inode->i_nlink);
     99	else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode))
    100		perms->dev = cpu_to_be32(inode->i_rdev);
    101	else
    102		perms->dev = 0;
    103}
    104
    105static int hfsplus_cat_build_record(hfsplus_cat_entry *entry,
    106		u32 cnid, struct inode *inode)
    107{
    108	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
    109
    110	if (S_ISDIR(inode->i_mode)) {
    111		struct hfsplus_cat_folder *folder;
    112
    113		folder = &entry->folder;
    114		memset(folder, 0, sizeof(*folder));
    115		folder->type = cpu_to_be16(HFSPLUS_FOLDER);
    116		if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags))
    117			folder->flags |= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT);
    118		folder->id = cpu_to_be32(inode->i_ino);
    119		HFSPLUS_I(inode)->create_date =
    120			folder->create_date =
    121			folder->content_mod_date =
    122			folder->attribute_mod_date =
    123			folder->access_date = hfsp_now2mt();
    124		hfsplus_cat_set_perms(inode, &folder->permissions);
    125		if (inode == sbi->hidden_dir)
    126			/* invisible and namelocked */
    127			folder->user_info.frFlags = cpu_to_be16(0x5000);
    128		return sizeof(*folder);
    129	} else {
    130		struct hfsplus_cat_file *file;
    131
    132		file = &entry->file;
    133		memset(file, 0, sizeof(*file));
    134		file->type = cpu_to_be16(HFSPLUS_FILE);
    135		file->flags = cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS);
    136		file->id = cpu_to_be32(cnid);
    137		HFSPLUS_I(inode)->create_date =
    138			file->create_date =
    139			file->content_mod_date =
    140			file->attribute_mod_date =
    141			file->access_date = hfsp_now2mt();
    142		if (cnid == inode->i_ino) {
    143			hfsplus_cat_set_perms(inode, &file->permissions);
    144			if (S_ISLNK(inode->i_mode)) {
    145				file->user_info.fdType =
    146					cpu_to_be32(HFSP_SYMLINK_TYPE);
    147				file->user_info.fdCreator =
    148					cpu_to_be32(HFSP_SYMLINK_CREATOR);
    149			} else {
    150				file->user_info.fdType =
    151					cpu_to_be32(sbi->type);
    152				file->user_info.fdCreator =
    153					cpu_to_be32(sbi->creator);
    154			}
    155			if (HFSPLUS_FLG_IMMUTABLE &
    156					(file->permissions.rootflags |
    157					file->permissions.userflags))
    158				file->flags |=
    159					cpu_to_be16(HFSPLUS_FILE_LOCKED);
    160		} else {
    161			file->user_info.fdType =
    162				cpu_to_be32(HFSP_HARDLINK_TYPE);
    163			file->user_info.fdCreator =
    164				cpu_to_be32(HFSP_HFSPLUS_CREATOR);
    165			file->user_info.fdFlags =
    166				cpu_to_be16(0x100);
    167			file->create_date =
    168				HFSPLUS_I(sbi->hidden_dir)->create_date;
    169			file->permissions.dev =
    170				cpu_to_be32(HFSPLUS_I(inode)->linkid);
    171		}
    172		return sizeof(*file);
    173	}
    174}
    175
    176static int hfsplus_fill_cat_thread(struct super_block *sb,
    177				   hfsplus_cat_entry *entry, int type,
    178				   u32 parentid, const struct qstr *str)
    179{
    180	int err;
    181
    182	entry->type = cpu_to_be16(type);
    183	entry->thread.reserved = 0;
    184	entry->thread.parentID = cpu_to_be32(parentid);
    185	err = hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN,
    186				str->name, str->len);
    187	if (unlikely(err < 0))
    188		return err;
    189
    190	return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
    191}
    192
    193/* Try to get a catalog entry for given catalog id */
    194int hfsplus_find_cat(struct super_block *sb, u32 cnid,
    195		     struct hfs_find_data *fd)
    196{
    197	hfsplus_cat_entry tmp;
    198	int err;
    199	u16 type;
    200
    201	hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid);
    202	err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
    203	if (err)
    204		return err;
    205
    206	type = be16_to_cpu(tmp.type);
    207	if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
    208		pr_err("found bad thread record in catalog\n");
    209		return -EIO;
    210	}
    211
    212	if (be16_to_cpu(tmp.thread.nodeName.length) > 255) {
    213		pr_err("catalog name length corrupted\n");
    214		return -EIO;
    215	}
    216
    217	hfsplus_cat_build_key_uni(fd->search_key,
    218		be32_to_cpu(tmp.thread.parentID),
    219		&tmp.thread.nodeName);
    220	return hfs_brec_find(fd, hfs_find_rec_by_key);
    221}
    222
    223static void hfsplus_subfolders_inc(struct inode *dir)
    224{
    225	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
    226
    227	if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
    228		/*
    229		 * Increment subfolder count. Note, the value is only meaningful
    230		 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
    231		 */
    232		HFSPLUS_I(dir)->subfolders++;
    233	}
    234}
    235
    236static void hfsplus_subfolders_dec(struct inode *dir)
    237{
    238	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
    239
    240	if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
    241		/*
    242		 * Decrement subfolder count. Note, the value is only meaningful
    243		 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
    244		 *
    245		 * Check for zero. Some subfolders may have been created
    246		 * by an implementation ignorant of this counter.
    247		 */
    248		if (HFSPLUS_I(dir)->subfolders)
    249			HFSPLUS_I(dir)->subfolders--;
    250	}
    251}
    252
    253int hfsplus_create_cat(u32 cnid, struct inode *dir,
    254		const struct qstr *str, struct inode *inode)
    255{
    256	struct super_block *sb = dir->i_sb;
    257	struct hfs_find_data fd;
    258	hfsplus_cat_entry entry;
    259	int entry_size;
    260	int err;
    261
    262	hfs_dbg(CAT_MOD, "create_cat: %s,%u(%d)\n",
    263		str->name, cnid, inode->i_nlink);
    264	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
    265	if (err)
    266		return err;
    267
    268	/*
    269	 * Fail early and avoid ENOSPC during the btree operations. We may
    270	 * have to split the root node at most once.
    271	 */
    272	err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
    273	if (err)
    274		goto err2;
    275
    276	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
    277	entry_size = hfsplus_fill_cat_thread(sb, &entry,
    278		S_ISDIR(inode->i_mode) ?
    279			HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
    280		dir->i_ino, str);
    281	if (unlikely(entry_size < 0)) {
    282		err = entry_size;
    283		goto err2;
    284	}
    285
    286	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
    287	if (err != -ENOENT) {
    288		if (!err)
    289			err = -EEXIST;
    290		goto err2;
    291	}
    292	err = hfs_brec_insert(&fd, &entry, entry_size);
    293	if (err)
    294		goto err2;
    295
    296	err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
    297	if (unlikely(err))
    298		goto err1;
    299
    300	entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
    301	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
    302	if (err != -ENOENT) {
    303		/* panic? */
    304		if (!err)
    305			err = -EEXIST;
    306		goto err1;
    307	}
    308	err = hfs_brec_insert(&fd, &entry, entry_size);
    309	if (err)
    310		goto err1;
    311
    312	dir->i_size++;
    313	if (S_ISDIR(inode->i_mode))
    314		hfsplus_subfolders_inc(dir);
    315	dir->i_mtime = dir->i_ctime = current_time(dir);
    316	hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
    317
    318	hfs_find_exit(&fd);
    319	return 0;
    320
    321err1:
    322	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
    323	if (!hfs_brec_find(&fd, hfs_find_rec_by_key))
    324		hfs_brec_remove(&fd);
    325err2:
    326	hfs_find_exit(&fd);
    327	return err;
    328}
    329
    330int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
    331{
    332	struct super_block *sb = dir->i_sb;
    333	struct hfs_find_data fd;
    334	struct hfsplus_fork_raw fork;
    335	struct list_head *pos;
    336	int err, off;
    337	u16 type;
    338
    339	hfs_dbg(CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
    340	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
    341	if (err)
    342		return err;
    343
    344	/*
    345	 * Fail early and avoid ENOSPC during the btree operations. We may
    346	 * have to split the root node at most once.
    347	 */
    348	err = hfs_bmap_reserve(fd.tree, 2 * (int)fd.tree->depth - 2);
    349	if (err)
    350		goto out;
    351
    352	if (!str) {
    353		int len;
    354
    355		hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
    356		err = hfs_brec_find(&fd, hfs_find_rec_by_key);
    357		if (err)
    358			goto out;
    359
    360		off = fd.entryoffset +
    361			offsetof(struct hfsplus_cat_thread, nodeName);
    362		fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
    363		hfs_bnode_read(fd.bnode,
    364			&fd.search_key->cat.name.length, off, 2);
    365		len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
    366		hfs_bnode_read(fd.bnode,
    367			&fd.search_key->cat.name.unicode,
    368			off + 2, len);
    369		fd.search_key->key_len = cpu_to_be16(6 + len);
    370	} else {
    371		err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
    372		if (unlikely(err))
    373			goto out;
    374	}
    375
    376	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
    377	if (err)
    378		goto out;
    379
    380	type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
    381	if (type == HFSPLUS_FILE) {
    382#if 0
    383		off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
    384		hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
    385		hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
    386#endif
    387
    388		off = fd.entryoffset +
    389			offsetof(struct hfsplus_cat_file, rsrc_fork);
    390		hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
    391		hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
    392	}
    393
    394	/* we only need to take spinlock for exclusion with ->release() */
    395	spin_lock(&HFSPLUS_I(dir)->open_dir_lock);
    396	list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
    397		struct hfsplus_readdir_data *rd =
    398			list_entry(pos, struct hfsplus_readdir_data, list);
    399		if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
    400			rd->file->f_pos--;
    401	}
    402	spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
    403
    404	err = hfs_brec_remove(&fd);
    405	if (err)
    406		goto out;
    407
    408	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
    409	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
    410	if (err)
    411		goto out;
    412
    413	err = hfs_brec_remove(&fd);
    414	if (err)
    415		goto out;
    416
    417	dir->i_size--;
    418	if (type == HFSPLUS_FOLDER)
    419		hfsplus_subfolders_dec(dir);
    420	dir->i_mtime = dir->i_ctime = current_time(dir);
    421	hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
    422
    423	if (type == HFSPLUS_FILE || type == HFSPLUS_FOLDER) {
    424		if (HFSPLUS_SB(sb)->attr_tree)
    425			hfsplus_delete_all_attrs(dir, cnid);
    426	}
    427
    428out:
    429	hfs_find_exit(&fd);
    430
    431	return err;
    432}
    433
    434int hfsplus_rename_cat(u32 cnid,
    435		       struct inode *src_dir, const struct qstr *src_name,
    436		       struct inode *dst_dir, const struct qstr *dst_name)
    437{
    438	struct super_block *sb = src_dir->i_sb;
    439	struct hfs_find_data src_fd, dst_fd;
    440	hfsplus_cat_entry entry;
    441	int entry_size, type;
    442	int err;
    443
    444	hfs_dbg(CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
    445		cnid, src_dir->i_ino, src_name->name,
    446		dst_dir->i_ino, dst_name->name);
    447	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
    448	if (err)
    449		return err;
    450	dst_fd = src_fd;
    451
    452	/*
    453	 * Fail early and avoid ENOSPC during the btree operations. We may
    454	 * have to split the root node at most twice.
    455	 */
    456	err = hfs_bmap_reserve(src_fd.tree, 4 * (int)src_fd.tree->depth - 1);
    457	if (err)
    458		goto out;
    459
    460	/* find the old dir entry and read the data */
    461	err = hfsplus_cat_build_key(sb, src_fd.search_key,
    462			src_dir->i_ino, src_name);
    463	if (unlikely(err))
    464		goto out;
    465
    466	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
    467	if (err)
    468		goto out;
    469	if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
    470		err = -EIO;
    471		goto out;
    472	}
    473
    474	hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
    475				src_fd.entrylength);
    476	type = be16_to_cpu(entry.type);
    477
    478	/* create new dir entry with the data from the old entry */
    479	err = hfsplus_cat_build_key(sb, dst_fd.search_key,
    480			dst_dir->i_ino, dst_name);
    481	if (unlikely(err))
    482		goto out;
    483
    484	err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
    485	if (err != -ENOENT) {
    486		if (!err)
    487			err = -EEXIST;
    488		goto out;
    489	}
    490
    491	err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
    492	if (err)
    493		goto out;
    494	dst_dir->i_size++;
    495	if (type == HFSPLUS_FOLDER)
    496		hfsplus_subfolders_inc(dst_dir);
    497	dst_dir->i_mtime = dst_dir->i_ctime = current_time(dst_dir);
    498
    499	/* finally remove the old entry */
    500	err = hfsplus_cat_build_key(sb, src_fd.search_key,
    501			src_dir->i_ino, src_name);
    502	if (unlikely(err))
    503		goto out;
    504
    505	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
    506	if (err)
    507		goto out;
    508	err = hfs_brec_remove(&src_fd);
    509	if (err)
    510		goto out;
    511	src_dir->i_size--;
    512	if (type == HFSPLUS_FOLDER)
    513		hfsplus_subfolders_dec(src_dir);
    514	src_dir->i_mtime = src_dir->i_ctime = current_time(src_dir);
    515
    516	/* remove old thread entry */
    517	hfsplus_cat_build_key_with_cnid(sb, src_fd.search_key, cnid);
    518	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
    519	if (err)
    520		goto out;
    521	type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
    522	err = hfs_brec_remove(&src_fd);
    523	if (err)
    524		goto out;
    525
    526	/* create new thread entry */
    527	hfsplus_cat_build_key_with_cnid(sb, dst_fd.search_key, cnid);
    528	entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
    529		dst_dir->i_ino, dst_name);
    530	if (unlikely(entry_size < 0)) {
    531		err = entry_size;
    532		goto out;
    533	}
    534
    535	err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
    536	if (err != -ENOENT) {
    537		if (!err)
    538			err = -EEXIST;
    539		goto out;
    540	}
    541	err = hfs_brec_insert(&dst_fd, &entry, entry_size);
    542
    543	hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
    544	hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
    545out:
    546	hfs_bnode_put(dst_fd.bnode);
    547	hfs_find_exit(&src_fd);
    548	return err;
    549}