OpenSolaris_b135/lib/libparted/common/libparted/fs/ntfs/ntfs.c

Compare this file to the similar file:
Show the results in this format:

/*
    libparted - a library for manipulating disk partitions
    Copyright (C) 2000, 2007 Free Software Foundation, Inc.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <config.h>

#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>

#if ENABLE_NLS
#  include <libintl.h>
#  define _(String) dgettext (PACKAGE, String)
#else
#  define _(String) (String)
#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;

	if (strncmp (NTFS_SIGNATURE, buf + 3, strlen (NTFS_SIGNATURE)) == 0)
		return ped_geometry_new (geom->dev, geom->start,
					 PED_LE64_TO_CPU (*(uint64_t*)
						 	  (buf + 0x28)));
	else
		return NULL;
}

#ifndef DISCOVER_ONLY
static int
ntfs_clobber (PedGeometry* geom)
{
	char	buf[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,
	.open =		NULL,
	.create =		NULL,
	.close =		NULL,
	.check =		NULL,
	.copy =		NULL,
	.resize =		NULL,
	.get_create_constraint =	NULL,
	.get_resize_constraint =	NULL,
	.get_copy_constraint =	NULL
#endif 
};

static PedFileSystemType ntfs_type = {
	.next =	NULL,
	.ops =	&ntfs_ops,
	.name =	"ntfs",
	.block_sizes = NTFS_BLOCK_SIZES
};

void
ped_file_system_ntfs_init ()
{
	ped_file_system_type_register (&ntfs_type);
}

void
ped_file_system_ntfs_done ()
{
	ped_file_system_type_unregister (&ntfs_type);
}