http://linux-ntfs.sourceforge.net/snapshots/ntfsprogs-200307311516.tar.bz2
[ntfsprogs.git] / ntfsprogs / dumplog.c
1 /**
2  * DumpLog - 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) specified
7  * on the command line and display the results on stdout. Errors will be output
8  * to 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 #include <unistd.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/stat.h>
33
34 #include "types.h"
35 #include "mst.h"
36 #include "logfile.h"
37
38 const char *EXEC_NAME = "dumplog";
39 const char *EXEC_VERSION = "1.0";
40
41 /**
42  * main
43  */
44 int main(int argc, char **argv)
45 {
46         s64 l, br;
47         unsigned char *lfd = NULL;
48         RESTART_PAGE_HEADER *rph;
49         RESTART_AREA *rr;
50         RESTART_CLIENT *cr;
51         RECORD_PAGE_HEADER *rcrd_ph;
52         LOG_RECORD *lr;
53         int pass = 1;
54         int i, lps, client;
55         int f = 0;
56         char zero[4096];
57         struct stat sbuf;
58
59         memset(zero, 0, sizeof(zero));
60         printf("\n");
61         if (argc != 2) {
62                 printf("%s v%s - Interpret and display information about the "
63                        "journal\ngiven on the command line.\n\n"
64                        /* Generic copyright / disclaimer. */
65                        "Copyright (c) 2001 Anton Altaparmakov.\n\n"
66                        "%s is free software, released under the GNU "
67                        "General Public License\nand you are welcome to "
68                        "redistribute it under certain conditions.\n"
69                        "%s comes with ABSOLUTELY NO WARRANTY; for details "
70                        "read the GNU\nGeneral Public License to be found "
71                        "in the file COPYING in the main Linux-NTFS\n"
72                        "distribution directory.\n\n"
73                        /* Generic part ends here. */
74                        "Syntax: dumplog log_file_name\n"
75                        "        e.g. dumplog /mnt/ntfstest/\\$LogFile\n\n",
76                        EXEC_NAME, EXEC_VERSION, EXEC_NAME, EXEC_NAME);
77                 fprintf(stderr, "Error: incorrect syntax\n");
78                 exit(1);
79         }
80         if (stat(argv[1], &sbuf) == -1) {
81                 if (errno == ENOENT)
82                         fprintf(stderr, "The file doesn't exist; did you "
83                                         "specify it correctly?\n");
84                 else
85                         fprintf(stderr, "Error getting information about %s: "
86                                         "%s\n", argv[1], strerror(errno));
87                 exit(1);
88         }
89         f = open(argv[1], O_RDONLY);
90         if (f == -1) {
91                 perror("Couldn't open file");
92                 exit(1);
93         }
94         l = sbuf.st_size;
95         if (l > 0x400000LL) {
96                 printf("Only analysing the first four megabytes of the "
97                                 "logfile (real size = 0x%Lx).\n",
98                                 (unsigned long long)l);
99                 l = 0x400000LL;
100         }
101         lfd = (unsigned char*)calloc(1, l);
102         if (!lfd) {
103                 perror("Couldn't allocate internal buffer");
104                 goto log_file_error;
105         }
106         /* Read in the $LogFile into the buffer. */
107         if ((br = read(f, lfd, l)) == -1) {
108                 perror("Couldn't read file");
109                 goto log_file_error;
110         }
111         /* Valid data length in buffer. */
112         l = min(br, l);
113         /* Check restart area. */
114         if (!ntfs_is_rstr_recordp(lfd)) {
115                 s64 _l;
116
117                 for (_l = 0LL; _l < l; _l++)
118                         if (lfd[_l] != (unsigned char)-1)
119                                 break;
120                 if (_l < l)
121                         puts("Logfile contents are corrupt (magic RSTR "
122                                         "missing)!");
123                 else
124                         puts("Logfile is empty.");
125                 goto log_file_error;
126         }
127         /* Do the interpretation and display now. */
128         rph = (RESTART_PAGE_HEADER*)lfd;
129         lps = le32_to_cpu(rph->log_page_size);
130 pass_loc:
131         if (ntfs_mst_post_read_fixup((NTFS_RECORD*)rph, lps) ||
132                         ntfs_is_baad_record(rph->magic)) {
133                 puts("Logfile incomplete multi sector transfer detected! "
134                                 "Cannot handle this yet!");
135                 goto log_file_error;
136         }
137         if ((pass == 2) && !memcmp(lfd, rph, lps)) {
138                 printf("2nd restart area fully matches the 1st one. Skipping "
139                                 "display.\n");
140                 goto skip_rstr_pass;
141         }
142         if (le16_to_cpu(rph->major_ver != 1) ||
143             le16_to_cpu(rph->minor_ver != 1)) {
144                 fprintf(stderr, "$LogFile version %i.%i! Error: Unknown "
145                                 "$LogFile version!\n",
146                                 le16_to_cpu(rph->major_ver),
147                                 le16_to_cpu(rph->minor_ver));
148                 goto log_file_error;
149         }
150         rr = (RESTART_AREA*)((char*)rph + le16_to_cpu(rph->restart_offset));
151         cr = (RESTART_CLIENT*)((char*)rr +
152                         le16_to_cpu(rr->client_array_offset));
153         /* Dump of the interpreted $LogFile restart area. */
154         if (pass == 1)
155                 printf("\n$LogFile version %i.%i.\n",
156                                 le16_to_cpu(rph->major_ver),
157                                 le16_to_cpu(rph->minor_ver));
158         printf("\n%s restart area:\n", pass == 1? "1st": "2nd");
159         printf("magic = RSTR\n");
160         printf("ChkDskLsn = 0x%Lx\n", sle64_to_cpu(rph->chkdsk_lsn));
161         printf("SystemPageSize = %u\n", le32_to_cpu(rph->system_page_size));
162         printf("LogPageSize = %u\n", le32_to_cpu(rph->log_page_size));
163         printf("RestartOffset = 0x%x\n", le16_to_cpu(rph->restart_offset));
164         printf("\n(1st) restart record:\n");
165         printf("CurrentLsn = %Lx\n", sle64_to_cpu(rr->current_lsn));
166         printf("LogClients = %u\n", le16_to_cpu(rr->log_clients));
167         printf("ClientFreeList = %i\n", sle16_to_cpu(rr->client_free_list));
168         printf("ClientInUseList = %i\n", sle16_to_cpu(rr->client_in_use_list));
169         printf("Flags = 0x%x\n", le16_to_cpu(rr->flags));
170         printf("SeqNumberBits = %u (0x%x)\n", le32_to_cpu(rr->seq_number_bits),
171                         le32_to_cpu(rr->seq_number_bits));
172         printf("RestartAreaLength = 0x%x\n",
173                         le16_to_cpu(rr->restart_area_length));
174         printf("ClientArrayOffset = 0x%x\n",
175                         le16_to_cpu(rr->client_array_offset));
176         printf("FileSize = %Lu (0x%Lx)\n", le64_to_cpu(rr->file_size),
177                         le64_to_cpu(rr->file_size));
178         printf("LastLsnDataLength = 0x%x\n",
179                         le32_to_cpu(rr->last_lsn_data_length));
180         printf("RecordLength = 0x%x\n", le16_to_cpu(rr->record_length));
181         printf("LogPageDataOffset = 0x%x\n",
182                         le16_to_cpu(rr->log_page_data_offset));
183         for (client = 0; client < le16_to_cpu(rr->log_clients); client++) {
184                 printf("\nRestart client record number %i:\n", client);
185                 printf("OldestLsn = 0x%Lx\n", sle64_to_cpu(cr->oldest_lsn));
186                 printf("ClientRestartLsn = 0x%Lx\n",
187                                 sle64_to_cpu(cr->client_restart_lsn));
188                 printf("PrevClient = %i\n", sle16_to_cpu(cr->prev_client));
189                 printf("NextClient = %i\n", sle16_to_cpu(cr->next_client));
190                 printf("SeqNumber = 0x%Lx\n", le64_to_cpu(cr->seq_number));
191                 printf("ClientNameLength = 0x%x\n",
192                                 le32_to_cpu(cr->client_name_length));
193                 if (le32_to_cpu(cr->client_name_length)) {
194                         // convert to ascii and print out.
195                         // printf("ClientName = %u\n", le16_to_cpu(cr->client_name));
196                 }
197                 /* Size of a restart client record is fixed at 0xa0 bytes. */
198                 cr = (RESTART_CLIENT*)((char*)cr + 0xa0);
199         }
200 skip_rstr_pass:
201         if (pass == 1) {
202                 rph = (RESTART_PAGE_HEADER*)((char*)rph + lps);
203                 ++pass;
204                 goto pass_loc;
205         }
206         rcrd_ph = (RECORD_PAGE_HEADER*)rph;
207         /* Reuse pass for log record clienter. */
208         pass = 0;
209         printf("\nFinished with restart area. Beginning with log area.\n");
210 rcrd_pass_loc:
211         rcrd_ph = (RECORD_PAGE_HEADER*)((char*)rcrd_ph + lps);
212         if ((char*)rcrd_ph + lps > (char*)lfd + l)
213                 goto end_of_rcrd_passes;
214         printf("\nLog record page number %i", pass);
215         if (!ntfs_is_rcrd_record(rcrd_ph->magic)) {
216                 for (i = 0; i < lps; i++)
217                         if (((char*)rcrd_ph)[i] != (char)-1)
218                                 break;
219                 if (i < lps)
220                         puts(" is corrupt (magic RCRD is missing).");
221                 else
222                         puts(" is empty.");
223                 pass++;
224                 goto rcrd_pass_loc;
225         } else
226                 printf(":");
227         /* Dump log record page */
228         printf("\nmagic = RCRD\n");
229         printf("copy.last_lsn/file_offset = 0x%Lx\n",
230                         le64_to_cpu(rcrd_ph->copy.last_lsn));
231         printf("flags = 0x%x\n", le32_to_cpu(rcrd_ph->flags));
232         printf("page count = %i\n", le16_to_cpu(rcrd_ph->page_count));
233         printf("page position = %i\n", le16_to_cpu(rcrd_ph->page_position));
234         printf("header.next_record_offset = 0x%Lx\n",
235                         le64_to_cpu(rcrd_ph->header.packed.next_record_offset));
236         printf("header.last_end_lsn = 0x%Lx\n",
237                         le64_to_cpu(rcrd_ph->header.packed.last_end_lsn));
238         /*
239          * Where does the 0x40 come from? Is it just usa_offset +
240          * usa_client * 2 + 7 & ~7 or is it derived from somewhere?
241          */
242         lr = (LOG_RECORD*)((char*)rcrd_ph + 0x40);
243         client = 0;
244 log_record_pass:
245         printf("\nLog record %i:\n", client);
246         printf("this lsn = 0x%Lx\n", le64_to_cpu(lr->this_lsn));
247         printf("client previous lsn = 0x%Lx\n",
248                         le64_to_cpu(lr->client_previous_lsn));
249         printf("client undo next lsn = 0x%Lx\n",
250                         le64_to_cpu(lr->client_undo_next_lsn));
251         printf("client data length = 0x%x\n",
252                         le32_to_cpu(lr->client_data_length));
253         printf("client_id.seq_number = 0x%x\n",
254                         le16_to_cpu(lr->client_id.seq_number));
255         printf("client_id.client_index = 0x%x\n",
256                         le16_to_cpu(lr->client_id.client_index));
257         printf("record type = 0x%x\n", le32_to_cpu(lr->record_type));
258         printf("transaction_id = 0x%x\n", le32_to_cpu(lr->transaction_id));
259         printf("flags = 0x%x:", lr->flags);
260         if (!lr->flags)
261                 printf(" NONE\n");
262         else {
263                 int _b = 0;
264
265                 if (lr->flags & LOG_RECORD_MULTI_PAGE) {
266                         printf(" LOG_RECORD_MULTI_PAGE");
267                         _b = 1;
268                 }
269                 if (lr->flags & ~LOG_RECORD_MULTI_PAGE) {
270                         if (_b)
271                                 printf(" |");
272                         printf(" Unknown flags");
273                 }
274                 printf("\n");
275         }
276         printf("redo_operation = 0x%x\n", le16_to_cpu(lr->redo_operation));
277         printf("undo_operation = 0x%x\n", le16_to_cpu(lr->undo_operation));
278         printf("redo_offset = 0x%x\n", le16_to_cpu(lr->redo_offset));
279         printf("redo_length = 0x%x\n", le16_to_cpu(lr->redo_length));
280         printf("undo_offset = 0x%x\n", le16_to_cpu(lr->undo_offset));
281         printf("undo_length = 0x%x\n", le16_to_cpu(lr->undo_length));
282         printf("target_attribute = 0x%x\n", le16_to_cpu(lr->target_attribute));
283         printf("lcns_to_follow = 0x%x\n", le16_to_cpu(lr->lcns_to_follow));
284         printf("record_offset = 0x%x\n", le16_to_cpu(lr->record_offset));
285         printf("attribute_offset = 0x%x\n", le16_to_cpu(lr->attribute_offset));
286         printf("target_vcn = 0x%Lx\n", sle64_to_cpu(lr->target_vcn));
287         if (le16_to_cpu(lr->lcns_to_follow) > 0)
288                 printf("Array of lcns:\n");
289         for (i = 0; i < le16_to_cpu(lr->lcns_to_follow); i++)
290                 printf("lcn_list[%i].lcn = 0x%Lx\n", i,
291                                 sle64_to_cpu(lr->lcn_list[i].lcn));
292         client++;
293         lr = (LOG_RECORD*)((char*)lr + 0x70);
294         if (((char*)lr + 0x70 <= (char*)rcrd_ph +
295                         le64_to_cpu(rcrd_ph->header.packed.next_record_offset)))
296                 goto log_record_pass;
297         pass++;
298         goto rcrd_pass_loc;
299 end_of_rcrd_passes:
300 log_file_error:
301         printf("\n");
302         /* Set return code to 0. */
303         i = 0;
304         if (lfd)
305                 free(lfd);
306         if (f)
307                 close(f);
308         return i;
309 }
310