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