http://linux-ntfs.sourceforge.net/snapshots/ntfsprogs-200307311516.tar.bz2
[ntfsprogs.git] / ntfsprogs / ntfsdump_logfile.c
1 /**
2  * NtfsDump_LogFile - Part of the Linux-NTFS project.
3  *
4  * Copyright (c) 2000-2002 Anton Altaparmakov
5  *
6  * This utility will interpret the contents of the journal ($LogFile) of an
7  * NTFS partition and display the results on stdout. Errors will be output to
8  * stderr.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program (in the main directory of the Linux-NTFS source
22  * in the file COPYING); if not, write to the Free Software Foundation,
23  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 /*
27  * WARNING: This program might not work on architectures which do not allow
28  * unaligned access. For those, the program would need to start using
29  * get/put_unaligned macros (#include <asm/unaligned.h>), but not doing it yet,
30  * since NTFS really mostly applies to ia32 only, which does allow unaligned
31  * accesses. We might not actually have a problem though, since the structs are
32  * defined as being packed so that might be enough for gcc to insert the
33  * correct code.
34  *
35  * If anyone using a non-little endian and/or an aligned access only CPU tries
36  * this program please let me know whether it works or not!
37  *
38  *      Anton Altaparmakov <aia21@cantab.net>
39  */
40
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <string.h>
47
48 #include "types.h"
49 #include "attrib.h"
50 #include "mft.h"
51 #include "disk_io.h"
52 #include "logfile.h"
53 #include "mst.h"
54
55 const char *EXEC_NAME = "NtfsDump_LogFile";
56 const char *EXEC_VERSION = "1.0";
57
58 /**
59  * main
60  */
61 int main(int argc, char **argv)
62 {
63         MFT_RECORD *m = NULL;
64         ATTR_RECORD *a;
65         s64 l;
66         unsigned char *lfd = NULL;
67         ntfs_volume *vol = NULL;
68         ntfs_attr_search_ctx *ctx = NULL;
69         RESTART_PAGE_HEADER *rph;
70         RESTART_AREA *rr;
71         RESTART_CLIENT *cr;
72         RECORD_PAGE_HEADER *rcrd_ph;
73         LOG_RECORD *lr;
74         int pass = 1;
75         int i, lps, client;
76         char zero[4096];
77
78         memset(zero, 0, sizeof(zero));
79         printf("\n");
80         if (argc != 2) {
81                 printf("%s v%s - Interpret and display information about the "
82                        "journal\n($LogFile) of an NTFS volume.\n\n"
83                        /* Generic copyright / disclaimer. */
84                        "Copyright (c) 2000, 2001 Anton Altaparmakov.\n\n"
85                        "%s is free software, released under the GNU "
86                        "General Public License\nand you are welcome to "
87                        "redistribute it under certain conditions.\n"
88                        "%s comes with ABSOLUTELY NO WARRANTY; for details "
89                        "read the GNU\nGeneral Public License to be found "
90                        "in the file COPYING in the main Linux-NTFS\n"
91                        "distribution directory.\n\n"
92                        /* Generic part ends here. */
93                        "Syntax: ntfsdump_logfile partition_or_file_name\n"
94                        "        e.g. ntfsdump_logfile /dev/hda6\n\n",
95                        EXEC_NAME, EXEC_VERSION, EXEC_NAME, EXEC_NAME);
96                 fprintf(stderr, "Error: incorrect syntax\n");
97                 exit(1);
98         }
99         vol = ntfs_mount(argv[1], MS_RDONLY);
100         if (!vol) {
101                 perror("ntfs_mount(MS_RDONLY) failed");
102                 exit(1);
103         }
104         /* Check NTFS version is ok for us. */
105         printf("\nNTFS volume version is %i.%i.\n", vol->major_ver,
106                                                   vol->minor_ver);
107         switch (vol->major_ver) {
108                 case 1:
109                         if (vol->minor_ver == 1 || vol->minor_ver == 2)
110                                 break;
111                         else
112                                 goto version_error;
113                 case 2: case 3:
114                         if (vol->minor_ver == 0)
115                                 break;
116                         /* Fall through on error. */
117                 default:
118 version_error:
119                 fprintf(stderr, "Error: Unknown NTFS version.\n");
120                         goto error_exit;
121         }
122         /* Read in $LogFile. */
123         if (ntfs_file_record_read(vol, FILE_LogFile, &m, NULL)) {
124                 fprintf(stderr, "Error reading mft record for $LogFile.\n");
125                 goto error_exit;
126         }
127         if (!(m->flags & MFT_RECORD_IN_USE)) {
128                 fprintf(stderr, "Error: $LogFile has been deleted. Run chkdsk "
129                                 "to fix this.\n");
130                 goto error_exit;
131         }
132         ctx = ntfs_attr_get_search_ctx(NULL, m);
133         if (!ctx) {
134                 perror("Failed to allocate attribute search context");
135                 goto error_exit;
136         }
137         /* Find the $DATA attribute of the $LogFile. */
138         if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
139                 fprintf(stderr, "Error: Attribute $DATA was not found in" \
140                                 "$LogFile!\n");
141                 goto log_file_error;
142         }
143         a = ctx->attr;
144         /* Get length of $LogFile contents. */
145         l = ntfs_get_attribute_value_length(a);
146         if (!l) {
147                 puts("$LogFile has zero length, no need to write to disk.");
148                 goto log_file_error;
149         }
150         /* Allocate a buffer to hold all of the $LogFile contents. */
151         lfd = (unsigned char*)malloc(l);
152         if (!lfd) {
153                 puts("Not enough memory to load $LogFile.");
154                 goto log_file_error;
155         }
156         /* Read in the $LogFile into the buffer. */
157         if (l != ntfs_get_attribute_value(vol, m, a, lfd)) {
158                 puts("Amount of data read does not correspond to expected "
159                      "length!");
160                 free(lfd);
161                 goto log_file_error;
162         }
163         /* Check restart area. */
164         if (!ntfs_is_rstr_recordp(lfd)) {
165                 s64 _l;
166
167                 for (_l = 0LL; _l < l; _l++)
168                         if (lfd[_l] != (unsigned char)-1)
169                                 break;
170                 if (_l < l)
171                         puts("$LogFile contents are corrupt (magic RSTR "
172                                         "missing)!");
173                 else
174                         puts("$LogFile is empty.");
175                 goto log_file_error;
176         }
177         /* Do the interpretation and display now. */
178         rph = (RESTART_PAGE_HEADER*)lfd;
179         lps = le32_to_cpu(rph->log_page_size);
180 pass_loc:
181         if (ntfs_mst_post_read_fixup((NTFS_RECORD*)rph, lps) ||
182             ntfs_is_baad_record(rph->magic)) {
183                 puts("$LogFile incomplete multi sector transfer detected! "
184                      "Cannot handle this yet!");
185                 goto log_file_error;
186         }
187         if ((pass == 2) && !memcmp(lfd, rph, lps)) {
188                 printf("2nd restart area fully matches the 1st one. Skipping "
189                                 "display.\n");
190                 goto skip_rstr_pass;
191         }
192         if (le16_to_cpu(rph->major_ver != 1) ||
193             le16_to_cpu(rph->minor_ver != 1)) {
194                 fprintf(stderr, "$LogFile version %i.%i! Error: Unknown "
195                                 "$LogFile version!\n",
196                                         le16_to_cpu(rph->major_ver),
197                                         le16_to_cpu(rph->minor_ver));
198                 goto log_file_error;
199         }
200         rr = (RESTART_AREA*)((char*)rph + le16_to_cpu(rph->restart_offset));
201         cr = (RESTART_CLIENT*)((char*)rr +
202                         le16_to_cpu(rr->client_array_offset));
203         /* Dump of the interpreted $LogFile restart area. */
204         if (pass == 1)
205                 printf("\n$LogFile version %i.%i.\n",
206                                 le16_to_cpu(rph->major_ver),
207                                 le16_to_cpu(rph->minor_ver));
208         printf("\n%s restart area:\n", pass == 1? "1st": "2nd");
209         printf("magic = RSTR\n");
210         printf("ChkDskLsn = 0x%Lx\n", sle64_to_cpu(rph->chkdsk_lsn));
211         printf("SystemPageSize = %u\n", le32_to_cpu(rph->system_page_size));
212         printf("LogPageSize = %u\n", le32_to_cpu(rph->log_page_size));
213         printf("RestartOffset = 0x%x\n", le16_to_cpu(rph->restart_offset));
214         printf("\n(1st) restart record:\n");
215         printf("CurrentLsn = %Lx\n", sle64_to_cpu(rr->current_lsn));
216         printf("LogClients = %u\n", le16_to_cpu(rr->log_clients));
217         printf("ClientFreeList = %i\n", sle16_to_cpu(rr->client_free_list));
218         printf("ClientInUseList = %i\n", sle16_to_cpu(rr->client_in_use_list));
219         printf("Flags = 0x%x\n", le16_to_cpu(rr->flags));
220         printf("SeqNumberBits = %u (0x%x)\n", le32_to_cpu(rr->seq_number_bits),
221                         le32_to_cpu(rr->seq_number_bits));
222         printf("RestartAreaLength = 0x%x\n",
223                         le16_to_cpu(rr->restart_area_length));
224         printf("ClientArrayOffset = 0x%x\n",
225                         le16_to_cpu(rr->client_array_offset));
226         printf("FileSize = %Lu (0x%Lx)\n", le64_to_cpu(rr->file_size),
227                         le64_to_cpu(rr->file_size));
228         if (le64_to_cpu(rr->file_size) != l)
229                 puts("$LogFile restart area indicates a log file size"
230                      "different from the actual size!");
231         printf("LastLsnDataLength = 0x%x\n",
232                         le32_to_cpu(rr->last_lsn_data_length));
233         printf("RecordLength = 0x%x\n", le16_to_cpu(rr->record_length));
234         printf("LogPageDataOffset = 0x%x\n",
235                         le16_to_cpu(rr->log_page_data_offset));
236         for (client = 0; client < le16_to_cpu(rr->log_clients); client++) {
237                 printf("\nRestart client record number %i:\n", client);
238                 printf("OldestLsn = 0x%Lx\n", sle64_to_cpu(cr->oldest_lsn));
239                 printf("ClientRestartLsn = 0x%Lx\n",
240                                 sle64_to_cpu(cr->client_restart_lsn));
241                 printf("PrevClient = %i\n", sle16_to_cpu(cr->prev_client));
242                 printf("NextClient = %i\n", sle16_to_cpu(cr->next_client));
243                 printf("SeqNumber = 0x%Lx\n", le64_to_cpu(cr->seq_number));
244                 printf("ClientNameLength = 0x%x\n",
245                                 le32_to_cpu(cr->client_name_length));
246                 if (le32_to_cpu(cr->client_name_length)) {
247                         // convert to ascii and print out.
248                         // printf("ClientName = %u\n", le16_to_cpu(cr->client_name));
249                 }
250                 /* Size of a restart client record is fixed at 0xa0 bytes. */
251                 cr = (RESTART_CLIENT*)((char*)cr + 0xa0);
252         }
253 skip_rstr_pass:
254         if (pass == 1) {
255                 rph = (RESTART_PAGE_HEADER*)((char*)rph + lps);
256                 ++pass;
257                 goto pass_loc;
258         }
259         rcrd_ph = (RECORD_PAGE_HEADER*)rph;
260         /* Reuse pass for log record clienter. */
261         pass = 0;
262         printf("\nFinished with restart area. Beginning with log area.\n");
263 rcrd_pass_loc:
264         rcrd_ph = (RECORD_PAGE_HEADER*)((char*)rcrd_ph + lps);
265         if ((char*)rcrd_ph + lps > (char*)lfd + l)
266                 goto end_of_rcrd_passes;
267         printf("\nLog record page number %i", pass);
268         if (!ntfs_is_rcrd_record(rcrd_ph->magic)) {
269                 for (i = 0; i < lps; i++)
270                         if (((char*)rcrd_ph)[i] != (char)-1)
271                                 break;
272                 if (i < lps)
273                         puts(" is corrupt (magic RCRD is missing).");
274                 else
275                         puts(" is empty.");
276                 pass++;
277                 goto rcrd_pass_loc;
278         } else
279                 printf(":");
280         /* Dump log record page */
281         printf("\nmagic = RCRD\n");
282         printf("copy.last_lsn/file_offset = 0x%Lx\n",
283                         le64_to_cpu(rcrd_ph->copy.last_lsn));
284         printf("flags = 0x%x\n", le32_to_cpu(rcrd_ph->flags));
285         printf("page count = %i\n", le16_to_cpu(rcrd_ph->page_count));
286         printf("page position = %i\n", le16_to_cpu(rcrd_ph->page_position));
287         printf("header.next_record_offset = 0x%Lx\n",
288                         le64_to_cpu(rcrd_ph->header.packed.next_record_offset));
289         printf("header.last_end_lsn = 0x%Lx\n",
290                         le64_to_cpu(rcrd_ph->header.packed.last_end_lsn));
291         /*
292          * Where does the 0x40 come from? Is it just usa_offset +
293          * usa_client * 2 + 7 & ~7 or is it derived from somewhere?
294          */
295         lr = (LOG_RECORD*)((char*)rcrd_ph + 0x40);
296         client = 0;
297 log_record_pass:
298         printf("\nLog record %i:\n", client);
299         printf("this lsn = 0x%Lx\n", le64_to_cpu(lr->this_lsn));
300         printf("client previous lsn = 0x%Lx\n",
301                         le64_to_cpu(lr->client_previous_lsn));
302         printf("client undo next lsn = 0x%Lx\n",
303                         le64_to_cpu(lr->client_undo_next_lsn));
304         printf("client data length = 0x%x\n",
305                         le32_to_cpu(lr->client_data_length));
306         printf("client_id.seq_number = 0x%x\n",
307                         le16_to_cpu(lr->client_id.seq_number));
308         printf("client_id.client_index = 0x%x\n",
309                         le16_to_cpu(lr->client_id.client_index));
310         printf("record type = 0x%x\n", le32_to_cpu(lr->record_type));
311         printf("transaction_id = 0x%x\n", le32_to_cpu(lr->transaction_id));
312         printf("flags = 0x%x:", lr->flags);
313         if (!lr->flags)
314                 printf(" NONE\n");
315         else {
316                 int _b = 0;
317
318                 if (lr->flags & LOG_RECORD_MULTI_PAGE) {
319                         printf(" LOG_RECORD_MULTI_PAGE");
320                         _b = 1;
321                 }
322                 if (lr->flags & ~LOG_RECORD_MULTI_PAGE) {
323                         if (_b)
324                                 printf(" |");
325                         printf(" Unknown flags");
326                 }
327                 printf("\n");
328         }
329         printf("redo_operation = 0x%x\n", le16_to_cpu(lr->redo_operation));
330         printf("undo_operation = 0x%x\n", le16_to_cpu(lr->undo_operation));
331         printf("redo_offset = 0x%x\n", le16_to_cpu(lr->redo_offset));
332         printf("redo_length = 0x%x\n", le16_to_cpu(lr->redo_length));
333         printf("undo_offset = 0x%x\n", le16_to_cpu(lr->undo_offset));
334         printf("undo_length = 0x%x\n", le16_to_cpu(lr->undo_length));
335         printf("target_attribute = 0x%x\n", le16_to_cpu(lr->target_attribute));
336         printf("lcns_to_follow = 0x%x\n", le16_to_cpu(lr->lcns_to_follow));
337         printf("record_offset = 0x%x\n", le16_to_cpu(lr->record_offset));
338         printf("attribute_offset = 0x%x\n", le16_to_cpu(lr->attribute_offset));
339         printf("target_vcn = 0x%Lx\n", sle64_to_cpu(lr->target_vcn));
340         if (le16_to_cpu(lr->lcns_to_follow) > 0)
341                 printf("Array of lcns:\n");
342         for (i = 0; i < le16_to_cpu(lr->lcns_to_follow); i++)
343                 printf("lcn_list[%i].lcn = 0x%Lx\n", i,
344                                 sle64_to_cpu(lr->lcn_list[i].lcn));
345         client++;
346         lr = (LOG_RECORD*)((char*)lr + 0x70);
347         if (((char*)lr + 0x70 <= (char*)rcrd_ph +
348                         le64_to_cpu(rcrd_ph->header.packed.next_record_offset)))
349                 goto log_record_pass;
350         pass++;
351         goto rcrd_pass_loc;
352 end_of_rcrd_passes:
353 log_file_error:
354         printf("\n");
355         /* Set return code to 0. */
356         i = 0;
357 final_exit:
358         if (lfd)
359                 free(lfd);
360         if (ctx)
361                 ntfs_attr_put_search_ctx(ctx);
362         if (m)
363                 free(m);
364         if (vol && ntfs_umount(vol, 0))
365                 ntfs_umount(vol, 1);
366         return i;
367 error_exit:
368         i = 1;
369         goto final_exit;
370 }
371