xref: /haiku/src/bin/diskimage.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <errno.h>
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 
15 #include <DiskDevice.h>
16 #include <DiskDeviceRoster.h>
17 #include <Path.h>
18 
19 
20 extern const char* __progname;
21 const char* kCommandName = __progname;
22 
23 
24 static const char* kUsage =
25 	"Usage: %s register <file>\n"
26 	"       %s unregister ( <file> | <device path> | <device ID> )\n"
27 	"       %s list\n"
28 	"Registers a regular file as disk device, unregisters an already "
29 		"registered\n"
30 	"one, or lists all file disk devices.\n"
31 	"\n"
32 	"Options:\n"
33 	"  -h, --help   - Print this usage info.\n"
34 ;
35 
36 
37 static void
38 print_usage_and_exit(bool error)
39 {
40     fprintf(error ? stderr : stdout, kUsage, kCommandName, kCommandName,
41 		kCommandName);
42     exit(error ? 1 : 0);
43 }
44 
45 
46 static status_t
47 list_file_disk_devices()
48 {
49 	printf("    ID  File                               Device\n");
50 	printf("------------------------------------------------------------------"
51 		"-------------\n");
52 
53 	BDiskDeviceRoster roster;
54 	BDiskDevice device;
55 	while (roster.GetNextDevice(&device) == B_OK) {
56 		if (!device.IsFile())
57 			continue;
58 
59 		// ID
60 		printf("%6" B_PRId32 "  ", device.ID());
61 
62 		// file path
63 		BPath path;
64 		printf("%-35s",
65 			device.GetFilePath(&path) == B_OK ? path.Path() : "???");
66 
67 		// device path
68 		printf("%s", device.GetPath(&path) == B_OK ? path.Path() : "???");
69 		printf("\n");
70 	}
71 
72 	return B_OK;
73 }
74 
75 
76 static status_t
77 register_file_disk_device(const char* fileName)
78 {
79 	// stat() the file to verify that it's a regular file
80 	struct stat st;
81 	if (lstat(fileName, &st) != 0) {
82 		status_t error = errno;
83 		fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileName,
84 			strerror(error));
85 		return error;
86 	}
87 
88 	if (!S_ISREG(st.st_mode)) {
89 		fprintf(stderr, "Error: \"%s\" is not a regular file.\n", fileName);
90 		return B_BAD_VALUE;
91 	}
92 
93 	// register the file
94 	BDiskDeviceRoster roster;
95 	partition_id id = roster.RegisterFileDevice(fileName);
96 	if (id < 0) {
97 		fprintf(stderr, "Error: Failed to register file disk device: %s\n",
98 			strerror(id));
99 		return id;
100 	}
101 
102 	// print the success message (get the device path)
103 	BDiskDevice device;
104 	BPath path;
105 	if (roster.GetDeviceWithID(id, &device) == B_OK
106 		&& device.GetPath(&path) == B_OK) {
107 		printf("Registered file as disk device \"%s\" with ID %" B_PRId32 ".\n",
108 			path.Path(), id);
109 	} else {
110 		printf("Registered file as disk device with ID %" B_PRId32 ", "
111 			"but failed to get the device path.\n", id);
112 	}
113 
114 	return B_OK;
115 }
116 
117 
118 static status_t
119 unregister_file_disk_device(const char* fileNameOrID)
120 {
121 	// try to parse the parameter as ID
122 	char* numberEnd;
123 	partition_id id = strtol(fileNameOrID, &numberEnd, 0);
124 	BDiskDeviceRoster roster;
125 	if (id >= 0 && numberEnd != fileNameOrID && *numberEnd == '\0') {
126 		BDiskDevice device;
127 		if (roster.GetDeviceWithID(id, &device) == B_OK && device.IsFile()) {
128 			status_t error = roster.UnregisterFileDevice(id);
129 			if (error != B_OK) {
130 				fprintf(stderr, "Error: Failed to unregister file disk device "
131 					"with ID %" B_PRId32 ": %s\n", id, strerror(error));
132 				return error;
133 			}
134 
135 			printf("Unregistered file disk device with ID %" B_PRId32 ".\n",
136 				id);
137 			return B_OK;
138 		} else {
139 			fprintf(stderr, "No file disk device with ID %" B_PRId32 ","
140 				"trying file \"%s\"\n", id, fileNameOrID);
141 		}
142 	}
143 
144 	// the parameter must be a file name -- stat() it
145 	struct stat st;
146 	if (lstat(fileNameOrID, &st) != 0) {
147 		status_t error = errno;
148 		fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", fileNameOrID,
149 			strerror(error));
150 		return error;
151 	}
152 
153 	// remember the volume and node ID, so we can identify the file
154 	// NOTE: There's a race condition -- we would need to open the file and
155 	// keep it open to avoid it.
156 	dev_t volumeID = st.st_dev;
157 	ino_t nodeID = st.st_ino;
158 
159 	// iterate through all file disk devices and try to find a match
160 	BDiskDevice device;
161 	while (roster.GetNextDevice(&device) == B_OK) {
162 		if (!device.IsFile())
163 			continue;
164 
165 		// get file path and stat it, same for the device path
166 		BPath path;
167 		bool isFilePath = true;
168 		if ((device.GetFilePath(&path) == B_OK && lstat(path.Path(), &st) == 0
169 				&& volumeID == st.st_dev && nodeID == st.st_ino)
170 			|| (isFilePath = false, false)
171 			|| (device.GetPath(&path) == B_OK && lstat(path.Path(), &st) == 0
172 				&& volumeID == st.st_dev && nodeID == st.st_ino)) {
173 			status_t error = roster.UnregisterFileDevice(device.ID());
174 			if (error != B_OK) {
175 				fprintf(stderr, "Error: Failed to unregister file disk device"
176 					"%s \"%s\" (ID: %" B_PRId32 "): %s\n",
177 					isFilePath ? " for file" : "", fileNameOrID, device.ID(),
178 					strerror(error));
179 				return error;
180 			}
181 
182 			printf("Unregistered file disk device%s \"%s\" "
183 				"(ID: %" B_PRId32 ")\n", isFilePath ? " for file" : "",
184 				fileNameOrID, device.ID());
185 
186 			return B_OK;
187 		}
188 	}
189 
190 	fprintf(stderr, "Error: \"%s\" does not refer to a file disk device.\n",
191 		fileNameOrID);
192 	return B_BAD_VALUE;
193 }
194 
195 
196 int
197 main(int argc, const char* const* argv)
198 {
199 	while (true) {
200 		static struct option sLongOptions[] = {
201 			{ "help", no_argument, 0, 'h' },
202 			{ 0, 0, 0, 0 }
203 		};
204 
205 		opterr = 0; // don't print errors
206 		int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
207 		if (c == -1)
208 			break;
209 
210 		switch (c) {
211 			case 'h':
212 				print_usage_and_exit(false);
213 				break;
214 
215 			default:
216 				print_usage_and_exit(true);
217 				break;
218 		}
219 	}
220 
221 	// Of the remaining arguments the next one should be the command.
222 	if (optind >= argc)
223 		print_usage_and_exit(true);
224 
225 	status_t error = B_OK;
226 	const char* command = argv[optind++];
227 
228 	if (strcmp(command, "list") == 0) {
229 		if (optind < argc)
230 			print_usage_and_exit(true);
231 
232 		list_file_disk_devices();
233 	} else if (strcmp(command, "register") == 0) {
234 		if (optind + 1 != argc)
235 			print_usage_and_exit(true);
236 
237 		const char* fileName = argv[optind++];
238 		register_file_disk_device(fileName);
239 	} else if (strcmp(command, "unregister") == 0) {
240 		if (optind + 1 != argc)
241 			print_usage_and_exit(true);
242 
243 		const char* fileName = argv[optind++];
244 		unregister_file_disk_device(fileName);
245 	} else
246 		print_usage_and_exit(true);
247 
248 	return error == B_OK ? 0 : 1;
249 }
250