diff options
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/dir.c | 229 | ||||
-rw-r--r-- | fs/fat/inode.c | 34 | ||||
-rw-r--r-- | fs/fat/misc.c | 10 |
3 files changed, 155 insertions, 118 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 34541d06e626..cd4a0162e10d 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c | |||
@@ -17,7 +17,6 @@ | |||
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/time.h> | 18 | #include <linux/time.h> |
19 | #include <linux/msdos_fs.h> | 19 | #include <linux/msdos_fs.h> |
20 | #include <linux/dirent.h> | ||
21 | #include <linux/smp_lock.h> | 20 | #include <linux/smp_lock.h> |
22 | #include <linux/buffer_head.h> | 21 | #include <linux/buffer_head.h> |
23 | #include <linux/compat.h> | 22 | #include <linux/compat.h> |
@@ -124,10 +123,11 @@ static inline int fat_get_entry(struct inode *dir, loff_t *pos, | |||
124 | * but ignore that right now. | 123 | * but ignore that right now. |
125 | * Ahem... Stack smashing in ring 0 isn't fun. Fixed. | 124 | * Ahem... Stack smashing in ring 0 isn't fun. Fixed. |
126 | */ | 125 | */ |
127 | static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int len, | 126 | static int uni16_to_x8(unsigned char *ascii, const wchar_t *uni, int len, |
128 | int uni_xlate, struct nls_table *nls) | 127 | int uni_xlate, struct nls_table *nls) |
129 | { | 128 | { |
130 | wchar_t *ip, ec; | 129 | const wchar_t *ip; |
130 | wchar_t ec; | ||
131 | unsigned char *op, nc; | 131 | unsigned char *op, nc; |
132 | int charlen; | 132 | int charlen; |
133 | int k; | 133 | int k; |
@@ -167,6 +167,16 @@ static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int len, | |||
167 | return (op - ascii); | 167 | return (op - ascii); |
168 | } | 168 | } |
169 | 169 | ||
170 | static inline int fat_uni_to_x8(struct msdos_sb_info *sbi, const wchar_t *uni, | ||
171 | unsigned char *buf, int size) | ||
172 | { | ||
173 | if (sbi->options.utf8) | ||
174 | return utf8_wcstombs(buf, uni, size); | ||
175 | else | ||
176 | return uni16_to_x8(buf, uni, size, sbi->options.unicode_xlate, | ||
177 | sbi->nls_io); | ||
178 | } | ||
179 | |||
170 | static inline int | 180 | static inline int |
171 | fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) | 181 | fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) |
172 | { | 182 | { |
@@ -227,6 +237,19 @@ fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size, | |||
227 | return len; | 237 | return len; |
228 | } | 238 | } |
229 | 239 | ||
240 | static inline int fat_name_match(struct msdos_sb_info *sbi, | ||
241 | const unsigned char *a, int a_len, | ||
242 | const unsigned char *b, int b_len) | ||
243 | { | ||
244 | if (a_len != b_len) | ||
245 | return 0; | ||
246 | |||
247 | if (sbi->options.name_check != 's') | ||
248 | return !nls_strnicmp(sbi->nls_io, a, b, a_len); | ||
249 | else | ||
250 | return !memcmp(a, b, a_len); | ||
251 | } | ||
252 | |||
230 | enum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, }; | 253 | enum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, }; |
231 | 254 | ||
232 | /** | 255 | /** |
@@ -302,6 +325,19 @@ parse_long: | |||
302 | } | 325 | } |
303 | 326 | ||
304 | /* | 327 | /* |
328 | * Maximum buffer size of short name. | ||
329 | * [(MSDOS_NAME + '.') * max one char + nul] | ||
330 | * For msdos style, ['.' (hidden) + MSDOS_NAME + '.' + nul] | ||
331 | */ | ||
332 | #define FAT_MAX_SHORT_SIZE ((MSDOS_NAME + 1) * NLS_MAX_CHARSET_SIZE + 1) | ||
333 | /* | ||
334 | * Maximum buffer size of unicode chars from slots. | ||
335 | * [(max longname slots * 13 (size in a slot) + nul) * sizeof(wchar_t)] | ||
336 | */ | ||
337 | #define FAT_MAX_UNI_CHARS ((MSDOS_SLOTS - 1) * 13 + 1) | ||
338 | #define FAT_MAX_UNI_SIZE (FAT_MAX_UNI_CHARS * sizeof(wchar_t)) | ||
339 | |||
340 | /* | ||
305 | * Return values: negative -> error, 0 -> not found, positive -> found, | 341 | * Return values: negative -> error, 0 -> not found, positive -> found, |
306 | * value is the total amount of slots, including the shortname entry. | 342 | * value is the total amount of slots, including the shortname entry. |
307 | */ | 343 | */ |
@@ -312,29 +348,20 @@ int fat_search_long(struct inode *inode, const unsigned char *name, | |||
312 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 348 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
313 | struct buffer_head *bh = NULL; | 349 | struct buffer_head *bh = NULL; |
314 | struct msdos_dir_entry *de; | 350 | struct msdos_dir_entry *de; |
315 | struct nls_table *nls_io = sbi->nls_io; | ||
316 | struct nls_table *nls_disk = sbi->nls_disk; | 351 | struct nls_table *nls_disk = sbi->nls_disk; |
317 | wchar_t bufuname[14]; | ||
318 | unsigned char nr_slots; | 352 | unsigned char nr_slots; |
319 | int xlate_len; | 353 | wchar_t bufuname[14]; |
320 | wchar_t *unicode = NULL; | 354 | wchar_t *unicode = NULL; |
321 | unsigned char work[MSDOS_NAME]; | 355 | unsigned char work[MSDOS_NAME]; |
322 | unsigned char *bufname = NULL; | 356 | unsigned char bufname[FAT_MAX_SHORT_SIZE]; |
323 | int uni_xlate = sbi->options.unicode_xlate; | ||
324 | int utf8 = sbi->options.utf8; | ||
325 | int anycase = (sbi->options.name_check != 's'); | ||
326 | unsigned short opt_shortname = sbi->options.shortname; | 357 | unsigned short opt_shortname = sbi->options.shortname; |
327 | loff_t cpos = 0; | 358 | loff_t cpos = 0; |
328 | int chl, i, j, last_u, err; | 359 | int chl, i, j, last_u, err, len; |
329 | |||
330 | bufname = __getname(); | ||
331 | if (!bufname) | ||
332 | return -ENOMEM; | ||
333 | 360 | ||
334 | err = -ENOENT; | 361 | err = -ENOENT; |
335 | while(1) { | 362 | while (1) { |
336 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | 363 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) |
337 | goto EODir; | 364 | goto end_of_dir; |
338 | parse_record: | 365 | parse_record: |
339 | nr_slots = 0; | 366 | nr_slots = 0; |
340 | if (de->name[0] == DELETED_FLAG) | 367 | if (de->name[0] == DELETED_FLAG) |
@@ -353,7 +380,7 @@ parse_record: | |||
353 | else if (status == PARSE_NOT_LONGNAME) | 380 | else if (status == PARSE_NOT_LONGNAME) |
354 | goto parse_record; | 381 | goto parse_record; |
355 | else if (status == PARSE_EOF) | 382 | else if (status == PARSE_EOF) |
356 | goto EODir; | 383 | goto end_of_dir; |
357 | } | 384 | } |
358 | 385 | ||
359 | memcpy(work, de->name, sizeof(de->name)); | 386 | memcpy(work, de->name, sizeof(de->name)); |
@@ -394,30 +421,24 @@ parse_record: | |||
394 | if (!last_u) | 421 | if (!last_u) |
395 | continue; | 422 | continue; |
396 | 423 | ||
424 | /* Compare shortname */ | ||
397 | bufuname[last_u] = 0x0000; | 425 | bufuname[last_u] = 0x0000; |
398 | xlate_len = utf8 | 426 | len = fat_uni_to_x8(sbi, bufuname, bufname, sizeof(bufname)); |
399 | ?utf8_wcstombs(bufname, bufuname, PATH_MAX) | 427 | if (fat_name_match(sbi, name, name_len, bufname, len)) |
400 | :uni16_to_x8(bufname, bufuname, PATH_MAX, uni_xlate, nls_io); | 428 | goto found; |
401 | if (xlate_len == name_len) | ||
402 | if ((!anycase && !memcmp(name, bufname, xlate_len)) || | ||
403 | (anycase && !nls_strnicmp(nls_io, name, bufname, | ||
404 | xlate_len))) | ||
405 | goto Found; | ||
406 | 429 | ||
407 | if (nr_slots) { | 430 | if (nr_slots) { |
408 | xlate_len = utf8 | 431 | void *longname = unicode + FAT_MAX_UNI_CHARS; |
409 | ?utf8_wcstombs(bufname, unicode, PATH_MAX) | 432 | int size = PATH_MAX - FAT_MAX_UNI_SIZE; |
410 | :uni16_to_x8(bufname, unicode, PATH_MAX, uni_xlate, nls_io); | 433 | |
411 | if (xlate_len != name_len) | 434 | /* Compare longname */ |
412 | continue; | 435 | len = fat_uni_to_x8(sbi, unicode, longname, size); |
413 | if ((!anycase && !memcmp(name, bufname, xlate_len)) || | 436 | if (fat_name_match(sbi, name, name_len, longname, len)) |
414 | (anycase && !nls_strnicmp(nls_io, name, bufname, | 437 | goto found; |
415 | xlate_len))) | ||
416 | goto Found; | ||
417 | } | 438 | } |
418 | } | 439 | } |
419 | 440 | ||
420 | Found: | 441 | found: |
421 | nr_slots++; /* include the de */ | 442 | nr_slots++; /* include the de */ |
422 | sinfo->slot_off = cpos - nr_slots * sizeof(*de); | 443 | sinfo->slot_off = cpos - nr_slots * sizeof(*de); |
423 | sinfo->nr_slots = nr_slots; | 444 | sinfo->nr_slots = nr_slots; |
@@ -425,9 +446,7 @@ Found: | |||
425 | sinfo->bh = bh; | 446 | sinfo->bh = bh; |
426 | sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); | 447 | sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); |
427 | err = 0; | 448 | err = 0; |
428 | EODir: | 449 | end_of_dir: |
429 | if (bufname) | ||
430 | __putname(bufname); | ||
431 | if (unicode) | 450 | if (unicode) |
432 | __putname(unicode); | 451 | __putname(unicode); |
433 | 452 | ||
@@ -453,23 +472,20 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, | |||
453 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 472 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
454 | struct buffer_head *bh; | 473 | struct buffer_head *bh; |
455 | struct msdos_dir_entry *de; | 474 | struct msdos_dir_entry *de; |
456 | struct nls_table *nls_io = sbi->nls_io; | ||
457 | struct nls_table *nls_disk = sbi->nls_disk; | 475 | struct nls_table *nls_disk = sbi->nls_disk; |
458 | unsigned char long_slots; | 476 | unsigned char nr_slots; |
459 | const char *fill_name; | ||
460 | int fill_len; | ||
461 | wchar_t bufuname[14]; | 477 | wchar_t bufuname[14]; |
462 | wchar_t *unicode = NULL; | 478 | wchar_t *unicode = NULL; |
463 | unsigned char c, work[MSDOS_NAME], bufname[56], *ptname = bufname; | 479 | unsigned char c, work[MSDOS_NAME]; |
464 | unsigned long lpos, dummy, *furrfu = &lpos; | 480 | unsigned char bufname[FAT_MAX_SHORT_SIZE], *ptname = bufname; |
465 | int uni_xlate = sbi->options.unicode_xlate; | 481 | unsigned short opt_shortname = sbi->options.shortname; |
466 | int isvfat = sbi->options.isvfat; | 482 | int isvfat = sbi->options.isvfat; |
467 | int utf8 = sbi->options.utf8; | ||
468 | int nocase = sbi->options.nocase; | 483 | int nocase = sbi->options.nocase; |
469 | unsigned short opt_shortname = sbi->options.shortname; | 484 | const char *fill_name = NULL; |
470 | unsigned long inum; | 485 | unsigned long inum; |
471 | int chi, chl, i, i2, j, last, last_u, dotoffset = 0; | 486 | unsigned long lpos, dummy, *furrfu = &lpos; |
472 | loff_t cpos; | 487 | loff_t cpos; |
488 | int chi, chl, i, i2, j, last, last_u, dotoffset = 0, fill_len = 0; | ||
473 | int ret = 0; | 489 | int ret = 0; |
474 | 490 | ||
475 | lock_super(sb); | 491 | lock_super(sb); |
@@ -489,43 +505,58 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, | |||
489 | cpos = 0; | 505 | cpos = 0; |
490 | } | 506 | } |
491 | } | 507 | } |
492 | if (cpos & (sizeof(struct msdos_dir_entry)-1)) { | 508 | if (cpos & (sizeof(struct msdos_dir_entry) - 1)) { |
493 | ret = -ENOENT; | 509 | ret = -ENOENT; |
494 | goto out; | 510 | goto out; |
495 | } | 511 | } |
496 | 512 | ||
497 | bh = NULL; | 513 | bh = NULL; |
498 | GetNew: | 514 | get_new: |
499 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | 515 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) |
500 | goto EODir; | 516 | goto end_of_dir; |
501 | parse_record: | 517 | parse_record: |
502 | long_slots = 0; | 518 | nr_slots = 0; |
503 | /* Check for long filename entry */ | 519 | /* |
504 | if (isvfat) { | 520 | * Check for long filename entry, but if short_only, we don't |
521 | * need to parse long filename. | ||
522 | */ | ||
523 | if (isvfat && !short_only) { | ||
505 | if (de->name[0] == DELETED_FLAG) | 524 | if (de->name[0] == DELETED_FLAG) |
506 | goto RecEnd; | 525 | goto record_end; |
507 | if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) | 526 | if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) |
508 | goto RecEnd; | 527 | goto record_end; |
509 | if (de->attr != ATTR_EXT && IS_FREE(de->name)) | 528 | if (de->attr != ATTR_EXT && IS_FREE(de->name)) |
510 | goto RecEnd; | 529 | goto record_end; |
511 | } else { | 530 | } else { |
512 | if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) | 531 | if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) |
513 | goto RecEnd; | 532 | goto record_end; |
514 | } | 533 | } |
515 | 534 | ||
516 | if (isvfat && de->attr == ATTR_EXT) { | 535 | if (isvfat && de->attr == ATTR_EXT) { |
517 | int status = fat_parse_long(inode, &cpos, &bh, &de, | 536 | int status = fat_parse_long(inode, &cpos, &bh, &de, |
518 | &unicode, &long_slots); | 537 | &unicode, &nr_slots); |
519 | if (status < 0) { | 538 | if (status < 0) { |
520 | filp->f_pos = cpos; | 539 | filp->f_pos = cpos; |
521 | ret = status; | 540 | ret = status; |
522 | goto out; | 541 | goto out; |
523 | } else if (status == PARSE_INVALID) | 542 | } else if (status == PARSE_INVALID) |
524 | goto RecEnd; | 543 | goto record_end; |
525 | else if (status == PARSE_NOT_LONGNAME) | 544 | else if (status == PARSE_NOT_LONGNAME) |
526 | goto parse_record; | 545 | goto parse_record; |
527 | else if (status == PARSE_EOF) | 546 | else if (status == PARSE_EOF) |
528 | goto EODir; | 547 | goto end_of_dir; |
548 | |||
549 | if (nr_slots) { | ||
550 | void *longname = unicode + FAT_MAX_UNI_CHARS; | ||
551 | int size = PATH_MAX - FAT_MAX_UNI_SIZE; | ||
552 | int len = fat_uni_to_x8(sbi, unicode, longname, size); | ||
553 | |||
554 | fill_name = longname; | ||
555 | fill_len = len; | ||
556 | /* !both && !short_only, so we don't need shortname. */ | ||
557 | if (!both) | ||
558 | goto start_filldir; | ||
559 | } | ||
529 | } | 560 | } |
530 | 561 | ||
531 | if (sbi->options.dotsOK) { | 562 | if (sbi->options.dotsOK) { |
@@ -587,12 +618,32 @@ parse_record: | |||
587 | } | 618 | } |
588 | } | 619 | } |
589 | if (!last) | 620 | if (!last) |
590 | goto RecEnd; | 621 | goto record_end; |
591 | 622 | ||
592 | i = last + dotoffset; | 623 | i = last + dotoffset; |
593 | j = last_u; | 624 | j = last_u; |
594 | 625 | ||
595 | lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry); | 626 | if (isvfat) { |
627 | bufuname[j] = 0x0000; | ||
628 | i = fat_uni_to_x8(sbi, bufuname, bufname, sizeof(bufname)); | ||
629 | } | ||
630 | if (nr_slots) { | ||
631 | /* hack for fat_ioctl_filldir() */ | ||
632 | struct fat_ioctl_filldir_callback *p = dirent; | ||
633 | |||
634 | p->longname = fill_name; | ||
635 | p->long_len = fill_len; | ||
636 | p->shortname = bufname; | ||
637 | p->short_len = i; | ||
638 | fill_name = NULL; | ||
639 | fill_len = 0; | ||
640 | } else { | ||
641 | fill_name = bufname; | ||
642 | fill_len = i; | ||
643 | } | ||
644 | |||
645 | start_filldir: | ||
646 | lpos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry); | ||
596 | if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) | 647 | if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) |
597 | inum = inode->i_ino; | 648 | inum = inode->i_ino; |
598 | else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { | 649 | else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { |
@@ -607,49 +658,17 @@ parse_record: | |||
607 | inum = iunique(sb, MSDOS_ROOT_INO); | 658 | inum = iunique(sb, MSDOS_ROOT_INO); |
608 | } | 659 | } |
609 | 660 | ||
610 | if (isvfat) { | ||
611 | bufuname[j] = 0x0000; | ||
612 | i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname)) | ||
613 | : uni16_to_x8(bufname, bufuname, sizeof(bufname), uni_xlate, nls_io); | ||
614 | } | ||
615 | |||
616 | fill_name = bufname; | ||
617 | fill_len = i; | ||
618 | if (!short_only && long_slots) { | ||
619 | /* convert the unicode long name. 261 is maximum size | ||
620 | * of unicode buffer. (13 * slots + nul) */ | ||
621 | void *longname = unicode + 261; | ||
622 | int buf_size = PATH_MAX - (261 * sizeof(unicode[0])); | ||
623 | int long_len = utf8 | ||
624 | ? utf8_wcstombs(longname, unicode, buf_size) | ||
625 | : uni16_to_x8(longname, unicode, buf_size, uni_xlate, nls_io); | ||
626 | |||
627 | if (!both) { | ||
628 | fill_name = longname; | ||
629 | fill_len = long_len; | ||
630 | } else { | ||
631 | /* hack for fat_ioctl_filldir() */ | ||
632 | struct fat_ioctl_filldir_callback *p = dirent; | ||
633 | |||
634 | p->longname = longname; | ||
635 | p->long_len = long_len; | ||
636 | p->shortname = bufname; | ||
637 | p->short_len = i; | ||
638 | fill_name = NULL; | ||
639 | fill_len = 0; | ||
640 | } | ||
641 | } | ||
642 | if (filldir(dirent, fill_name, fill_len, *furrfu, inum, | 661 | if (filldir(dirent, fill_name, fill_len, *furrfu, inum, |
643 | (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) | 662 | (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) |
644 | goto FillFailed; | 663 | goto fill_failed; |
645 | 664 | ||
646 | RecEnd: | 665 | record_end: |
647 | furrfu = &lpos; | 666 | furrfu = &lpos; |
648 | filp->f_pos = cpos; | 667 | filp->f_pos = cpos; |
649 | goto GetNew; | 668 | goto get_new; |
650 | EODir: | 669 | end_of_dir: |
651 | filp->f_pos = cpos; | 670 | filp->f_pos = cpos; |
652 | FillFailed: | 671 | fill_failed: |
653 | brelse(bh); | 672 | brelse(bh); |
654 | if (unicode) | 673 | if (unicode) |
655 | __putname(unicode); | 674 | __putname(unicode); |
@@ -715,7 +734,7 @@ efault: \ | |||
715 | return -EFAULT; \ | 734 | return -EFAULT; \ |
716 | } | 735 | } |
717 | 736 | ||
718 | FAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, dirent) | 737 | FAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, __fat_dirent) |
719 | 738 | ||
720 | static int fat_ioctl_readdir(struct inode *inode, struct file *filp, | 739 | static int fat_ioctl_readdir(struct inode *inode, struct file *filp, |
721 | void __user *dirent, filldir_t filldir, | 740 | void __user *dirent, filldir_t filldir, |
@@ -741,7 +760,7 @@ static int fat_ioctl_readdir(struct inode *inode, struct file *filp, | |||
741 | static int fat_dir_ioctl(struct inode *inode, struct file *filp, | 760 | static int fat_dir_ioctl(struct inode *inode, struct file *filp, |
742 | unsigned int cmd, unsigned long arg) | 761 | unsigned int cmd, unsigned long arg) |
743 | { | 762 | { |
744 | struct dirent __user *d1 = (struct dirent __user *)arg; | 763 | struct __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg; |
745 | int short_only, both; | 764 | int short_only, both; |
746 | 765 | ||
747 | switch (cmd) { | 766 | switch (cmd) { |
@@ -757,7 +776,7 @@ static int fat_dir_ioctl(struct inode *inode, struct file *filp, | |||
757 | return fat_generic_ioctl(inode, filp, cmd, arg); | 776 | return fat_generic_ioctl(inode, filp, cmd, arg); |
758 | } | 777 | } |
759 | 778 | ||
760 | if (!access_ok(VERIFY_WRITE, d1, sizeof(struct dirent[2]))) | 779 | if (!access_ok(VERIFY_WRITE, d1, sizeof(struct __fat_dirent[2]))) |
761 | return -EFAULT; | 780 | return -EFAULT; |
762 | /* | 781 | /* |
763 | * Yes, we don't need this put_user() absolutely. However old | 782 | * Yes, we don't need this put_user() absolutely. However old |
@@ -1082,7 +1101,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) | |||
1082 | goto error_free; | 1101 | goto error_free; |
1083 | } | 1102 | } |
1084 | 1103 | ||
1085 | fat_date_unix2dos(ts->tv_sec, &time, &date); | 1104 | fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); |
1086 | 1105 | ||
1087 | de = (struct msdos_dir_entry *)bhs[0]->b_data; | 1106 | de = (struct msdos_dir_entry *)bhs[0]->b_data; |
1088 | /* filling the new directory slots ("." and ".." entries) */ | 1107 | /* filling the new directory slots ("." and ".." entries) */ |
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 46a4508ffd2e..23676f9d79ce 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c | |||
@@ -382,17 +382,20 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) | |||
382 | inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) | 382 | inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) |
383 | & ~((loff_t)sbi->cluster_size - 1)) >> 9; | 383 | & ~((loff_t)sbi->cluster_size - 1)) >> 9; |
384 | inode->i_mtime.tv_sec = | 384 | inode->i_mtime.tv_sec = |
385 | date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date)); | 385 | date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date), |
386 | sbi->options.tz_utc); | ||
386 | inode->i_mtime.tv_nsec = 0; | 387 | inode->i_mtime.tv_nsec = 0; |
387 | if (sbi->options.isvfat) { | 388 | if (sbi->options.isvfat) { |
388 | int secs = de->ctime_cs / 100; | 389 | int secs = de->ctime_cs / 100; |
389 | int csecs = de->ctime_cs % 100; | 390 | int csecs = de->ctime_cs % 100; |
390 | inode->i_ctime.tv_sec = | 391 | inode->i_ctime.tv_sec = |
391 | date_dos2unix(le16_to_cpu(de->ctime), | 392 | date_dos2unix(le16_to_cpu(de->ctime), |
392 | le16_to_cpu(de->cdate)) + secs; | 393 | le16_to_cpu(de->cdate), |
394 | sbi->options.tz_utc) + secs; | ||
393 | inode->i_ctime.tv_nsec = csecs * 10000000; | 395 | inode->i_ctime.tv_nsec = csecs * 10000000; |
394 | inode->i_atime.tv_sec = | 396 | inode->i_atime.tv_sec = |
395 | date_dos2unix(0, le16_to_cpu(de->adate)); | 397 | date_dos2unix(0, le16_to_cpu(de->adate), |
398 | sbi->options.tz_utc); | ||
396 | inode->i_atime.tv_nsec = 0; | 399 | inode->i_atime.tv_nsec = 0; |
397 | } else | 400 | } else |
398 | inode->i_ctime = inode->i_atime = inode->i_mtime; | 401 | inode->i_ctime = inode->i_atime = inode->i_mtime; |
@@ -591,11 +594,14 @@ retry: | |||
591 | raw_entry->attr = fat_attr(inode); | 594 | raw_entry->attr = fat_attr(inode); |
592 | raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); | 595 | raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); |
593 | raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); | 596 | raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); |
594 | fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date); | 597 | fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, |
598 | &raw_entry->date, sbi->options.tz_utc); | ||
595 | if (sbi->options.isvfat) { | 599 | if (sbi->options.isvfat) { |
596 | __le16 atime; | 600 | __le16 atime; |
597 | fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate); | 601 | fat_date_unix2dos(inode->i_ctime.tv_sec, &raw_entry->ctime, |
598 | fat_date_unix2dos(inode->i_atime.tv_sec,&atime,&raw_entry->adate); | 602 | &raw_entry->cdate, sbi->options.tz_utc); |
603 | fat_date_unix2dos(inode->i_atime.tv_sec, &atime, | ||
604 | &raw_entry->adate, sbi->options.tz_utc); | ||
599 | raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 + | 605 | raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 + |
600 | inode->i_ctime.tv_nsec / 10000000; | 606 | inode->i_ctime.tv_nsec / 10000000; |
601 | } | 607 | } |
@@ -836,6 +842,8 @@ static int fat_show_options(struct seq_file *m, struct vfsmount *mnt) | |||
836 | } | 842 | } |
837 | if (sbi->options.flush) | 843 | if (sbi->options.flush) |
838 | seq_puts(m, ",flush"); | 844 | seq_puts(m, ",flush"); |
845 | if (opts->tz_utc) | ||
846 | seq_puts(m, ",tz=UTC"); | ||
839 | 847 | ||
840 | return 0; | 848 | return 0; |
841 | } | 849 | } |
@@ -848,7 +856,7 @@ enum { | |||
848 | Opt_charset, Opt_shortname_lower, Opt_shortname_win95, | 856 | Opt_charset, Opt_shortname_lower, Opt_shortname_win95, |
849 | Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, | 857 | Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, |
850 | Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, | 858 | Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, |
851 | Opt_obsolate, Opt_flush, Opt_err, | 859 | Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err, |
852 | }; | 860 | }; |
853 | 861 | ||
854 | static match_table_t fat_tokens = { | 862 | static match_table_t fat_tokens = { |
@@ -883,6 +891,7 @@ static match_table_t fat_tokens = { | |||
883 | {Opt_obsolate, "cvf_options=%100s"}, | 891 | {Opt_obsolate, "cvf_options=%100s"}, |
884 | {Opt_obsolate, "posix"}, | 892 | {Opt_obsolate, "posix"}, |
885 | {Opt_flush, "flush"}, | 893 | {Opt_flush, "flush"}, |
894 | {Opt_tz_utc, "tz=UTC"}, | ||
886 | {Opt_err, NULL}, | 895 | {Opt_err, NULL}, |
887 | }; | 896 | }; |
888 | static match_table_t msdos_tokens = { | 897 | static match_table_t msdos_tokens = { |
@@ -947,10 +956,11 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, | |||
947 | opts->utf8 = opts->unicode_xlate = 0; | 956 | opts->utf8 = opts->unicode_xlate = 0; |
948 | opts->numtail = 1; | 957 | opts->numtail = 1; |
949 | opts->usefree = opts->nocase = 0; | 958 | opts->usefree = opts->nocase = 0; |
959 | opts->tz_utc = 0; | ||
950 | *debug = 0; | 960 | *debug = 0; |
951 | 961 | ||
952 | if (!options) | 962 | if (!options) |
953 | return 0; | 963 | goto out; |
954 | 964 | ||
955 | while ((p = strsep(&options, ",")) != NULL) { | 965 | while ((p = strsep(&options, ",")) != NULL) { |
956 | int token; | 966 | int token; |
@@ -1036,6 +1046,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, | |||
1036 | case Opt_flush: | 1046 | case Opt_flush: |
1037 | opts->flush = 1; | 1047 | opts->flush = 1; |
1038 | break; | 1048 | break; |
1049 | case Opt_tz_utc: | ||
1050 | opts->tz_utc = 1; | ||
1051 | break; | ||
1039 | 1052 | ||
1040 | /* msdos specific */ | 1053 | /* msdos specific */ |
1041 | case Opt_dots: | 1054 | case Opt_dots: |
@@ -1104,10 +1117,13 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, | |||
1104 | return -EINVAL; | 1117 | return -EINVAL; |
1105 | } | 1118 | } |
1106 | } | 1119 | } |
1120 | |||
1121 | out: | ||
1107 | /* UTF-8 doesn't provide FAT semantics */ | 1122 | /* UTF-8 doesn't provide FAT semantics */ |
1108 | if (!strcmp(opts->iocharset, "utf8")) { | 1123 | if (!strcmp(opts->iocharset, "utf8")) { |
1109 | printk(KERN_ERR "FAT: utf8 is not a recommended IO charset" | 1124 | printk(KERN_ERR "FAT: utf8 is not a recommended IO charset" |
1110 | " for FAT filesystems, filesystem will be case sensitive!\n"); | 1125 | " for FAT filesystems, filesystem will be " |
1126 | "case sensitive!\n"); | ||
1111 | } | 1127 | } |
1112 | 1128 | ||
1113 | /* If user doesn't specify allow_utime, it's initialized from dmask. */ | 1129 | /* If user doesn't specify allow_utime, it's initialized from dmask. */ |
diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 61f23511eacf..79fb98ad36d4 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c | |||
@@ -142,7 +142,7 @@ static int day_n[] = { | |||
142 | }; | 142 | }; |
143 | 143 | ||
144 | /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ | 144 | /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ |
145 | int date_dos2unix(unsigned short time, unsigned short date) | 145 | int date_dos2unix(unsigned short time, unsigned short date, int tz_utc) |
146 | { | 146 | { |
147 | int month, year, secs; | 147 | int month, year, secs; |
148 | 148 | ||
@@ -156,16 +156,18 @@ int date_dos2unix(unsigned short time, unsigned short date) | |||
156 | ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && | 156 | ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && |
157 | month < 2 ? 1 : 0)+3653); | 157 | month < 2 ? 1 : 0)+3653); |
158 | /* days since 1.1.70 plus 80's leap day */ | 158 | /* days since 1.1.70 plus 80's leap day */ |
159 | secs += sys_tz.tz_minuteswest*60; | 159 | if (!tz_utc) |
160 | secs += sys_tz.tz_minuteswest*60; | ||
160 | return secs; | 161 | return secs; |
161 | } | 162 | } |
162 | 163 | ||
163 | /* Convert linear UNIX date to a MS-DOS time/date pair. */ | 164 | /* Convert linear UNIX date to a MS-DOS time/date pair. */ |
164 | void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date) | 165 | void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc) |
165 | { | 166 | { |
166 | int day, year, nl_day, month; | 167 | int day, year, nl_day, month; |
167 | 168 | ||
168 | unix_date -= sys_tz.tz_minuteswest*60; | 169 | if (!tz_utc) |
170 | unix_date -= sys_tz.tz_minuteswest*60; | ||
169 | 171 | ||
170 | /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ | 172 | /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ |
171 | if (unix_date < 315532800) | 173 | if (unix_date < 315532800) |