xref: /haiku/src/add-ons/kernel/file_systems/udf/Utils.cpp (revision 11c9f9a1d649c006f9066ae9436372ab75728c4b)
1 /*
2  * Copyright 2012, Jérôme Duval, korli@users.berlios.de.
3  * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 /*! \file Utils.cpp - Miscellaneous Udf utility functions. */
8 
9 #include "UdfStructures.h"
10 #include "Utils.h"
11 
12 extern "C" {
13 	extern int32 timezone_offset;
14 }
15 
16 /*! \brief Returns "true" if \a value is true, "false" otherwise. */
17 const char*
18 bool_to_string(bool value)
19 {
20 	return value ? "true" : "false";
21 }
22 
23 
24 /*! \brief Calculates the UDF crc checksum for the given byte stream.
25 
26 	Based on crc code from UDF-2.50 6.5, as permitted.
27 
28 	\param data Pointer to the byte stream.
29 	\param length Length of the byte stream in bytes.
30 
31 	\return The crc checksum, or 0 if an error occurred.
32 */
33 uint16
34 calculate_crc(uint8 *data, uint16 length)
35 {
36 	uint16 crc = 0;
37 	if (data) {
38 		for ( ; length > 0; length--, data++)
39 			crc = kCrcTable[(crc >> 8 ^ *data) & 0xff] ^ (crc << 8);
40 	}
41 	return crc;
42 }
43 
44 
45 /*! \brief Takes an overloaded ssize_t return value like those returned
46 	by BFile::Read() and friends, as well as an expected number of bytes,
47 	and returns B_OK if the byte counts match, or the appropriate error
48 	code otherwise.
49 */
50 status_t
51 check_size_error(ssize_t bytesReturned, ssize_t bytesExpected)
52 {
53 	return bytesReturned == bytesExpected
54 		? B_OK : (bytesReturned >= 0 ? B_IO_ERROR : status_t(bytesReturned));
55 }
56 
57 
58 /*! \brief Calculates the block shift amount for the given
59  	block size, which must be a positive power of 2.
60 */
61 status_t
62 get_block_shift(uint32 blockSize, uint32 &blockShift)
63 {
64 	if (blockSize == 0)
65 		return B_BAD_VALUE;
66 	uint32 bitCount = 0;
67 	uint32 result = 0;
68 	for (int i = 0; i < 32; i++) {
69 		// Zero out all bits except bit i
70 		uint32 block = blockSize & (uint32(1) << i);
71 		if (block) {
72 			if (++bitCount > 1)
73 				return B_BAD_VALUE;
74 			else
75 				result = i;
76 		}
77 	}
78 	blockShift = result;
79 	return B_OK;
80 }
81 
82 
83 #define EPOCH_YEAR	1970
84 #define MAX_YEAR	69
85 #define SECSPERMIN	60
86 #define MINSPERHOUR	60
87 #define HOURSPERDAY	24
88 #define SECSPERDAY	(SECSPERMIN * MINSPERHOUR * HOURSPERDAY)
89 #define DAYSPERNYEAR	365
90 
91 status_t
92 decode_time(timestamp &timestamp, struct timespec &timespec)
93 {
94 	DEBUG_INIT_ETC(NULL, ("timestamp: (tnt: 0x%x, type: %d, timezone: %d = 0x%x, year: %d, "
95 	           "month: %d, day: %d, hour: %d, minute: %d, second: %d)", timestamp.type_and_timezone(),
96 	           timestamp.type(), timestamp.timezone(),
97 	            timestamp.timezone(),timestamp.year(),
98 	           timestamp.month(), timestamp.day(), timestamp.hour(), timestamp.minute(), timestamp.second()));
99 
100 	if (timestamp.year() < EPOCH_YEAR || timestamp.year() >= EPOCH_YEAR + MAX_YEAR)
101 		return B_BAD_VALUE;
102 
103 	time_t result = 0;
104 	const int monthLengths[12]
105 		= { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
106 
107 	int year = timestamp.year();
108 	int month = timestamp.month();
109 	int day = timestamp.day();
110 	int hour = timestamp.hour();
111 	int minute = timestamp.minute();
112 	int second = timestamp.second();
113 
114 	// Range check the timezone offset, then round it down
115 	// to the nearest hour, since no one I know treats timezones
116 	// with a per-minute granularity, and none of the other OSes
117 	// I've looked at appear to either.
118 	int timezone_offset = 0;
119 	if (timestamp.type() == 1)
120 		timezone_offset = timestamp.timezone();
121 	if (-SECSPERDAY > timezone_offset || timezone_offset > SECSPERDAY)
122 		timezone_offset = 0;
123 	timezone_offset -= timezone_offset % 60;
124 
125 	int previousLeapYears = (year - 1968) / 4;
126 	bool isLeapYear = (year - 1968) % 4 == 0;
127 	if (isLeapYear)
128 		--previousLeapYears;
129 
130 	// Years to days
131 	result = (year - EPOCH_YEAR) * DAYSPERNYEAR + previousLeapYears;
132 	// Months to days
133 	for (int i = 0; i < month-1; i++) {
134 		result += monthLengths[i];
135 	}
136 	if (month > 2 && isLeapYear)
137 		++result;
138 	// Days to hours
139 	result = (result + day - 1) * HOURSPERDAY;
140 	// Hours to minutes
141 	result = (result + hour) * MINSPERHOUR + timezone_offset;
142 	// Minutes to seconds
143 	result = (result + minute) * SECSPERMIN + second;
144 
145 	timespec.tv_sec = result;
146 	timespec.tv_nsec = 1000 * (timestamp.microsecond()
147 		+ timestamp.hundred_microsecond() * 100
148 		+ timestamp.centisecond() * 10000);
149 	return B_OK;
150 }
151 
152 
153 long_address
154 to_long_address(ino_t id, uint32 length)
155 {
156 	TRACE(("udf_to_long_address: ino_t = %Ld (0x%Lx), length = %ld",
157 		id, id, length));
158 	long_address result;
159 	result.set_block((id >> 16) & 0xffffffff);
160 	result.set_partition(id & 0xffff);
161 	result.set_length(length);
162 	DUMP(result);
163 	return result;
164 }
165 
166 
167 ino_t
168 to_vnode_id(long_address address)
169 {
170 	DEBUG_INIT(NULL);
171 	ino_t result = address.block();
172 	result <<= 16;
173 	result |= address.partition();
174 	TRACE(("block:     %ld, 0x%lx\n", address.block(), address.block()));
175 	TRACE(("partition: %d, 0x%x\n", address.partition(), address.partition()));
176 	TRACE(("length:    %ld, 0x%lx\n", address.length(), address.length()));
177 	TRACE(("ino_t:     %Ld, 0x%Lx\n", result, result));
178 	return result;
179 }
180