summaryrefslogtreecommitdiffstats
path: root/exfat/libexfat/utils.c
blob: 499833b50bc0f46b28c52d39da0146f71bf094c7 (plain) (blame)
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*
	utils.c (04.09.09)
	exFAT file system implementation library.

	Copyright (C) 2010-2013  Andrew Nayenko

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "exfat.h"
#include <string.h>
#include <stdio.h>
#include <inttypes.h>

void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
		struct stat* stbuf)
{
	memset(stbuf, 0, sizeof(struct stat));
	if (node->flags & EXFAT_ATTRIB_DIR)
		stbuf->st_mode = S_IFDIR | (0777 & ~ef->dmask);
	else
		stbuf->st_mode = S_IFREG | (0777 & ~ef->fmask);
	stbuf->st_nlink = 1;
	stbuf->st_uid = ef->uid;
	stbuf->st_gid = ef->gid;
	stbuf->st_size = node->size;
	stbuf->st_blocks = DIV_ROUND_UP(node->size, CLUSTER_SIZE(*ef->sb)) *
		CLUSTER_SIZE(*ef->sb) / 512;
	stbuf->st_mtime = node->mtime;
	stbuf->st_atime = node->atime;
	/* set ctime to mtime to ensure we don't break programs that rely on ctime
	   (e.g. rsync) */
	stbuf->st_ctime = node->mtime;
}

void exfat_get_name(const struct exfat_node* node, char* buffer, size_t n)
{
	if (utf16_to_utf8(buffer, node->name, n, EXFAT_NAME_MAX) != 0)
		exfat_bug("failed to convert name to UTF-8");
}

uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry)
{
	uint16_t sum = 0;
	int i;

	for (i = 0; i < sizeof(struct exfat_entry); i++)
		if (i != 2 && i != 3) /* skip checksum field itself */
			sum = ((sum << 15) | (sum >> 1)) + ((const uint8_t*) entry)[i];
	return sum;
}

uint16_t exfat_add_checksum(const void* entry, uint16_t sum)
{
	int i;

	for (i = 0; i < sizeof(struct exfat_entry); i++)
		sum = ((sum << 15) | (sum >> 1)) + ((const uint8_t*) entry)[i];
	return sum;
}

le16_t exfat_calc_checksum(const struct exfat_entry_meta1* meta1,
		const struct exfat_entry_meta2* meta2, const le16_t* name)
{
	uint16_t checksum;
	const int name_entries = DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX);
	int i;

	checksum = exfat_start_checksum(meta1);
	checksum = exfat_add_checksum(meta2, checksum);
	for (i = 0; i < name_entries; i++)
	{
		struct exfat_entry_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0};
		memcpy(name_entry.name, name + i * EXFAT_ENAME_MAX,
				EXFAT_ENAME_MAX * sizeof(le16_t));
		checksum = exfat_add_checksum(&name_entry, checksum);
	}
	return cpu_to_le16(checksum);
}

uint32_t exfat_vbr_start_checksum(const void* sector, size_t size)
{
	size_t i;
	uint32_t sum = 0;

	for (i = 0; i < size; i++)
		/* skip volume_state and allocated_percent fields */
		if (i != 0x6a && i != 0x6b && i != 0x70)
			sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
	return sum;
}

uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum)
{
	size_t i;

	for (i = 0; i < size; i++)
		sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
	return sum;
}

le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name)
{
	size_t i;
	size_t length = utf16_length(name);
	uint16_t hash = 0;

	for (i = 0; i < length; i++)
	{
		uint16_t c = le16_to_cpu(name[i]);

		/* convert to upper case */
		if (c < ef->upcase_chars)
			c = le16_to_cpu(ef->upcase[c]);

		hash = ((hash << 15) | (hash >> 1)) + (c & 0xff);
		hash = ((hash << 15) | (hash >> 1)) + (c >> 8);
	}
	return cpu_to_le16(hash);
}

void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb)
{
	size_t i;
	/* 16 EB (minus 1 byte) is the largest size that can be represented by
	   uint64_t */
	const char* units[] = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"};
	uint64_t divisor = 1;
	uint64_t temp = 0;

	for (i = 0; ; i++, divisor *= 1024)
	{
		temp = (value + divisor / 2) / divisor;

		if (temp == 0)
			break;
		if (temp / 1024 * 1024 == temp)
			continue;
		if (temp < 10240)
			break;
	}
	hb->value = temp;
	hb->unit = units[i];
}

void exfat_print_info(const struct exfat_super_block* sb,
		uint32_t free_clusters)
{
	struct exfat_human_bytes hb;
	off64_t total_space = le64_to_cpu(sb->sector_count) * SECTOR_SIZE(*sb);
	off64_t avail_space = (off64_t) free_clusters * CLUSTER_SIZE(*sb);

	printf("File system version           %hhu.%hhu\n",
			sb->version.major, sb->version.minor);
	exfat_humanize_bytes(SECTOR_SIZE(*sb), &hb);
	printf("Sector size          %10"PRIu64" %s\n", hb.value, hb.unit);
	exfat_humanize_bytes(CLUSTER_SIZE(*sb), &hb);
	printf("Cluster size         %10"PRIu64" %s\n", hb.value, hb.unit);
	exfat_humanize_bytes(total_space, &hb);
	printf("Volume size          %10"PRIu64" %s\n", hb.value, hb.unit);
	exfat_humanize_bytes(total_space - avail_space, &hb);
	printf("Used space           %10"PRIu64" %s\n", hb.value, hb.unit);
	exfat_humanize_bytes(avail_space, &hb);
	printf("Available space      %10"PRIu64" %s\n", hb.value, hb.unit);
}