aboutsummaryrefslogtreecommitdiff
path: root/fs/exfat/fatent.c
blob: 8b87d03c5569fba85e0d3a37187a6bff0b8c54c5 (plain)
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
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
 *
 *  fatent.c: exFAT FAT entry manager
 */

#include <asm/unaligned.h>

#include "exfat.h"
#include "core.h"

/* All buffer structures are protected w/ fsi->v_sem */

static inline bool is_reserved_clus(u32 clus)
{
	if (IS_CLUS_FREE(clus))
		return true;
	if (IS_CLUS_EOF(clus))
		return true;
	if (IS_CLUS_BAD(clus))
		return true;
	return false;
}

static inline bool is_valid_clus(FS_INFO_T *fsi, u32 clus)
{
	if (clus < CLUS_BASE || fsi->num_clusters <= clus)
		return false;
	return true;
}

s32 exfat_ent_get(struct super_block *sb, u32 loc, u32 *content)
{
	FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi);
	u32 off, _content;
	u64 sec;
	u8 *fat_sector;

	if (!is_valid_clus(fsi, loc)) {
		exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", loc);
		return -EIO;
	}

	sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
	off = (loc << 2) & (u32)(sb->s_blocksize - 1);

	fat_sector = exfat_fcache_getblk(sb, sec);
	if (!fat_sector) {
		exfat_fs_error(sb, "failed to access to FAT "
				"(entry 0x%08x)", loc);
		return -EIO;
	}

	_content = le32_to_cpu(*(__le32 *)(&fat_sector[off]));

	/* remap reserved clusters to simplify code */
	if (_content >= CLUSTER_32(0xFFFFFFF8))
		_content = CLUS_EOF;

	*content = CLUSTER_32(_content);

	if (!is_reserved_clus(*content) && !is_valid_clus(fsi, *content)) {
		exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x) "
			"bogus content (0x%08x)", loc, *content);
		return -EIO;
	}

	return 0;
}

s32 exfat_ent_set(struct super_block *sb, u32 loc, u32 content)
{
	u32 off;
	u64 sec;
	u8 *fat_sector;
	__le32 *fat_entry;
	FS_INFO_T *fsi = &(EXFAT_SB(sb)->fsi);

	sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
	off = (loc << 2) & (u32)(sb->s_blocksize - 1);

	fat_sector = exfat_fcache_getblk(sb, sec);
	if (!fat_sector)
		return -EIO;

	fat_entry = (__le32 *)&(fat_sector[off]);
	*fat_entry = cpu_to_le32(content);

	return exfat_fcache_modify(sb, sec);
}

s32 exfat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content)
{
	s32 err = exfat_ent_get(sb, loc, content);

	if (err)
		return err;

	if (IS_CLUS_FREE(*content)) {
		exfat_fs_error(sb, "invalid access to FAT free cluster "
				"(entry 0x%08x)", loc);
		return -EIO;
	}

	if (IS_CLUS_BAD(*content)) {
		exfat_fs_error(sb, "invalid access to FAT bad cluster "
				"(entry 0x%08x)", loc);
		return -EIO;
	}

	return 0;
}