1 /**
2 * logfile.c - NTFS journal handling. Part of the Linux-NTFS project.
3 *
4 * Copyright (c) 2002-2005 Anton Altaparmakov
5 * Copyright (c) 2005 Yura Pakhuchiy
6 *
7 * This program/include file is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as published
9 * by the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program/include file is distributed in the hope that it will be
13 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program (in the main directory of the Linux-NTFS
19 * distribution in the file COPYING); if not, write to the Free Software
20 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #ifdef HAVE_STDLIB_H
28 #include <stdlib.h>
29 #endif
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33 #ifdef HAVE_ERRNO_H
34 #include <errno.h>
35 #endif
36
37 #include "compat.h"
38 #include "attrib.h"
39 #include "debug.h"
40 #include "logfile.h"
41 #include "volume.h"
42 #include "mst.h"
43 #include "logging.h"
44
45 /**
46 * ntfs_check_restart_page_header - check the page header for consistency
47 * @rp: restart page header to check
48 * @pos: position in logfile at which the restart page header resides
49 *
50 * Check the restart page header @rp for consistency and return TRUE if it is
51 * consistent and FALSE otherwise.
52 *
53 * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
54 * require the full restart page.
55 */
56 static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos)
57 {
58 u32 logfile_system_page_size, logfile_log_page_size;
59 u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
60 BOOL have_usa = TRUE;
61
62 ntfs_log_trace("Entering.\n");
63 /*
64 * If the system or log page sizes are smaller than the ntfs block size
65 * or either is not a power of 2 we cannot handle this log file.
66 */
67 logfile_system_page_size = le32_to_cpu(rp->system_page_size);
68 logfile_log_page_size = le32_to_cpu(rp->log_page_size);
69 if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
70 logfile_log_page_size < NTFS_BLOCK_SIZE ||
71 logfile_system_page_size &
72 (logfile_system_page_size - 1) ||
73 logfile_log_page_size & (logfile_log_page_size - 1)) {
74 ntfs_log_error("$LogFile uses unsupported page size.\n");
75 return FALSE;
76 }
77 /*
78 * We must be either at !pos (1st restart page) or at pos = system page
79 * size (2nd restart page).
80 */
81 if (pos && pos != logfile_system_page_size) {
82 ntfs_log_error("Found restart area in incorrect "
83 "position in $LogFile.\n");
84 return FALSE;
85 }
86 /* We only know how to handle version 1.1. */
87 if (sle16_to_cpu(rp->major_ver) != 1 ||
88 sle16_to_cpu(rp->minor_ver) != 1) {
89 ntfs_log_error("$LogFile version %i.%i is not "
90 "supported. (This driver supports version "
91 "1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver),
92 (int)sle16_to_cpu(rp->minor_ver));
93 return FALSE;
94 }
95 /*
96 * If chkdsk has been run the restart page may not be protected by an
97 * update sequence array.
98 */
99 if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) {
100 have_usa = FALSE;
101 goto skip_usa_checks;
102 }
103 /* Verify the size of the update sequence array. */
104 usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
105 if (usa_count != le16_to_cpu(rp->usa_count)) {
106 ntfs_log_error("$LogFile restart page specifies "
107 "inconsistent update sequence array count.\n");
108 return FALSE;
109 }
110 /* Verify the position of the update sequence array. */
111 usa_ofs = le16_to_cpu(rp->usa_ofs);
112 usa_end = usa_ofs + usa_count * sizeof(u16);
113 if (usa_ofs < sizeof(RESTART_PAGE_HEADER) ||
114 usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
115 ntfs_log_error("$LogFile restart page specifies "
116 "inconsistent update sequence array offset.\n");
117 return FALSE;
118 }
119 skip_usa_checks:
120 /*
121 * Verify the position of the restart area. It must be:
122 * - aligned to 8-byte boundary,
123 * - after the update sequence array, and
124 * - within the system page size.
125 */
126 ra_ofs = le16_to_cpu(rp->restart_area_offset);
127 if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end :
128 ra_ofs < sizeof(RESTART_PAGE_HEADER)) ||
129 ra_ofs > logfile_system_page_size) {
130 ntfs_log_error("$LogFile restart page specifies "
131 "inconsistent restart area offset.\n");
132 return FALSE;
133 }
134 /*
135 * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
136 * set.
137 */
138 if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
139 ntfs_log_error("$LogFile restart page is not modified "
140 "by chkdsk but a chkdsk LSN is specified.\n");
141 return FALSE;
142 }
143 ntfs_log_trace("Done.\n");
144 return TRUE;
145 }
146
147 /**
148 * ntfs_check_restart_area - check the restart area for consistency
149 * @rp: restart page whose restart area to check
150 *
151 * Check the restart area of the restart page @rp for consistency and return
152 * TRUE if it is consistent and FALSE otherwise.
153 *
154 * This function assumes that the restart page header has already been
155 * consistency checked.
156 *
157 * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
158 * require the full restart page.
159 */
160 static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp)
161 {
162 u64 file_size;
163 RESTART_AREA *ra;
164 u16 ra_ofs, ra_len, ca_ofs;
165 u8 fs_bits;
166
167 ntfs_log_trace("Entering.\n");
168 ra_ofs = le16_to_cpu(rp->restart_area_offset);
169 ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
170 /*
171 * Everything before ra->file_size must be before the first word
172 * protected by an update sequence number. This ensures that it is
173 * safe to access ra->client_array_offset.
174 */
175 if (ra_ofs + offsetof(RESTART_AREA, file_size) >
176 NTFS_BLOCK_SIZE - sizeof(u16)) {
177 ntfs_log_error("$LogFile restart area specifies "
178 "inconsistent file offset.\n");
179 return FALSE;
180 }
181 /*
182 * Now that we can access ra->client_array_offset, make sure everything
183 * up to the log client array is before the first word protected by an
184 * update sequence number. This ensures we can access all of the
185 * restart area elements safely. Also, the client array offset must be
186 * aligned to an 8-byte boundary.
187 */
188 ca_ofs = le16_to_cpu(ra->client_array_offset);
189 if (((ca_ofs + 7) & ~7) != ca_ofs ||
190 ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE -
191 sizeof(u16))) {
192 ntfs_log_error("$LogFile restart area specifies "
193 "inconsistent client array offset.\n");
194 return FALSE;
195 }
196 /*
197 * The restart area must end within the system page size both when
198 * calculated manually and as specified by ra->restart_area_length.
199 * Also, the calculated length must not exceed the specified length.
200 */
201 ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
202 sizeof(LOG_CLIENT_RECORD);
203 if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) ||
204 (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) >
205 le32_to_cpu(rp->system_page_size) ||
206 ra_len > le16_to_cpu(ra->restart_area_length)) {
207 ntfs_log_error("$LogFile restart area is out of bounds "
208 "of the system page size specified by the "
209 "restart page header and/or the specified "
210 "restart area length is inconsistent.\n");
211 return FALSE;
212 }
213 /*
214 * The ra->client_free_list and ra->client_in_use_list must be either
215 * LOGFILE_NO_CLIENT or less than ra->log_clients or they are
216 * overflowing the client array.
217 */
218 if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
219 le16_to_cpu(ra->client_free_list) >=
220 le16_to_cpu(ra->log_clients)) ||
221 (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
222 le16_to_cpu(ra->client_in_use_list) >=
223 le16_to_cpu(ra->log_clients))) {
224 ntfs_log_error("$LogFile restart area specifies "
225 "overflowing client free and/or in use lists.\n");
226 return FALSE;
227 }
228 /*
229 * Check ra->seq_number_bits against ra->file_size for consistency.
230 * We cannot just use ffs() because the file size is not a power of 2.
231 */
232 file_size = (u64)sle64_to_cpu(ra->file_size);
233 fs_bits = 0;
234 while (file_size) {
235 file_size >>= 1;
236 fs_bits++;
237 }
238 if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) {
239 ntfs_log_error("$LogFile restart area specifies "
240 "inconsistent sequence number bits.\n");
241 return FALSE;
242 }
243 /* The log record header length must be a multiple of 8. */
244 if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
245 le16_to_cpu(ra->log_record_header_length)) {
246 ntfs_log_error("$LogFile restart area specifies "
247 "inconsistent log record header length.\n");
248 return FALSE;
249 }
250 /* Ditto for the log page data offset. */
251 if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
252 le16_to_cpu(ra->log_page_data_offset)) {
253 ntfs_log_error("$LogFile restart area specifies "
254 "inconsistent log page data offset.\n");
255 return FALSE;
256 }
257 ntfs_log_trace("Done.\n");
258 return TRUE;
259 }
260
261 /**
262 * ntfs_check_log_client_array - check the log client array for consistency
263 * @rp: restart page whose log client array to check
264 *
265 * Check the log client array of the restart page @rp for consistency and
266 * return TRUE if it is consistent and FALSE otherwise.
267 *
268 * This function assumes that the restart page header and the restart area have
269 * already been consistency checked.
270 *
271 * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
272 * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
273 * restart page and the page must be multi sector transfer deprotected.
274 */
275 static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
276 {
277 RESTART_AREA *ra;
278 LOG_CLIENT_RECORD *ca, *cr;
279 u16 nr_clients, idx;
280 BOOL in_free_list, idx_is_first;
281
282 ntfs_log_trace("Entering.\n");
283 ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
284 ca = (LOG_CLIENT_RECORD*)((u8*)ra +
285 le16_to_cpu(ra->client_array_offset));
286 /*
287 * Check the ra->client_free_list first and then check the
288 * ra->client_in_use_list. Check each of the log client records in
289 * each of the lists and check that the array does not overflow the
290 * ra->log_clients value. Also keep track of the number of records
291 * visited as there cannot be more than ra->log_clients records and
292 * that way we detect eventual loops in within a list.
293 */
294 nr_clients = le16_to_cpu(ra->log_clients);
295 idx = le16_to_cpu(ra->client_free_list);
296 in_free_list = TRUE;
297 check_list:
298 for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
299 idx = le16_to_cpu(cr->next_client)) {
300 if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
301 goto err_out;
302 /* Set @cr to the current log client record. */
303 cr = ca + idx;
304 /* The first log client record must not have a prev_client. */
305 if (idx_is_first) {
306 if (cr->prev_client != LOGFILE_NO_CLIENT)
307 goto err_out;
308 idx_is_first = FALSE;
309 }
310 }
311 /* Switch to and check the in use list if we just did the free list. */
312 if (in_free_list) {
313 in_free_list = FALSE;
314 idx = le16_to_cpu(ra->client_in_use_list);
315 goto check_list;
316 }
317 ntfs_log_trace("Done.\n");
318 return TRUE;
319 err_out:
320 ntfs_log_error("$LogFile log client array is corrupt.\n");
321 return FALSE;
322 }
323
324 /**
325 * ntfs_check_and_load_restart_page - check the restart page for consistency
326 * @log_na: opened ntfs attribute for journal $LogFile
327 * @rp: restart page to check
328 * @pos: position in @log_na at which the restart page resides
329 * @wrp: [OUT] copy of the multi sector transfer deprotected restart page
330 * @lsn: [OUT] set to the current logfile lsn on success
331 *
332 * Check the restart page @rp for consistency and return 0 if it is consistent
333 * and errno otherwise. The restart page may have been modified by chkdsk in
334 * which case its magic is CHKD instead of RSTR.
335 *
336 * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
337 * require the full restart page.
338 *
339 * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
340 * copy of the complete multi sector transfer deprotected page. On failure,
341 * *@wrp is undefined.
342 *
343 * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current
344 * logfile lsn according to this restart page. On failure, *@lsn is undefined.
345 *
346 * The following error codes are defined:
347 * EINVAL - The restart page is inconsistent.
348 * ENOMEM - Not enough memory to load the restart page.
349 * EIO - Failed to reading from $LogFile.
350 */
351 static int ntfs_check_and_load_restart_page(ntfs_attr *log_na,
352 RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
353 LSN *lsn)
354 {
355 RESTART_AREA *ra;
356 RESTART_PAGE_HEADER *trp;
357 int err;
358
359 ntfs_log_trace("Entering.\n");
360 /* Check the restart page header for consistency. */
361 if (!ntfs_check_restart_page_header(rp, pos)) {
362 /* Error output already done inside the function. */
363 return EINVAL;
364 }
365 /* Check the restart area for consistency. */
366 if (!ntfs_check_restart_area(rp)) {
367 /* Error output already done inside the function. */
368 return EINVAL;
369 }
370 ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
371 /*
372 * Allocate a buffer to store the whole restart page so we can multi
373 * sector transfer deprotect it.
374 */
375 trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
376 if (!trp)
377 return ENOMEM;
378 /*
379 * Read the whole of the restart page into the buffer. If it fits
380 * completely inside @rp, just copy it from there. Otherwise read it
381 * from disk.
382 */
383 if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE)
384 memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
385 else if (ntfs_attr_pread(log_na, pos,
386 le32_to_cpu(rp->system_page_size), trp) !=
387 le32_to_cpu(rp->system_page_size)) {
388 err = errno;
389 ntfs_log_error("Failed to read whole restart page into the "
390 "buffer.\n");
391 if (err != ENOMEM)
392 err = EIO;
393 goto err_out;
394 }
395 /*
396 * Perform the multi sector transfer deprotection on the buffer if the
397 * restart page is protected.
398 */
399 if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count))
400 && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp,
401 le32_to_cpu(rp->system_page_size))) {
402 /*
403 * A multi sector tranfer error was detected. We only need to
404 * abort if the restart page contents exceed the multi sector
405 * transfer fixup of the first sector.
406 */
407 if (le16_to_cpu(rp->restart_area_offset) +
408 le16_to_cpu(ra->restart_area_length) >
409 NTFS_BLOCK_SIZE - (int)sizeof(u16)) {
410 ntfs_log_error("Multi sector transfer error "
411 "detected in $LogFile restart page.\n");
412 err = EINVAL;
413 goto err_out;
414 }
415 }
416 /*
417 * If the restart page is modified by chkdsk or there are no active
418 * logfile clients, the logfile is consistent. Otherwise, need to
419 * check the log client records for consistency, too.
420 */
421 err = 0;
422 if (ntfs_is_rstr_record(rp->magic) &&
423 ra->client_in_use_list != LOGFILE_NO_CLIENT) {
424 if (!ntfs_check_log_client_array(trp)) {
425 err = EINVAL;
426 goto err_out;
427 }
428 }
429 if (lsn) {
430 if (ntfs_is_rstr_record(rp->magic))
431 *lsn = sle64_to_cpu(ra->current_lsn);
432 else /* if (ntfs_is_chkd_record(rp->magic)) */
433 *lsn = sle64_to_cpu(rp->chkdsk_lsn);
434 }
435 ntfs_log_trace("Done.\n");
436 if (wrp)
437 *wrp = trp;
438 else {
439 err_out:
440 free(trp);
441 }
442 return err;
443 }
444
445 /**
446 * ntfs_check_logfile - check in the journal if the volume is consistent
447 * @log_na: ntfs attribute of loaded journal $LogFile to check
448 * @rp: [OUT] on success this is a copy of the current restart page
449 *
450 * Check the $LogFile journal for consistency and return TRUE if it is
451 * consistent and FALSE if not. On success, the current restart page is
452 * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it.
453 *
454 * At present we only check the two restart pages and ignore the log record
455 * pages.
456 *
457 * Note that the MstProtected flag is not set on the $LogFile inode and hence
458 * when reading pages they are not deprotected. This is because we do not know
459 * if the $LogFile was created on a system with a different page size to ours
460 * yet and mst deprotection would fail if our page size is smaller.
461 */
462 BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
463 {
464 s64 size, pos;
465 LSN rstr1_lsn, rstr2_lsn;
466 ntfs_volume *vol = log_na->ni->vol;
467 u8 *kaddr = NULL;
468 RESTART_PAGE_HEADER *rstr1_ph = NULL;
469 RESTART_PAGE_HEADER *rstr2_ph = NULL;
470 int log_page_size, log_page_mask, err;
471 BOOL logfile_is_empty = TRUE;
472 u8 log_page_bits;
473
474 ntfs_log_trace("Entering.\n");
475 /* An empty $LogFile must have been clean before it got emptied. */
476 if (NVolLogFileEmpty(vol))
477 goto is_empty;
478 size = log_na->data_size;
479 /* Make sure the file doesn't exceed the maximum allowed size. */
480 if (size > (s64)MaxLogFileSize)
481 size = MaxLogFileSize;
482 log_page_size = DefaultLogPageSize;
483 log_page_mask = log_page_size - 1;
484 /*
485 * Use generic_ffs() instead of ffs() to enable the compiler to
486 * optimize log_page_size and log_page_bits into constants.
487 */
488 log_page_bits = ffs(log_page_size) - 1;
489 size &= ~(log_page_size - 1);
490
491 /*
492 * Ensure the log file is big enough to store at least the two restart
493 * pages and the minimum number of log record pages.
494 */
495 if (size < log_page_size * 2 || (size - log_page_size * 2) >>
496 log_page_bits < MinLogRecordPages) {
497 ntfs_log_error("$LogFile is too small.\n");
498 return FALSE;
499 }
500 /* Allocate memory for restart page. */
501 kaddr = ntfs_malloc(NTFS_BLOCK_SIZE);
502 if (!kaddr)
503 return FALSE;
504 /*
505 * Read through the file looking for a restart page. Since the restart
506 * page header is at the beginning of a page we only need to search at
507 * what could be the beginning of a page (for each page size) rather
508 * than scanning the whole file byte by byte. If all potential places
509 * contain empty and uninitialized records, the log file can be assumed
510 * to be empty.
511 */
512 for (pos = 0; pos < size; pos <<= 1) {
513 /*
514 * Read first NTFS_BLOCK_SIZE bytes of potential restart page.
515 */
516 if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) !=
517 NTFS_BLOCK_SIZE) {
518 ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE "
519 "bytes of potential restart page.\n");
520 goto err_out;
521 }
522
523 /*
524 * A non-empty block means the logfile is not empty while an
525 * empty block after a non-empty block has been encountered
526 * means we are done.
527 */
528 if (!ntfs_is_empty_recordp((le32*)kaddr))
529 logfile_is_empty = FALSE;
530 else if (!logfile_is_empty)
531 break;
532 /*
533 * A log record page means there cannot be a restart page after
534 * this so no need to continue searching.
535 */
536 if (ntfs_is_rcrd_recordp((le32*)kaddr))
537 break;
538 /* If not a (modified by chkdsk) restart page, continue. */
539 if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
540 !ntfs_is_chkd_recordp((le32*)kaddr)) {
541 if (!pos)
542 pos = NTFS_BLOCK_SIZE >> 1;
543 continue;
544 }
545 /*
546 * Check the (modified by chkdsk) restart page for consistency
547 * and get a copy of the complete multi sector transfer
548 * deprotected restart page.
549 */
550 err = ntfs_check_and_load_restart_page(log_na,
551 (RESTART_PAGE_HEADER*)kaddr, pos,
552 !rstr1_ph ? &rstr1_ph : &rstr2_ph,
553 !rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
554 if (!err) {
555 /*
556 * If we have now found the first (modified by chkdsk)
557 * restart page, continue looking for the second one.
558 */
559 if (!pos) {
560 pos = NTFS_BLOCK_SIZE >> 1;
561 continue;
562 }
563 /*
564 * We have now found the second (modified by chkdsk)
565 * restart page, so we can stop looking.
566 */
567 break;
568 }
569 /*
570 * Error output already done inside the function. Note, we do
571 * not abort if the restart page was invalid as we might still
572 * find a valid one further in the file.
573 */
574 if (err != EINVAL)
575 goto err_out;
576 /* Continue looking. */
577 if (!pos)
578 pos = NTFS_BLOCK_SIZE >> 1;
579 }
580 if (kaddr) {
581 free(kaddr);
582 kaddr = NULL;
583 }
584 if (logfile_is_empty) {
585 NVolSetLogFileEmpty(vol);
586 is_empty:
587 ntfs_log_trace("Done. ($LogFile is empty.)\n");
588 return TRUE;
589 }
590 if (!rstr1_ph) {
591 if (rstr2_ph)
592 ntfs_log_error("BUG: rstr2_ph isn't NULL!\n");
593 ntfs_log_error("Did not find any restart pages in "
594 "$LogFile and it was not empty.\n");
595 return FALSE;
596 }
597 /* If both restart pages were found, use the more recent one. */
598 if (rstr2_ph) {
599 /*
600 * If the second restart area is more recent, switch to it.
601 * Otherwise just throw it away.
602 */
603 if (rstr2_lsn > rstr1_lsn) {
604 ntfs_log_debug("Using second restart page as it is more "
605 "recent.\n");
606 free(rstr1_ph);
607 rstr1_ph = rstr2_ph;
608 /* rstr1_lsn = rstr2_lsn; */
609 } else {
610 ntfs_log_debug("Using first restart page as it is more "
611 "recent.\n");
612 free(rstr2_ph);
613 }
614 rstr2_ph = NULL;
615 }
616 /* All consistency checks passed. */
617 if (rp)
618 *rp = rstr1_ph;
619 else
620 free(rstr1_ph);
621 ntfs_log_trace("Done.\n");
622 return TRUE;
623 err_out:
624 free(kaddr);
625 free(rstr1_ph);
626 free(rstr2_ph);
627 return FALSE;
628 }
629
630 /**
631 * ntfs_is_logfile_clean - check in the journal if the volume is clean
632 * @log_na: ntfs attribute of loaded journal $LogFile to check
633 * @rp: copy of the current restart page
634 *
635 * Analyze the $LogFile journal and return TRUE if it indicates the volume was
636 * shutdown cleanly and FALSE if not.
637 *
638 * At present we only look at the two restart pages and ignore the log record
639 * pages. This is a little bit crude in that there will be a very small number
640 * of cases where we think that a volume is dirty when in fact it is clean.
641 * This should only affect volumes that have not been shutdown cleanly but did
642 * not have any pending, non-check-pointed i/o, i.e. they were completely idle
643 * at least for the five seconds preceding the unclean shutdown.
644 *
645 * This function assumes that the $LogFile journal has already been consistency
646 * checked by a call to ntfs_check_logfile() and in particular if the $LogFile
647 * is empty this function requires that NVolLogFileEmpty() is true otherwise an
648 * empty volume will be reported as dirty.
649 */
650 BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp)
651 {
652 RESTART_AREA *ra;
653
654 ntfs_log_trace("Entering.\n");
655 /* An empty $LogFile must have been clean before it got emptied. */
656 if (NVolLogFileEmpty(log_na->ni->vol)) {
657 ntfs_log_trace("Done. ($LogFile is empty.)\n");
658 return TRUE;
659 }
660 if (!rp) {
661 ntfs_log_error("Restart page header is NULL.\n");
662 return FALSE;
663 }
664 if (!ntfs_is_rstr_record(rp->magic) &&
665 !ntfs_is_chkd_record(rp->magic)) {
666 ntfs_log_error("Restart page buffer is invalid. This is "
667 "probably a bug in that the $LogFile should "
668 "have been consistency checked before calling "
669 "this function.\n");
670 return FALSE;
671 }
672
673 ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
674 /*
675 * If the $LogFile has active clients, i.e. it is open, and we do not
676 * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
677 * we assume there was an unclean shutdown.
678 */
679 if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
680 !(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
681 ntfs_log_debug("Done. $LogFile indicates a dirty shutdown.\n");
682 return FALSE;
683 }
684 /* $LogFile indicates a clean shutdown. */
685 ntfs_log_trace("Done. $LogFile indicates a clean shutdown.\n");
686 return TRUE;
687 }
688
689 /**
690 * ntfs_empty_logfile - empty the contents of the $LogFile journal
691 * @na: ntfs attribute of journal $LogFile to empty
692 *
693 * Empty the contents of the $LogFile journal @na and return 0 on success and
694 * -1 on error.
695 *
696 * This function assumes that the $LogFile journal has already been consistency
697 * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
698 * has been used to ensure that the $LogFile is clean.
699 */
700 int ntfs_empty_logfile(ntfs_attr *na)
701 {
702 s64 len, pos, count;
703 char buf[NTFS_BUF_SIZE];
704 int err;
705
706 ntfs_log_trace("Entering.\n");
707 if (NVolLogFileEmpty(na->ni->vol))
708 goto done;
709
710 /* The $DATA attribute of the $LogFile has to be non-resident. */
711 if (!NAttrNonResident(na)) {
712 err = EIO;
713 ntfs_log_debug("$LogFile $DATA attribute is resident!?!\n");
714 goto io_error_exit;
715 }
716
717 /* Get length of $LogFile contents. */
718 len = na->data_size;
719 if (!len) {
720 ntfs_log_debug("$LogFile has zero length, no disk write "
721 "needed.\n");
722 return 0;
723 }
724
725 /* Read $LogFile until its end. We do this as a check for correct
726 length thus making sure we are decompressing the mapping pairs
727 array correctly and hence writing below is safe as well. */
728 pos = 0;
729 while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE, buf)) > 0)
730 pos += count;
731
732 if (count == -1 || pos != len) {
733 err = errno;
734 ntfs_log_debug("Amount of $LogFile data read does not "
735 "correspond to expected length!\n");
736 if (count != -1)
737 err = EIO;
738 goto io_error_exit;
739 }
740
741 /* Fill the buffer with 0xff's. */
742 memset(buf, -1, NTFS_BUF_SIZE);
743
744 /* Set the $DATA attribute. */
745 pos = 0;
746 while ((count = len - pos) > 0) {
747 if (count > NTFS_BUF_SIZE)
748 count = NTFS_BUF_SIZE;
749
750 if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) {
751 err = errno;
752 ntfs_log_debug("Failed to set the $LogFile attribute "
753 "value.\n");
754 if (count != -1)
755 err = EIO;
756 goto io_error_exit;
757 }
758 pos += count;
759 }
760
761 /* Set the flag so we do not have to do it again on remount. */
762 NVolSetLogFileEmpty(na->ni->vol);
763 done:
764 ntfs_log_trace("Done.\n");
765 return 0;
766 io_error_exit:
767 errno = err;
768 return -1;
769 }