Improved NTFS support. --- parted-1.8.8/libparted/fs/ntfs/ntfs.c.orig +++ parted-1.8.8/libparted/fs/ntfs/ntfs.c @@ -20,6 +20,7 @@ #include <parted/parted.h> #include <parted/endian.h> +#include <parted/debug.h> #if ENABLE_NLS # include <libintl.h> @@ -29,16 +30,29 @@ #endif /* ENABLE_NLS */ #include <unistd.h> +#include <string.h> +#include <limits.h> /* for PATH_MAX */ #define NTFS_BLOCK_SIZES ((int[2]){512, 0}) #define NTFS_SIGNATURE "NTFS" +#define NTFSRESIZE_CMD_PATH "ntfsresize" +#define NTFSCREATE_CMD_PATH "mkntfs" +#define NTFSFIX_CMD_PATH "ntfsfix" +#define NTFSCLONE_CMD_PATH "ntfsclone" + +static PedFileSystemType ntfs_type; + +static char bigbuf[128*1024]; /* for command output storage */ + static PedGeometry* ntfs_probe (PedGeometry* geom) { char buf[512]; + PED_ASSERT(geom != NULL, return 0); + if (!ped_geometry_read (geom, buf, 0, 1)) return 0; @@ -56,18 +70,501 @@ { char buf[512]; - memset (buf, 0, 512); + PED_ASSERT(geom != NULL, return 0); + + memset (buf, 0, sizeof(buf)); return ped_geometry_write (geom, buf, 0, 1); } + +static PedFileSystem* +ntfs_open (PedGeometry* geom) +{ + PedFileSystem* fs; + + PED_ASSERT(geom != NULL, return 0); + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + return NULL; + + fs->type = &ntfs_type; + fs->geom = ped_geometry_duplicate (geom); + fs->checked = 1; /* XXX */ + fs->type_specific = NULL; + + return fs; +} + +/* + * Returns partition number (1..4) that contains geom, 0 otherwise. + */ +static int +_get_partition_num_by_geom(const PedGeometry* geom) +{ + PedDisk *disk; + PedPartition *part; + int partnum = 0; + + PED_ASSERT(geom != NULL, return 0); + + disk = ped_disk_new (geom->dev); + if (!disk) { + printf("_get_partition_num_by_geom: ped_disk_new failed!\n"); + } + else { + part = ped_disk_get_partition_by_sector (disk, geom->start); + if (part == NULL) { + printf("_get_partition_num_by_geom: " + "ped_disk_get_partition_by_sector failed!\n"); + } + else { + if (part->num > 0) + partnum = part->num; + } + ped_disk_destroy (disk); + } + return partnum; +} + +/* + * return the partition device name for geom in partpath. + * return 1 on success, 0 on failure. + */ +static int +_get_part_device_path(const PedGeometry* geom, char *partpath, const int len) +{ + int partnum; + + PED_ASSERT(geom != NULL, return 0); + PED_ASSERT(partpath != NULL, return 0); + + partnum = _get_partition_num_by_geom(geom); + if (!partnum) + return 0; + + strncpy(partpath, geom->dev->path, len); + /* + * XXX Solaris specific + * Create the path name to the *pn device, where n is the partition # + * geom->dev->path looks like this: "/devices/.../cmdk@0,0:q" + * or like this: "/dev/dsk/...p0" + * ":q" is the "/dev/dsk/...p0" device + * :r is p1, :s is p2, :t is p3, :u is p4 + * 'q' + 1 == 'r' + * '0' + 1 == '1' + */ + partpath[strlen(partpath) -1] += partnum; + + return 1; +} + +/* + * Executes cmd in a pipe. + * Returns -1 on popen failure or the return value from pclose. + * Saves the output from cmd in bigbuf for later display. + */ +static int +_execute(const char *cmd) +{ + FILE *fp; + char buf[512]; + int szbigbuf; + + PED_ASSERT(cmd != NULL, return 0); + + fp = popen(cmd, "r"); + if (fp == NULL) + return -1; + + strcpy(bigbuf, ""); + szbigbuf = sizeof(bigbuf) -1; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (szbigbuf > 0) { + strncat(bigbuf, buf, szbigbuf); + szbigbuf -= strlen(buf); + } + } + + return pclose(fp); +} + +/* + * ./mkntfs -f -s 512 -S 63 -H 255 -p 0 /dev/dsk/c0d0p1 + * Returns new fs on success, NULL on failure. + */ +PedFileSystem* +ntfs_create (PedGeometry* geom, PedTimer* timer) +{ + int x; + PedFileSystem* fs = NULL; + char partpath[PATH_MAX]; + char cmd[PATH_MAX]; + + PED_ASSERT(geom != NULL, return 0); + PED_ASSERT(timer != NULL, return 0); + + ped_timer_reset (timer); + ped_timer_update (timer, 0.0); + ped_timer_set_state_name(timer, _("creating")); + + if (_get_part_device_path(geom, partpath, sizeof(partpath)) == 0) + goto error; + + snprintf(cmd, sizeof(cmd), "%s -f -s %lld -S %d -H %d -p %lld %s", + NTFSCREATE_CMD_PATH, + geom->dev->sector_size, + geom->dev->hw_geom.sectors, + geom->dev->hw_geom.heads, + (PedSector) 0, /* partition start sector */ + partpath); + printf("%s\n", cmd); + + /* + * Use system() so the output that shows progress is displayed. + */ + ped_device_begin_external_access(geom->dev); + x = system(cmd); + ped_device_end_external_access(geom->dev); + + if (x != 0) { + goto error; + } + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + goto error; + fs->type = &ntfs_type; + fs->geom = ped_geometry_duplicate (geom); + fs->checked = 1; /* XXX */ + fs->type_specific = NULL; + +error: + ped_timer_update (timer, 1.0); + return fs; +} + +/* + * Returns 1 on success, 0 on failure. + */ +static int +ntfs_close (PedFileSystem *fs) +{ + PED_ASSERT(fs != NULL, return 0); + + ped_geometry_destroy (fs->geom); + ped_free (fs); + + return 1; +} + +/* + * ntfsfix /dev/dsk/c0d0p1 + * Returns 1 on success, 0 on failure. + */ +static int +ntfs_check(PedFileSystem *fs, PedTimer *timer) +{ + int x; + int ret = 0; + char partpath[PATH_MAX]; + char cmd[PATH_MAX]; + + PED_ASSERT(fs != NULL, return 0); + PED_ASSERT(timer != NULL, return 0); + + ped_timer_reset(timer); + ped_timer_set_state_name(timer, _("checking")); + ped_timer_update(timer, 0.0); + + if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0) + goto error; + + snprintf(cmd, sizeof(cmd), "%s %s", + NTFSFIX_CMD_PATH, partpath); + printf("%s\n", cmd); + + /* + * Use system() so the output that shows progress is displayed. + */ + ped_device_begin_external_access(fs->geom->dev); + x = system(cmd); + ped_device_end_external_access(fs->geom->dev); + + if (x == 0) { + ret = 1; /* return success to the upper layer */ + } + else { + goto error; + } + +error: + ped_timer_update(timer, 1.0); + return ret; +} + +/* + * Copy from source fs to destination geom. + * The destination partition must alreay exist. + * ntfsclone --overwrite destination-device source-device + * Returns new fs on success, NULL on failure. + */ +static PedFileSystem* +ntfs_copy(const PedFileSystem *fs, PedGeometry *geom, PedTimer *timer) +{ + int x; + char spartpath[PATH_MAX]; + char dpartpath[PATH_MAX]; + char cmd[PATH_MAX]; + PedFileSystem *new_fs = NULL; + + PED_ASSERT(fs != NULL, return 0); + PED_ASSERT(geom != NULL, return 0); + PED_ASSERT(timer != NULL, return 0); + + ped_timer_reset(timer); + ped_timer_set_state_name(timer, _("copying")); + ped_timer_update(timer, 0.0); + + if (_get_part_device_path(fs->geom, spartpath, sizeof(spartpath)) == 0) + goto error; + + if (_get_part_device_path(geom, dpartpath, sizeof(dpartpath)) == 0) + goto error; + + snprintf(cmd, sizeof(cmd), "%s --overwrite %s %s", + NTFSCLONE_CMD_PATH, dpartpath, spartpath); + printf("%s\n", cmd); + + /* + * Use system() so the output that shows progress is displayed. + */ + ped_device_begin_external_access(geom->dev); + x = system(cmd); + ped_device_end_external_access(geom->dev); + + if (x != 0) { + goto error; + } + + if (!(new_fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem)))) + goto error; + + new_fs->type = &ntfs_type; + new_fs->geom = ped_geometry_duplicate(geom); + new_fs->checked = 0; + new_fs->type_specific = NULL; + +error: + ped_timer_update(timer, 1.0); + return new_fs; +} + +/* + * fs->geom has the current filesystem size in sectors. + * geom has the new, requested filesystem size in sectors. + * + * fs->geom->dev is the same object as geom->dev. + * geom->dev->path looks like this: + * /dev/dsk/...p0 + * or this: + * /devices/.../cmdk@0,0:q + * + * The ntfsresize cmd wants the block disk device, not the raw one. + * It also wants the partition device, not the whole disk. + * + * Returns 1 on success, 0 on failure. + */ +static int +ntfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + int x; + int ret = 0; /* this tells the upper layer NOT to resize partition */ + char partpath[PATH_MAX]; + char cmd[PATH_MAX]; + + PED_ASSERT(fs != NULL, return 0); + PED_ASSERT(geom != NULL, return 0); + PED_ASSERT(timer != NULL, return 0); + + if (fs->geom->start != geom->start) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Sorry, can't move the start of " + "ntfs partitions yet.")); + return 0; + } + + ped_timer_reset (timer); + ped_timer_update (timer, 0.0); + + if (fs->geom->length > geom->length) { + ped_timer_set_state_name(timer, _("shrinking")); + } + else if (fs->geom->length < geom->length) { + ped_timer_set_state_name(timer, _("enlarging")); + } + else { + ped_timer_set_state_name(timer, _("no change")); + } + + if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0) + goto error1; + + ped_device_begin_external_access(geom->dev); + + /* + * ntfsresize -f says don't worry about consistency flag + */ + snprintf(cmd, sizeof(cmd), "%s -f -i %s", + NTFSRESIZE_CMD_PATH, partpath); + printf("%s\n", cmd); + x = _execute(cmd); + if (x != 0) { + printf("ntfsresize had this message:\n%s\n", bigbuf); + goto error2; + } + + snprintf(cmd, sizeof(cmd), "%s -f -n -s %lld %s", + NTFSRESIZE_CMD_PATH, + geom->length * geom->dev->sector_size, partpath); + printf("%s\n", cmd); + x = _execute(cmd); + if (x != 0) { + printf("ntfsresize had this message:\n%s\n", bigbuf); + goto error2; + } + + /* + * ntfsresize -f -f means don't ask "Are you sure?" + * Use system() so the output that shows progress is displayed. + */ + snprintf(cmd, sizeof(cmd), "%s -f -f -s %lld %s", + NTFSRESIZE_CMD_PATH, + geom->length * geom->dev->sector_size, partpath); + printf("%s\n", cmd); + x = system(cmd); + if (x == 0) { + ret = 1; /* this tells upper layer to resize the partition */ + } + else { + goto error2; + } + +error2: + ped_device_end_external_access(geom->dev); +error1: + ped_timer_update (timer, 1.0); + return ret; +} + +/* + * return the minimum resize size from the ntfsresize external cmd + * in blocks, 0 on error. + * Saves the output from cmd in bigbuf for later display. + */ +static PedSector +_get_min_from_ntfsresize(const char *cmd) +{ + FILE *fp; + char buf[512]; + PedSector size = 0; + int x; + int szbigbuf; + + PED_ASSERT(cmd != NULL, return 0); + + fp = popen(cmd, "r"); + if (fp == NULL) + return 0; + + strcpy(bigbuf, ""); + szbigbuf = sizeof(bigbuf) -1; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (szbigbuf > 0) { + strncat(bigbuf, buf, szbigbuf); + szbigbuf -= strlen(buf); + } + x = sscanf(buf, "You might resize at %lld", &size); + if (x > 0) + break; + } + + pclose(fp); + return size; +} + +/* + * return the minimum resize size in blocks, fs->geom->length on error. + */ +static PedSector +_get_min_resize_size (const PedFileSystem* fs) +{ + PedSector max_length = fs->geom->length; + PedSector length; + char partpath[PATH_MAX]; + char cmd[PATH_MAX]; + + PED_ASSERT(fs != NULL, return 0); + + if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0) + return max_length; + + snprintf(cmd, sizeof(cmd), "%s -f -i %s", + NTFSRESIZE_CMD_PATH, partpath); + + length = _get_min_from_ntfsresize(cmd); + if (length == 0) { + printf("ntfsresize had this message:\n%s\n", bigbuf); + return max_length; + } + + return (length / fs->geom->dev->sector_size); +} + +PedConstraint* +ntfs_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev) +{ + PedGeometry full_dev; + + PED_ASSERT(fs != NULL, return 0); + PED_ASSERT(dev != NULL, return 0); + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + return ped_constraint_new (ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + _get_min_resize_size (fs), + dev->length); +} + +PedConstraint* +ntfs_get_resize_constraint (const PedFileSystem* fs) +{ + PED_ASSERT(fs != NULL, return 0); + + return ntfs_get_copy_constraint (fs, fs->geom->dev); +} + #endif /* !DISCOVER_ONLY */ static PedFileSystemOps ntfs_ops = { probe: ntfs_probe, #ifndef DISCOVER_ONLY clobber: ntfs_clobber, + open: ntfs_open, + create: ntfs_create, + close: ntfs_close, + check: ntfs_check, + copy: ntfs_copy, + resize: ntfs_resize, + get_create_constraint: NULL, + get_resize_constraint: ntfs_get_resize_constraint, + get_copy_constraint: ntfs_get_copy_constraint #else clobber: NULL, -#endif open: NULL, create: NULL, close: NULL, @@ -77,6 +574,7 @@ get_create_constraint: NULL, get_resize_constraint: NULL, get_copy_constraint: NULL +#endif }; static PedFileSystemType ntfs_type = {