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