]>
git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - fs/exfat/misc.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Written 1992,1993 by Werner Almesberger
4 * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
5 * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
6 * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
9 #include <linux/time.h>
11 #include <linux/slab.h>
12 #include <linux/buffer_head.h>
14 #include "exfat_raw.h"
18 * exfat_fs_error reports a file system problem that might indicate fa data
19 * corruption/inconsistency. Depending on 'errors' mount option the
20 * panic() is called, or error message is printed FAT and nothing is done,
21 * or filesystem is remounted read-only (default behavior).
22 * In case the file system is remounted read-only, it can be made writable
23 * again by remounting it.
25 void __exfat_fs_error(struct super_block
*sb
, int report
, const char *fmt
, ...)
27 struct exfat_mount_options
*opts
= &EXFAT_SB(sb
)->options
;
35 exfat_err(sb
, "error, %pV", &vaf
);
39 if (opts
->errors
== EXFAT_ERRORS_PANIC
) {
40 panic("exFAT-fs (%s): fs panic from previous error\n",
42 } else if (opts
->errors
== EXFAT_ERRORS_RO
&& !sb_rdonly(sb
)) {
43 sb
->s_flags
|= SB_RDONLY
;
44 exfat_err(sb
, "Filesystem has been set read-only");
49 * exfat_msg() - print preformated EXFAT specific messages.
50 * All logs except what uses exfat_fs_error() should be written by exfat_msg()
52 void exfat_msg(struct super_block
*sb
, const char *level
, const char *fmt
, ...)
60 /* level means KERN_ pacility level */
61 printk("%sexFAT-fs (%s): %pV\n", level
, sb
->s_id
, &vaf
);
65 #define SECS_PER_MIN (60)
66 #define TIMEZONE_SEC(x) ((x) * 15 * SECS_PER_MIN)
68 static void exfat_adjust_tz(struct timespec64
*ts
, u8 tz_off
)
71 ts
->tv_sec
-= TIMEZONE_SEC(tz_off
);
72 else /* 0x40 <= (tz_off & 0x7F) <=0x7F */
73 ts
->tv_sec
+= TIMEZONE_SEC(0x80 - tz_off
);
76 /* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */
77 void exfat_get_entry_time(struct exfat_sb_info
*sbi
, struct timespec64
*ts
,
78 u8 tz
, __le16 time
, __le16 date
, u8 time_cs
)
80 u16 t
= le16_to_cpu(time
);
81 u16 d
= le16_to_cpu(date
);
83 ts
->tv_sec
= mktime64(1980 + (d
>> 9), d
>> 5 & 0x000F, d
& 0x001F,
84 t
>> 11, (t
>> 5) & 0x003F, (t
& 0x001F) << 1);
87 /* time_cs field represent 0 ~ 199cs(1990 ms) */
89 ts
->tv_sec
+= time_cs
/ 100;
90 ts
->tv_nsec
= (time_cs
% 100) * 10 * NSEC_PER_MSEC
;
94 if (tz
& EXFAT_TZ_VALID
)
95 /* Adjust timezone to UTC0. */
96 exfat_adjust_tz(ts
, tz
& ~EXFAT_TZ_VALID
);
98 /* Convert from local time to UTC using time_offset. */
99 ts
->tv_sec
-= sbi
->options
.time_offset
* SECS_PER_MIN
;
102 /* Convert linear UNIX date to a EXFAT time/date pair. */
103 void exfat_set_entry_time(struct exfat_sb_info
*sbi
, struct timespec64
*ts
,
104 u8
*tz
, __le16
*time
, __le16
*date
, u8
*time_cs
)
109 time64_to_tm(ts
->tv_sec
, 0, &tm
);
110 t
= (tm
.tm_hour
<< 11) | (tm
.tm_min
<< 5) | (tm
.tm_sec
>> 1);
111 d
= ((tm
.tm_year
- 80) << 9) | ((tm
.tm_mon
+ 1) << 5) | tm
.tm_mday
;
113 *time
= cpu_to_le16(t
);
114 *date
= cpu_to_le16(d
);
116 /* time_cs field represent 0 ~ 199cs(1990 ms) */
118 *time_cs
= (tm
.tm_sec
& 1) * 100 +
119 ts
->tv_nsec
/ (10 * NSEC_PER_MSEC
);
122 * Record 00h value for OffsetFromUtc field and 1 value for OffsetValid
123 * to indicate that local time and UTC are the same.
125 *tz
= EXFAT_TZ_VALID
;
129 * The timestamp for access_time has double seconds granularity.
130 * (There is no 10msIncrement field for access_time unlike create/modify_time)
131 * atime also has only a 2-second resolution.
133 void exfat_truncate_atime(struct timespec64
*ts
)
135 ts
->tv_sec
= round_down(ts
->tv_sec
, 2);
139 u16
exfat_calc_chksum16(void *data
, int len
, u16 chksum
, int type
)
144 for (i
= 0; i
< len
; i
++, c
++) {
145 if (unlikely(type
== CS_DIR_ENTRY
&& (i
== 2 || i
== 3)))
147 chksum
= ((chksum
<< 15) | (chksum
>> 1)) + *c
;
152 u32
exfat_calc_chksum32(void *data
, int len
, u32 chksum
, int type
)
157 for (i
= 0; i
< len
; i
++, c
++) {
158 if (unlikely(type
== CS_BOOT_SECTOR
&&
159 (i
== 106 || i
== 107 || i
== 112)))
161 chksum
= ((chksum
<< 31) | (chksum
>> 1)) + *c
;
166 void exfat_update_bh(struct buffer_head
*bh
, int sync
)
168 set_buffer_uptodate(bh
);
169 mark_buffer_dirty(bh
);
172 sync_dirty_buffer(bh
);
175 int exfat_update_bhs(struct buffer_head
**bhs
, int nr_bhs
, int sync
)
179 for (i
= 0; i
< nr_bhs
; i
++) {
180 set_buffer_uptodate(bhs
[i
]);
181 mark_buffer_dirty(bhs
[i
]);
183 write_dirty_buffer(bhs
[i
], 0);
186 for (i
= 0; i
< nr_bhs
&& sync
; i
++) {
187 wait_on_buffer(bhs
[i
]);
188 if (!err
&& !buffer_uptodate(bhs
[i
]))
194 void exfat_chain_set(struct exfat_chain
*ec
, unsigned int dir
,
195 unsigned int size
, unsigned char flags
)
202 void exfat_chain_dup(struct exfat_chain
*dup
, struct exfat_chain
*ec
)
204 return exfat_chain_set(dup
, ec
->dir
, ec
->size
, ec
->flags
);