xref: /onnv-gate/usr/src/lib/libparted/common/libparted/fs/ntfs/ntfs.c (revision 9663:ace9a2ac3683)
1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 2000, 2007 Free Software Foundation, Inc.
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <config.h>
20 
21 #include <parted/parted.h>
22 #include <parted/endian.h>
23 #include <parted/debug.h>
24 
25 #if ENABLE_NLS
26 #  include <libintl.h>
27 #  define _(String) dgettext (PACKAGE, String)
28 #else
29 #  define _(String) (String)
30 #endif /* ENABLE_NLS */
31 
32 #include <unistd.h>
33 #include <string.h>
34 #include <limits.h> /* for PATH_MAX */
35 
36 #define NTFS_BLOCK_SIZES	((int[2]){512, 0})
37 
38 #define NTFS_SIGNATURE		"NTFS"
39 
40 #define NTFSRESIZE_CMD_PATH	"ntfsresize"
41 #define NTFSCREATE_CMD_PATH	"mkntfs"
42 #define NTFSFIX_CMD_PATH	"ntfsfix"
43 #define NTFSCLONE_CMD_PATH	"ntfsclone"
44 
45 static PedFileSystemType ntfs_type;
46 
47 static char bigbuf[128*1024];	/* for command output storage */
48 
49 static PedGeometry*
ntfs_probe(PedGeometry * geom)50 ntfs_probe (PedGeometry* geom)
51 {
52 	char	buf[512];
53 
54 	PED_ASSERT(geom != NULL, return 0);
55 
56 	if (!ped_geometry_read (geom, buf, 0, 1))
57 		return 0;
58 
59 	if (strncmp (NTFS_SIGNATURE, buf + 3, strlen (NTFS_SIGNATURE)) == 0)
60 		return ped_geometry_new (geom->dev, geom->start,
61 					 PED_LE64_TO_CPU (*(uint64_t*)
62 						 	  (buf + 0x28)));
63 	else
64 		return NULL;
65 }
66 
67 #ifndef DISCOVER_ONLY
68 static int
ntfs_clobber(PedGeometry * geom)69 ntfs_clobber (PedGeometry* geom)
70 {
71 	char	buf[512];
72 
73 	PED_ASSERT(geom != NULL, return 0);
74 
75 	memset (buf, 0, sizeof(buf));
76 	return ped_geometry_write (geom, buf, 0, 1);
77 }
78 
79 static PedFileSystem*
ntfs_open(PedGeometry * geom)80 ntfs_open (PedGeometry* geom)
81 {
82 	PedFileSystem*		fs;
83 
84 	PED_ASSERT(geom != NULL, return 0);
85 
86 	fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
87 	if (!fs)
88 		return NULL;
89 
90 	fs->type = &ntfs_type;
91 	fs->geom = ped_geometry_duplicate (geom);
92 	fs->checked = 1; /* XXX */
93 	fs->type_specific = NULL;
94 
95 	return fs;
96 }
97 
98 /*
99  * Returns partition number (1..4) that contains geom, 0 otherwise.
100  */
101 static int
_get_partition_num_by_geom(const PedGeometry * geom)102 _get_partition_num_by_geom(const PedGeometry* geom)
103 {
104         PedDisk *disk;
105 	PedPartition *part;
106 	int partnum = 0;
107 
108 	PED_ASSERT(geom != NULL, return 0);
109 
110         disk = ped_disk_new (geom->dev);
111         if (!disk) {
112 		printf("_get_partition_num_by_geom: ped_disk_new failed!\n");
113 	}
114 	else {
115 		part = ped_disk_get_partition_by_sector (disk, geom->start);
116 		if (part == NULL) {
117 			printf("_get_partition_num_by_geom: "
118 				"ped_disk_get_partition_by_sector failed!\n");
119 		}
120 		else {
121 			if (part->num > 0)
122 				partnum = part->num;
123 		}
124 		ped_disk_destroy (disk);
125 	}
126 	return partnum;
127 }
128 
129 /*
130  * return the partition device name for geom in partpath.
131  * return 1 on success, 0 on failure.
132  */
133 static int
_get_part_device_path(const PedGeometry * geom,char * partpath,const int len)134 _get_part_device_path(const PedGeometry* geom, char *partpath, const int len)
135 {
136 	int partnum;
137 
138 	PED_ASSERT(geom != NULL, return 0);
139 	PED_ASSERT(partpath != NULL, return 0);
140 
141 	partnum = _get_partition_num_by_geom(geom);
142 	if (!partnum)
143 		return 0;
144 
145 	strncpy(partpath, geom->dev->path, len);
146 	/*
147 	 * XXX Solaris specific
148 	 * Create the path name to the *pn device, where n is the partition #
149 	 * geom->dev->path looks like this: "/devices/.../cmdk@0,0:q"
150 	 * or like this: "/dev/dsk/...p0"
151 	 * ":q" is the "/dev/dsk/...p0" device
152 	 * :r is p1, :s is p2, :t is p3, :u is p4
153 	 * 'q' + 1 == 'r'
154 	 * '0' + 1 == '1'
155 	 */
156 	partpath[strlen(partpath) -1] += partnum;
157 
158 	return 1;
159 }
160 
161 /*
162  * Executes cmd in a pipe.
163  * Returns -1 on popen failure or the return value from pclose.
164  * Saves the output from cmd in bigbuf for later display.
165  */
166 static int
_execute(const char * cmd)167 _execute(const char *cmd)
168 {
169 	FILE *fp;
170 	char buf[512];
171 	int szbigbuf;
172 
173 	PED_ASSERT(cmd != NULL, return 0);
174 
175 	fp = popen(cmd, "r");
176 	if (fp == NULL)
177 		return -1;
178 
179 	strcpy(bigbuf, "");
180 	szbigbuf = sizeof(bigbuf) -1;
181 
182 	while (fgets(buf, sizeof(buf), fp) != NULL) {
183 		if (szbigbuf > 0) {
184 			strncat(bigbuf, buf, szbigbuf);
185 			szbigbuf -= strlen(buf);
186 		}
187 	}
188 
189 	return pclose(fp);
190 }
191 
192 /*
193  * ./mkntfs -f -s 512 -S 63 -H 255 -p 0 /dev/dsk/c0d0p1
194  * Returns new fs on success, NULL on failure.
195  */
196 PedFileSystem*
ntfs_create(PedGeometry * geom,PedTimer * timer)197 ntfs_create (PedGeometry* geom, PedTimer* timer)
198 {
199 	int x;
200 	PedFileSystem* fs = NULL;
201 	char partpath[PATH_MAX];
202 	char cmd[PATH_MAX];
203 
204 	PED_ASSERT(geom != NULL, return 0);
205 	PED_ASSERT(timer != NULL, return 0);
206 
207 	ped_timer_reset (timer);
208 	ped_timer_update (timer, 0.0);
209 	ped_timer_set_state_name(timer, _("creating"));
210 
211 	if (_get_part_device_path(geom, partpath, sizeof(partpath)) == 0)
212 		goto error;
213 
214 	snprintf(cmd, sizeof(cmd), "%s -f -s %lld -S %d -H %d -p %lld %s",
215 		NTFSCREATE_CMD_PATH,
216 		geom->dev->sector_size,
217 		geom->dev->hw_geom.sectors,
218 		geom->dev->hw_geom.heads,
219 		(PedSector) 0,		/* partition start sector */
220 		partpath);
221 	printf("%s\n", cmd);
222 
223 	/*
224 	 * Use system() so the output that shows progress is displayed.
225 	 */
226 	ped_device_begin_external_access(geom->dev);
227 	x = system(cmd);
228 	ped_device_end_external_access(geom->dev);
229 
230 	if (x != 0) {
231 		goto error;
232 	}
233 
234 	fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
235 	if (!fs)
236 		goto error;
237 	fs->type = &ntfs_type;
238 	fs->geom = ped_geometry_duplicate (geom);
239 	fs->checked = 1; /* XXX */
240 	fs->type_specific = NULL;
241 
242 error:
243 	ped_timer_update (timer, 1.0);
244 	return fs;
245 }
246 
247 /*
248  * Returns 1 on success, 0 on failure.
249  */
250 static int
ntfs_close(PedFileSystem * fs)251 ntfs_close (PedFileSystem *fs)
252 {
253 	PED_ASSERT(fs != NULL, return 0);
254 
255 	ped_geometry_destroy (fs->geom);
256 	ped_free (fs);
257 
258 	return 1;
259 }
260 
261 /*
262  * ntfsfix /dev/dsk/c0d0p1
263  * Returns 1 on success, 0 on failure.
264  */
265 static int
ntfs_check(PedFileSystem * fs,PedTimer * timer)266 ntfs_check(PedFileSystem *fs, PedTimer *timer)
267 {
268 	int x;
269 	int ret = 0;
270 	char partpath[PATH_MAX];
271 	char cmd[PATH_MAX];
272 
273 	PED_ASSERT(fs != NULL, return 0);
274 	PED_ASSERT(timer != NULL, return 0);
275 
276 	ped_timer_reset(timer);
277 	ped_timer_set_state_name(timer, _("checking"));
278 	ped_timer_update(timer, 0.0);
279 
280 	if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0)
281 		goto error;
282 
283 	snprintf(cmd, sizeof(cmd), "%s %s",
284 		NTFSFIX_CMD_PATH, partpath);
285 	printf("%s\n", cmd);
286 
287 	/*
288 	 * Use system() so the output that shows progress is displayed.
289 	 */
290 	ped_device_begin_external_access(fs->geom->dev);
291 	x = system(cmd);
292 	ped_device_end_external_access(fs->geom->dev);
293 
294 	if (x == 0) {
295 		ret = 1; /* return success to the upper layer */
296 	}
297 	else {
298 		goto error;
299 	}
300 
301 error:
302 	ped_timer_update(timer, 1.0);
303 	return ret;
304 }
305 
306 /*
307  * Copy from source fs to destination geom.
308  * The destination partition must alreay exist.
309  * ntfsclone --overwrite destination-device source-device
310  * Returns new fs on success, NULL on failure.
311  */
312 static PedFileSystem*
ntfs_copy(const PedFileSystem * fs,PedGeometry * geom,PedTimer * timer)313 ntfs_copy(const PedFileSystem *fs, PedGeometry *geom, PedTimer *timer)
314 {
315 	int x;
316 	char spartpath[PATH_MAX];
317 	char dpartpath[PATH_MAX];
318 	char cmd[PATH_MAX];
319 	PedFileSystem *new_fs = NULL;
320 
321 	PED_ASSERT(fs != NULL, return 0);
322 	PED_ASSERT(geom != NULL, return 0);
323 	PED_ASSERT(timer != NULL, return 0);
324 
325 	ped_timer_reset(timer);
326 	ped_timer_set_state_name(timer, _("copying"));
327 	ped_timer_update(timer, 0.0);
328 
329 	if (_get_part_device_path(fs->geom, spartpath, sizeof(spartpath)) == 0)
330 		goto error;
331 
332 	if (_get_part_device_path(geom, dpartpath, sizeof(dpartpath)) == 0)
333 		goto error;
334 
335 	snprintf(cmd, sizeof(cmd), "%s --overwrite %s %s",
336 		NTFSCLONE_CMD_PATH, dpartpath, spartpath);
337 	printf("%s\n", cmd);
338 
339 	/*
340 	 * Use system() so the output that shows progress is displayed.
341 	 */
342 	ped_device_begin_external_access(geom->dev);
343 	x = system(cmd);
344 	ped_device_end_external_access(geom->dev);
345 
346 	if (x != 0) {
347 		goto error;
348 	}
349 
350 	if (!(new_fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem))))
351 		goto error;
352 
353 	new_fs->type = &ntfs_type;
354 	new_fs->geom = ped_geometry_duplicate(geom);
355 	new_fs->checked = 0;
356 	new_fs->type_specific = NULL;
357 
358 error:
359 	ped_timer_update(timer, 1.0);
360 	return new_fs;
361 }
362 
363 /*
364  * fs->geom has the current filesystem size in sectors.
365  * geom has the new, requested filesystem size in sectors.
366  *
367  * fs->geom->dev is the same object as geom->dev.
368  * geom->dev->path looks like this:
369  *   /dev/dsk/...p0
370  * or this:
371  *   /devices/.../cmdk@0,0:q
372  *
373  * The ntfsresize cmd wants the block disk device, not the raw one.
374  * It also wants the partition device, not the whole disk.
375  *
376  * Returns 1 on success, 0 on failure.
377  */
378 static int
ntfs_resize(PedFileSystem * fs,PedGeometry * geom,PedTimer * timer)379 ntfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
380 {
381 	int x;
382 	int ret = 0; /* this tells the upper layer NOT to resize partition */
383 	char partpath[PATH_MAX];
384 	char cmd[PATH_MAX];
385 
386 	PED_ASSERT(fs != NULL, return 0);
387 	PED_ASSERT(geom != NULL, return 0);
388 	PED_ASSERT(timer != NULL, return 0);
389 
390 	if (fs->geom->start != geom->start) {
391 		ped_exception_throw(PED_EXCEPTION_ERROR,
392 		                    PED_EXCEPTION_CANCEL,
393 		                    _("Sorry, can't move the start of "
394 		                      "ntfs partitions yet."));
395 		return 0;
396 	}
397 
398 	ped_timer_reset (timer);
399 	ped_timer_update (timer, 0.0);
400 
401 	if (fs->geom->length > geom->length) {
402 		ped_timer_set_state_name(timer, _("shrinking"));
403 	}
404 	else if (fs->geom->length < geom->length) {
405 		ped_timer_set_state_name(timer, _("enlarging"));
406 	}
407 	else {
408 		ped_timer_set_state_name(timer, _("no change"));
409 	}
410 
411 	if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0)
412 		goto error1;
413 
414 	ped_device_begin_external_access(geom->dev);
415 
416 	/*
417 	 * ntfsresize -f says don't worry about consistency flag
418 	 */
419 	snprintf(cmd, sizeof(cmd), "%s -f -i %s",
420 		NTFSRESIZE_CMD_PATH, partpath);
421 	printf("%s\n", cmd);
422 	x = _execute(cmd);
423 	if (x != 0) {
424 		printf("ntfsresize had this message:\n%s\n", bigbuf);
425 		goto error2;
426 	}
427 
428 	snprintf(cmd, sizeof(cmd), "%s -f -n -s %lld %s",
429 	    NTFSRESIZE_CMD_PATH,
430 	    geom->length * geom->dev->sector_size, partpath);
431 	printf("%s\n", cmd);
432 	x = _execute(cmd);
433 	if (x != 0) {
434 		printf("ntfsresize had this message:\n%s\n", bigbuf);
435 		goto error2;
436 	}
437 
438 	/*
439 	 * ntfsresize -f -f means don't ask "Are you sure?"
440 	 * Use system() so the output that shows progress is displayed.
441 	 */
442 	snprintf(cmd, sizeof(cmd), "%s -f -f -s %lld %s",
443 	    NTFSRESIZE_CMD_PATH,
444 	    geom->length * geom->dev->sector_size, partpath);
445 	printf("%s\n", cmd);
446 	x = system(cmd);
447 	if (x == 0) {
448 		ret = 1; /* this tells upper layer to resize the partition */
449 	}
450 	else {
451 		goto error2;
452 	}
453 
454 error2:
455 	ped_device_end_external_access(geom->dev);
456 error1:
457 	ped_timer_update (timer, 1.0);
458 	return ret;
459 }
460 
461 /*
462  * return the minimum resize size from the ntfsresize external cmd
463  * in blocks, 0 on error.
464  * Saves the output from cmd in bigbuf for later display.
465  */
466 static PedSector
_get_min_from_ntfsresize(const char * cmd)467 _get_min_from_ntfsresize(const char *cmd)
468 {
469 	FILE *fp;
470 	char buf[512];
471 	PedSector size = 0;
472 	int x;
473 	int szbigbuf;
474 
475 	PED_ASSERT(cmd != NULL, return 0);
476 
477 	fp = popen(cmd, "r");
478 	if (fp == NULL)
479 		return 0;
480 
481 	strcpy(bigbuf, "");
482 	szbigbuf = sizeof(bigbuf) -1;
483 
484 	while (fgets(buf, sizeof(buf), fp) != NULL) {
485 		if (szbigbuf > 0) {
486 			strncat(bigbuf, buf, szbigbuf);
487 			szbigbuf -= strlen(buf);
488 		}
489 		x = sscanf(buf, "You might resize at %lld", &size);
490 		if (x > 0)
491 			break;
492 	}
493 
494 	pclose(fp);
495 	return size;
496 }
497 
498 /*
499  * return the minimum resize size in blocks, fs->geom->length on error.
500  */
501 static PedSector
_get_min_resize_size(const PedFileSystem * fs)502 _get_min_resize_size (const PedFileSystem* fs)
503 {
504 	PedSector	max_length = fs->geom->length;
505 	PedSector	length;
506 	char partpath[PATH_MAX];
507 	char cmd[PATH_MAX];
508 
509 	PED_ASSERT(fs != NULL, return 0);
510 
511 	if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0)
512 		return max_length;
513 
514 	snprintf(cmd, sizeof(cmd), "%s -f -i %s",
515 		NTFSRESIZE_CMD_PATH, partpath);
516 
517 	length = _get_min_from_ntfsresize(cmd);
518 	if (length == 0) {
519 		printf("ntfsresize had this message:\n%s\n", bigbuf);
520 		return max_length;
521 	}
522 
523 	return (length / fs->geom->dev->sector_size);
524 }
525 
526 PedConstraint*
ntfs_get_copy_constraint(const PedFileSystem * fs,const PedDevice * dev)527 ntfs_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev)
528 {
529 	PedGeometry	full_dev;
530 
531 	PED_ASSERT(fs != NULL, return 0);
532 	PED_ASSERT(dev != NULL, return 0);
533 
534 	if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
535 		return NULL;
536 
537 	return ped_constraint_new (ped_alignment_any, ped_alignment_any,
538 				   &full_dev, &full_dev,
539 				   _get_min_resize_size (fs),
540 				   dev->length);
541 }
542 
543 PedConstraint*
ntfs_get_resize_constraint(const PedFileSystem * fs)544 ntfs_get_resize_constraint (const PedFileSystem* fs)
545 {
546 	PED_ASSERT(fs != NULL, return 0);
547 
548 	return ntfs_get_copy_constraint (fs, fs->geom->dev);
549 }
550 
551 #endif /* !DISCOVER_ONLY */
552 
553 static PedFileSystemOps ntfs_ops = {
554 	.probe =		ntfs_probe,
555 #ifndef DISCOVER_ONLY
556 	.clobber =	ntfs_clobber,
557 	.open =		ntfs_open,
558 	.create =		ntfs_create,
559 	.close =		ntfs_close,
560 	.check =		ntfs_check,
561 	.copy =		ntfs_copy,
562 	.resize =		ntfs_resize,
563 	.get_create_constraint =	NULL,
564 	.get_resize_constraint =	ntfs_get_resize_constraint,
565 	.get_copy_constraint =	ntfs_get_copy_constraint
566 #else
567 	.clobber =	NULL,
568 	.open =		NULL,
569 	.create =		NULL,
570 	.close =		NULL,
571 	.check =		NULL,
572 	.copy =		NULL,
573 	.resize =		NULL,
574 	.get_create_constraint =	NULL,
575 	.get_resize_constraint =	NULL,
576 	.get_copy_constraint =	NULL
577 #endif
578 };
579 
580 static PedFileSystemType ntfs_type = {
581 	.next =	NULL,
582 	.ops =	&ntfs_ops,
583 	.name =	"ntfs",
584 	.block_sizes = NTFS_BLOCK_SIZES
585 };
586 
587 void
ped_file_system_ntfs_init()588 ped_file_system_ntfs_init ()
589 {
590 	ped_file_system_type_register (&ntfs_type);
591 }
592 
593 void
ped_file_system_ntfs_done()594 ped_file_system_ntfs_done ()
595 {
596 	ped_file_system_type_unregister (&ntfs_type);
597 }
598 
599 
600