source: mainline/uspace/srv/fs/fat/fat_dentry.c@ 2d0d637

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2d0d637 was 2d0d637, checked in by Oleg Romanenko <romanenko.oleg@…>, 14 years ago

Functions for checking string compatibility with SFN requirements

  • Property mode set to 100644
File size: 10.0 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33/**
34 * @file fat_dentry.c
35 * @brief Functions that work with FAT directory entries.
36 */
37
38#include "fat_dentry.h"
39#include <ctype.h>
40#include <str.h>
41#include <errno.h>
42#include <byteorder.h>
43#include <assert.h>
44
45static bool is_d_char(const char ch)
46{
47 if (isalnum(ch) || ch == '_')
48 return true;
49 else
50 return false;
51}
52
53/** Compare path component with the name read from the dentry.
54 *
55 * This function compares the path component with the name read from the dentry.
56 * The comparison is case insensitive and tolerates a mismatch on the trailing
57 * dot character at the end of the name (i.e. when there is a dot, but no
58 * extension).
59 *
60 * @param name Node name read from the dentry.
61 * @param component Path component.
62 *
63 * @return Zero on match, non-zero otherwise.
64 */
65int fat_dentry_namecmp(char *name, const char *component)
66{
67 int rc;
68 size_t size;
69
70 if (!(rc = stricmp(name, component)))
71 return rc;
72 if (!str_chr(name, '.')) {
73 /*
74 * There is no '.' in the name, so we know that there is enough
75 * space for appending an extra '.' to name.
76 */
77 size = str_size(name);
78 name[size] = '.';
79 name[size + 1] = '\0';
80 rc = stricmp(name, component);
81 }
82 return rc;
83}
84
85bool fat_dentry_name_verify(const char *name)
86{
87 unsigned int i;
88 unsigned int dot = 0;
89 bool dot_found = false;
90
91
92 for (i = 0; name[i]; i++) {
93 if (name[i] == '.') {
94 if (dot_found) {
95 return false;
96 } else {
97 dot_found = true;
98 dot = i;
99 }
100 } else {
101 if (!is_d_char(name[i]))
102 return false;
103 }
104 }
105
106 if (dot_found) {
107 if (dot > FAT_NAME_LEN)
108 return false;
109 if (i - dot > FAT_EXT_LEN + 1)
110 return false;
111 } else {
112 if (i > FAT_NAME_LEN)
113 return false;
114 }
115
116 return true;
117}
118
119void fat_dentry_name_get(const fat_dentry_t *d, char *buf)
120{
121 unsigned int i;
122
123 for (i = 0; i < FAT_NAME_LEN; i++) {
124 if (d->name[i] == FAT_PAD)
125 break;
126
127 if (d->name[i] == FAT_DENTRY_E5_ESC)
128 *buf++ = 0xe5;
129 else {
130 if (d->lcase & FAT_LCASE_LOWER_NAME)
131 *buf++ = tolower(d->name[i]);
132 else
133 *buf++ = d->name[i];
134 }
135 }
136
137 if (d->ext[0] != FAT_PAD)
138 *buf++ = '.';
139
140 for (i = 0; i < FAT_EXT_LEN; i++) {
141 if (d->ext[i] == FAT_PAD) {
142 *buf = '\0';
143 return;
144 }
145
146 if (d->ext[i] == FAT_DENTRY_E5_ESC)
147 *buf++ = 0xe5;
148 else {
149 if (d->lcase & FAT_LCASE_LOWER_EXT)
150 *buf++ = tolower(d->ext[i]);
151 else
152 *buf++ = d->ext[i];
153 }
154 }
155
156 *buf = '\0';
157}
158
159void fat_dentry_name_set(fat_dentry_t *d, const char *name)
160{
161 unsigned int i;
162 const char fake_ext[] = " ";
163 bool lower_name = true;
164 bool lower_ext = true;
165
166 for (i = 0; i < FAT_NAME_LEN; i++) {
167 switch ((uint8_t) *name) {
168 case 0xe5:
169 d->name[i] = FAT_DENTRY_E5_ESC;
170 name++;
171 break;
172 case '\0':
173 case '.':
174 d->name[i] = FAT_PAD;
175 break;
176 default:
177 if (isalpha(*name)) {
178 if (!islower(*name))
179 lower_name = false;
180 }
181
182 d->name[i] = toupper(*name++);
183 break;
184 }
185 }
186
187 if (*name++ != '.')
188 name = fake_ext;
189
190 for (i = 0; i < FAT_EXT_LEN; i++) {
191 switch ((uint8_t) *name) {
192 case 0xe5:
193 d->ext[i] = FAT_DENTRY_E5_ESC;
194 name++;
195 break;
196 case '\0':
197 d->ext[i] = FAT_PAD;
198 break;
199 default:
200 if (isalpha(*name)) {
201 if (!islower(*name))
202 lower_ext = false;
203 }
204
205 d->ext[i] = toupper(*name++);
206 break;
207 }
208 }
209
210 if (lower_name)
211 d->lcase |= FAT_LCASE_LOWER_NAME;
212 else
213 d->lcase &= ~FAT_LCASE_LOWER_NAME;
214
215 if (lower_ext)
216 d->lcase |= FAT_LCASE_LOWER_EXT;
217 else
218 d->lcase &= ~FAT_LCASE_LOWER_EXT;
219}
220
221fat_dentry_clsf_t fat_classify_dentry(const fat_dentry_t *d)
222{
223 if (d->attr == FAT_ATTR_LFN) {
224 /* long name entry */
225 if (FAT_LFN_ORDER(d) & FAT_LFN_ERASED)
226 return FAT_DENTRY_FREE;
227 else
228 return FAT_DENTRY_LFN;
229 }
230 if (d->attr & FAT_ATTR_VOLLABEL) {
231 /* volume label entry */
232 return FAT_DENTRY_SKIP;
233 }
234 if (d->name[0] == FAT_DENTRY_ERASED) {
235 /* not-currently-used entry */
236 return FAT_DENTRY_FREE;
237 }
238 if (d->name[0] == FAT_DENTRY_UNUSED) {
239 /* never used entry */
240 return FAT_DENTRY_LAST;
241 }
242 if (d->name[0] == FAT_DENTRY_DOT) {
243 /*
244 * Most likely '.' or '..'.
245 * It cannot occur in a regular file name.
246 */
247 return FAT_DENTRY_SKIP;
248 }
249 return FAT_DENTRY_VALID;
250}
251
252/** Compute checksum of Node name.
253 *
254 * Returns an unsigned byte checksum computed on an unsigned byte
255 * array. The array must be 11 bytes long and is assumed to contain
256 * a name stored in the format of a MS-DOS directory entry.
257 *
258 * @param name Node name read from the dentry.
259 *
260 * @return An 8-bit unsigned checksum of the name.
261 */
262uint8_t fat_dentry_chksum(uint8_t *name)
263{
264 uint8_t i, sum=0;
265 for (i=0; i<(FAT_NAME_LEN+FAT_EXT_LEN); i++) {
266 sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + name[i];
267 }
268 return sum;
269}
270
271/** Get number of bytes in a string with size limit.
272 *
273 * @param str NULL-terminated (or not) string.
274 * @param size Maximum number of bytes to consider.
275 *
276 * @return Number of bytes in string (without 0 and ff).
277 *
278 */
279size_t fat_lfn_str_nlength(const uint16_t *str, size_t size)
280{
281 size_t offset = 0;
282
283 while (offset < size) {
284 if (str[offset] == 0 || str[offset] == 0xffff)
285 break;
286 offset++;
287 }
288 return offset;
289}
290
291/** Get number of bytes in a FAT long entry occuped by characters.
292 *
293 * @param d FAT long entry.
294 *
295 * @return Number of bytes.
296 *
297 */
298size_t fat_lfn_size(const fat_dentry_t *d)
299{
300 size_t size = 0;
301
302 size += fat_lfn_str_nlength(FAT_LFN_PART1(d), FAT_LFN_PART1_SIZE);
303 size += fat_lfn_str_nlength(FAT_LFN_PART2(d), FAT_LFN_PART2_SIZE);
304 size += fat_lfn_str_nlength(FAT_LFN_PART3(d), FAT_LFN_PART3_SIZE);
305
306 return size;
307}
308
309size_t fat_lfn_copy_part(const uint16_t *src, size_t src_size, uint16_t *dst, size_t *offset)
310{
311 while (src_size!=0 && (*offset)!=0) {
312 src_size--;
313 if (src[src_size] == 0 || src[src_size] == 0xffff)
314 continue;
315
316 (*offset)--;
317 dst[(*offset)] = uint16_t_le2host(src[src_size]);
318 }
319 return (*offset);
320}
321
322size_t fat_lfn_copy_entry(const fat_dentry_t *d, uint16_t *dst, size_t *offset)
323{
324 fat_lfn_copy_part(FAT_LFN_PART3(d), FAT_LFN_PART3_SIZE, dst, offset);
325 fat_lfn_copy_part(FAT_LFN_PART2(d), FAT_LFN_PART2_SIZE, dst, offset);
326 fat_lfn_copy_part(FAT_LFN_PART1(d), FAT_LFN_PART1_SIZE, dst, offset);
327
328 return *offset;
329}
330
331/** Convert utf16 string to string.
332 *
333 * Convert wide string @a src to string. The output is written to the buffer
334 * specified by @a dest and @a size. @a size must be non-zero and the string
335 * written will always be well-formed.
336 *
337 * @param dest Destination buffer.
338 * @param size Size of the destination buffer.
339 * @param src Source wide string.
340 */
341int utf16_to_str(char *dest, size_t size, const uint16_t *src)
342{
343 int rc;
344 uint16_t ch;
345 size_t src_idx, dest_off;
346
347 /* There must be space for a null terminator in the buffer. */
348 assert(size > 0);
349
350 src_idx = 0;
351 dest_off = 0;
352
353 while ((ch = src[src_idx++]) != 0) {
354 rc = chr_encode(ch, dest, &dest_off, size - 1);
355 if (rc != EOK)
356 return rc;
357 }
358
359 dest[dest_off] = '\0';
360 return EOK;
361}
362
363/** Convert string to utf16 string.
364 *
365 * Convert string @a src to wide string. The output is written to the
366 * buffer specified by @a dest and @a dlen. @a dlen must be non-zero
367 * and the wide string written will always be null-terminated.
368 *
369 * @param dest Destination buffer.
370 * @param dlen Length of destination buffer (number of wchars).
371 * @param src Source string.
372 */
373int str_to_utf16(uint16_t *dest, size_t dlen, const char *src)
374{
375 size_t offset;
376 size_t di;
377 uint16_t c;
378
379 assert(dlen > 0);
380
381 offset = 0;
382 di = 0;
383
384 do {
385 if (di >= dlen - 1)
386 return EOVERFLOW;
387
388 c = str_decode(src, &offset, STR_NO_LIMIT);
389 dest[di++] = c;
390 } while (c != '\0');
391
392 dest[dlen - 1] = '\0';
393 return EOK;
394}
395
396bool fat_lfn_valid_char(uint16_t c)
397{
398 char valid[] = {"_.$%\'-@~!(){}^#&"};
399 size_t idx=0;
400
401 if (c > 0xff) return false;
402 if (isdigit(c) || (isalpha(c) && isupper(c)))
403 return true;
404 while(valid[idx]!=0)
405 if (c == valid[idx++])
406 return true;
407
408 return false;
409}
410
411bool fat_lfn_valid_str(const uint16_t *str)
412{
413 uint16_t c;
414 size_t idx=0;
415 if (str[idx] == 0 || str[idx] == '.')
416 return false;
417 while ((c=str[idx++]) != 0) {
418 if (!fat_lfn_valid_char(c))
419 return false;
420 }
421 return true;
422}
423
424/** Get number of characters in a wide string.
425 *
426 * @param str NULL-terminated wide string.
427 *
428 * @return Number of characters in @a str.
429 *
430 */
431size_t utf16_length(const uint16_t *wstr)
432{
433 size_t len = 0;
434
435 while (*wstr++ != 0)
436 len++;
437 return len;
438}
439
440bool fat_dentry_is_sfn(const uint16_t *str)
441{
442 /* 1. Length <= 11 characters */
443 if (utf16_length(str) > (FAT_NAME_LEN + FAT_EXT_LEN))
444 return false;
445 /*
446 * 2. All characters in string should be ASCII
447 * 3. All letters must be uppercase
448 * 4. String should not contain invalid characters
449 */
450 if (!fat_lfn_valid_str(str))
451 return false;
452
453 return true;
454}
455
456
457/**
458 * @}
459 */
Note: See TracBrowser for help on using the repository browser.