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* 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 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* 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 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 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 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* 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 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 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* 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 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 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 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* 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* 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 588 ped_file_system_ntfs_init () 589 { 590 ped_file_system_type_register (&ntfs_type); 591 } 592 593 void 594 ped_file_system_ntfs_done () 595 { 596 ped_file_system_type_unregister (&ntfs_type); 597 } 598 599