1/**
2 * ntfsmftalloc - Part of the Linux-NTFS project.
3 *
4 * Copyright (c) 2002-2005 Anton Altaparmakov
5 *
6 * This utility will allocate and initialize an mft record.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program (in the main directory of the Linux-NTFS source
20 * in the file COPYING); if not, write to the Free Software Foundation,
21 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 */
23
24#include "config.h"
25
26#ifdef HAVE_UNISTD_H
27#	include <unistd.h>
28#endif
29#ifdef HAVE_STDLIB_H
30#	include <stdlib.h>
31#endif
32#ifdef HAVE_STDIO_H
33#	include <stdio.h>
34#endif
35#ifdef HAVE_STDARG_H
36#	include <stdarg.h>
37#endif
38#ifdef HAVE_STRING_H
39#	include <string.h>
40#endif
41#ifdef HAVE_ERRNO_H
42#	include <errno.h>
43#endif
44#ifdef HAVE_TIME_H
45#include <time.h>
46#endif
47#ifdef HAVE_GETOPT_H
48#	include <getopt.h>
49#else
50	extern int optind;
51#endif
52#ifdef HAVE_LIMITS_H
53#include <limits.h>
54#endif
55#ifndef LLONG_MAX
56#	define LLONG_MAX 9223372036854775807LL
57#endif
58
59#include "types.h"
60#include "attrib.h"
61#include "inode.h"
62#include "layout.h"
63#include "volume.h"
64#include "mft.h"
65#include "utils.h"
66/* #include "version.h" */
67#include "logging.h"
68
69static const char *EXEC_NAME = "ntfsmftalloc";
70
71/* Need these global so ntfsmftalloc_exit can access them. */
72static BOOL success = FALSE;
73
74static char *dev_name;
75
76static ntfs_volume *vol;
77static ntfs_inode *ni = NULL;
78static ntfs_inode *base_ni = NULL;
79static s64 base_mft_no = -1;
80
81static struct {
82				/* -h, print usage and exit. */
83	int no_action;		/* -n, do not write to device, only display
84				       what would be done. */
85	int quiet;		/* -q, quiet execution. */
86	int verbose;		/* -v, verbose execution, given twice, really
87				       verbose execution (debug mode). */
88	int force;		/* -f, force allocation. */
89				/* -V, print version and exit. */
90} opts;
91
92/**
93 * err_exit - error output and terminate; ignores quiet (-q)
94 */
95__attribute__((noreturn))
96__attribute__((format(printf, 1, 2)))
97static void err_exit(const char *fmt, ...)
98{
99	va_list ap;
100
101	fprintf(stderr, "ERROR: ");
102	va_start(ap, fmt);
103	vfprintf(stderr, fmt, ap);
104	va_end(ap);
105	fprintf(stderr, "Aborting...\n");
106	exit(1);
107}
108
109/**
110 * copyright - print copyright statements
111 */
112static void copyright(void)
113{
114	ntfs_log_info("Copyright (c) 2004-2005 Anton Altaparmakov\n"
115			"Allocate and initialize a base or an extent mft "
116			"record.  If a base mft record\nis not specified, a "
117			"base mft record is allocated and initialized.  "
118			"Otherwise,\nan extent mft record is allocated and "
119			"initialized to point to the specified\nbase mft "
120			"record.\n");
121}
122
123/**
124 * license - print license statement
125 */
126static void license(void)
127{
128	ntfs_log_info("%s", ntfs_gpl);
129}
130
131/**
132 * usage - print a list of the parameters to the program
133 */
134__attribute__((noreturn))
135static void usage(void)
136{
137	copyright();
138	ntfs_log_info("Usage: %s [options] device [base-mft-record]\n"
139			"    -n    Do not write to disk\n"
140			"    -f    Force execution despite errors\n"
141			"    -q    Quiet execution\n"
142			"    -v    Verbose execution\n"
143			"    -vv   Very verbose execution\n"
144			"    -V    Display version information\n"
145			"    -l    Display licensing information\n"
146			"    -h    Display this help\n", EXEC_NAME);
147	ntfs_log_info("%s%s", ntfs_bugs, ntfs_home);
148	exit(1);
149}
150
151/**
152 * parse_options
153 */
154static void parse_options(int argc, char *argv[])
155{
156	long long ll;
157	char *s;
158	int c;
159
160	if (argc && *argv)
161		EXEC_NAME = *argv;
162	ntfs_log_info("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION);
163	while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF) {
164		switch (c) {
165		case 'f':
166			opts.force = 1;
167			break;
168		case 'n':
169			opts.no_action = 1;
170			break;
171		case 'q':
172			opts.quiet = 1;
173			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
174			break;
175		case 'v':
176			opts.verbose++;
177			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
178			break;
179		case 'V':
180			/* Version number already printed, so just exit. */
181			exit(0);
182		case 'l':
183			copyright();
184			license();
185			exit(0);
186		case 'h':
187		case '?':
188		default:
189			usage();
190		}
191	}
192
193	if (opts.verbose > 1)
194		ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE |
195			NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET);
196
197	if (optind == argc)
198		usage();
199	/* Get the device. */
200	dev_name = argv[optind++];
201	ntfs_log_verbose("device name = %s\n", dev_name);
202	if (optind != argc) {
203		/* Get the base mft record number. */
204		ll = strtoll(argv[optind++], &s, 0);
205		if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE))
206			err_exit("Invalid base mft record number: %s\n",
207					argv[optind - 1]);
208		base_mft_no = ll;
209		ntfs_log_verbose("base mft record number = 0x%llx\n", (long long)ll);
210	}
211	if (optind != argc)
212		usage();
213}
214
215/**
216 * dump_mft_record
217 */
218static void dump_mft_record(MFT_RECORD *m)
219{
220	ATTR_RECORD *a;
221	unsigned int u;
222	MFT_REF r;
223
224	ntfs_log_info("-- Beginning dump of mft record. --\n");
225	u = le32_to_cpu(m->magic);
226	ntfs_log_info("Mft record signature (magic) = %c%c%c%c\n", u & 0xff,
227			u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff);
228	u = le16_to_cpu(m->usa_ofs);
229	ntfs_log_info("Update sequence array offset = %u (0x%x)\n", u, u);
230	ntfs_log_info("Update sequence array size = %u\n", le16_to_cpu(m->usa_count));
231	ntfs_log_info("$LogFile sequence number (lsn) = %llu\n",
232			(unsigned long long)sle64_to_cpu(m->lsn));
233	ntfs_log_info("Sequence number = %u\n", le16_to_cpu(m->sequence_number));
234	ntfs_log_info("Reference (hard link) count = %u\n",
235						le16_to_cpu(m->link_count));
236	u = le16_to_cpu(m->attrs_offset);
237	ntfs_log_info("First attribute offset = %u (0x%x)\n", u, u);
238	ntfs_log_info("Flags = %u: ", le16_to_cpu(m->flags));
239	if (m->flags & MFT_RECORD_IN_USE)
240		ntfs_log_info("MFT_RECORD_IN_USE");
241	else
242		ntfs_log_info("MFT_RECORD_NOT_IN_USE");
243	if (m->flags & MFT_RECORD_IS_DIRECTORY)
244		ntfs_log_info(" | MFT_RECORD_IS_DIRECTORY");
245	ntfs_log_info("\n");
246	u = le32_to_cpu(m->bytes_in_use);
247	ntfs_log_info("Bytes in use = %u (0x%x)\n", u, u);
248	u = le32_to_cpu(m->bytes_allocated);
249	ntfs_log_info("Bytes allocated = %u (0x%x)\n", u, u);
250	r = le64_to_cpu(m->base_mft_record);
251	ntfs_log_info("Base mft record reference:\n\tMft record number = %llu\n\t"
252			"Sequence number = %u\n",
253			(unsigned long long)MREF(r), MSEQNO(r));
254	ntfs_log_info("Next attribute instance = %u\n",
255			le16_to_cpu(m->next_attr_instance));
256	a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset));
257	ntfs_log_info("-- Beginning dump of attributes within mft record. --\n");
258	while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) {
259		if (a->type == AT_END)
260			break;
261		a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
262	};
263	ntfs_log_info("-- End of attributes. --\n");
264}
265
266/**
267 * ntfsmftalloc_exit
268 */
269static void ntfsmftalloc_exit(void)
270{
271	if (success)
272		return;
273	/* If there is a base inode, close that instead of the extent inode. */
274	if (base_ni)
275		ni = base_ni;
276	/* Close the inode. */
277	if (ni && ntfs_inode_close(ni)) {
278		ntfs_log_perror("Warning: Failed to close inode 0x%llx",
279				(long long)ni->mft_no);
280	}
281	/* Unmount the volume. */
282	if (ntfs_umount(vol, 0) == -1)
283		ntfs_log_perror("Warning: Could not umount %s", dev_name);
284}
285
286/**
287 * main
288 */
289int main(int argc, char **argv)
290{
291	unsigned long mnt_flags, ul;
292	int err;
293
294	ntfs_log_set_handler(ntfs_log_handler_outerr);
295
296	/* Initialize opts to zero / required values. */
297	memset(&opts, 0, sizeof(opts));
298	/* Parse command line options. */
299	parse_options(argc, argv);
300	utils_set_locale();
301	/* Make sure the file system is not mounted. */
302	if (ntfs_check_if_mounted(dev_name, &mnt_flags))
303		ntfs_log_error("Failed to determine whether %s is mounted: %s\n",
304				dev_name, strerror(errno));
305	else if (mnt_flags & NTFS_MF_MOUNTED) {
306		ntfs_log_error("%s is mounted.\n", dev_name);
307		if (!opts.force)
308			err_exit("Refusing to run!\n");
309		ntfs_log_error("ntfsmftalloc forced anyway. Hope /etc/mtab "
310				"is incorrect.\n");
311	}
312	/* Mount the device. */
313	if (opts.no_action) {
314		ntfs_log_quiet("Running in READ-ONLY mode!\n");
315		ul = NTFS_MNT_RDONLY;
316	} else
317		ul = 0;
318	vol = ntfs_mount(dev_name, ul);
319	if (!vol)
320		err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno));
321	/* Register our exit function which will unlock and close the device. */
322	err = atexit(&ntfsmftalloc_exit);
323	if (err == -1) {
324		ntfs_log_error("Could not set up exit() function because atexit() "
325				"failed: %s Aborting...\n", strerror(errno));
326		ntfsmftalloc_exit();
327		exit(1);
328	}
329	if (base_mft_no != -1) {
330		base_ni = ntfs_inode_open(vol, base_mft_no);
331		if (!base_ni)
332			err_exit("Failed to open base inode 0x%llx: %s\n",
333					(long long)base_mft_no,
334					strerror(errno));
335	}
336	/* Open the specified inode. */
337	ni = ntfs_mft_record_alloc(vol, base_ni);
338	if (!ni)
339		err_exit("Failed to allocate mft record: %s\n",
340				strerror(errno));
341	ntfs_log_info("Allocated %s mft record 0x%llx", base_ni ? "extent" : "base",
342			(long long)ni->mft_no);
343	if (base_ni)
344		ntfs_log_info(" with base mft record 0x%llx",
345				(long long)base_mft_no);
346	ntfs_log_info(".\n");
347	if (!opts.quiet && opts.verbose > 1) {
348		ntfs_log_verbose("Dumping allocated mft record 0x%llx:\n",
349				(long long)ni->mft_no);
350		dump_mft_record(ni->mrec);
351	}
352	/* Close the (base) inode. */
353	if (base_ni)
354		ni = base_ni;
355	err = ntfs_inode_close(ni);
356	if (err)
357		err_exit("Failed to close inode 0x%llx: %s\n",
358				(long long)ni->mft_no, strerror(errno));
359	/* Unmount the volume. */
360	err = ntfs_umount(vol, 0);
361	/* Disable our ntfsmftalloc_exit() handler. */
362	success = TRUE;
363	if (err == -1)
364		ntfs_log_perror("Warning: Failed to umount %s", dev_name);
365	else
366		ntfs_log_quiet("ntfsmftalloc completed successfully.\n");
367	return 0;
368}
369