diff options
author | Anant Narayanan <anant@kix.in> | 2006-09-14 15:18:45 +0000 |
---|---|---|
committer | Anant Narayanan <anant@kix.in> | 2006-09-14 15:18:45 +0000 |
commit | 232dbda915dfcfec99e5983b7f53d57d4498a6aa (patch) | |
tree | 4d54060e75f7f2df07de6e83004551b610ac9865 /libparted | |
download | parted-232dbda915dfcfec99e5983b7f53d57d4498a6aa.tar.gz |
Fix ChangeLog
git-svn-id: svn://svn.debian.org/svn/parted/upstream/trunk@820 2d424fd7-7fe2-0310-af74-8bc65edeb173
Diffstat (limited to 'libparted')
127 files changed, 47532 insertions, 0 deletions
diff --git a/libparted/ChangeLog b/libparted/ChangeLog new file mode 100644 index 0000000..5fb99b1 --- /dev/null +++ b/libparted/ChangeLog @@ -0,0 +1,160 @@ +2006-09-01 Darren Lavender <dl1@hppine99.gbr.hp.com> + * labels/gpt.c (gpt_read, _parse_header): added support for + LUN/device resize detection and optional GPT header correction. + + * labels/gpt.c (_generate_header, gpt_write): fixed off-by-one + error in GPT header that allowed for overlap between LBAs of + LastUsableLBA and PartitionEntryLBA in backup-GPT. + +2006-09-01 David Cantrell <dcantrell@redhat.com> + * fs/fat/fat.c (fat_probe): prevent SIGFPE when FAT sector size is 0. + + * arch/linux.c: define VIODASD_MAJOR. + * arch/linux.c (_device_probe_type): check for PED_DEVICE_VIODASD. + * arch/linux.c (linux_new): init PED_DEVICE_VIODASD. + * arch/linux.c (_flush_cache, linux_open): open in O_DIRECT mode on + non-zSeries. + * arch/linux.c (linux_read, linux_write): modify reading and writing + to work correctly in O_DIRECT mode. + + * exception.c: add ped_exception_get_handler(). + + * libparted.c (init_disk_types): call ped_disk_aix_init(). + * libparted.c (done_disk_types): call ped_disk_aix_done(). + + * labels/dos.c (msdos_probe): if AIX physical volume detected, fail. + + * labels/aix.c: add AIX disk label code from Matt Wilson. + * labels/aix.c: fix prototypes for aix_probe() and aix_alloc() so they + can be used in PedDiskOps. + + * labels/vtoc.c (vtoc_error): make error buffer large enough for errors. + + * labels/dasd.c: fix prototypes for dasd_probe() and dasd_alloc() so + they can be used in PedDiskOps. + * labels/dasd.c (dasd_probe): fix typos: dasd_initialize_anchor() -> + fdasd_initialize_anchor(), dasd_cleanup() -> fdasd_cleanup(). + * labels/dasd.c (dasd_read): fix typo: DEBUG -> PDEBUG. + +2006-08-31 David Cantrell <dcantrell@redhat.com> + * arch/linux.c: add DASD_MAJOR. + * arch/linux.c (_device_stat): check for DASD devices. + * arch/linux.c (init_dasd): add init_dasd() to gather device info + for DASD devices. + * arch/linux.c (init_generic): add dev->path to the error message so + users know what device had the failure. + + * labels/Makefile.am: add dasd.c, fdasd.c, fdasd.h, vtoc.c, and vtoc.h + + * labels/dasd.c: add DASD label code. + + * labels/fdasd.c: add fdasd code (used by dasd.c). + + * labels/vtoc.c: add VTOC code (wonderful EBCDIC util code). + + * libparted.c (init_disk_types): if on zSeries, call + ped_disk_dasd_init(). + + * libparted.c (done_disk_types): if on zSeries, call + ped_disk_dasd_done(). + +2006-08-30 David Cantrell <dcantrell@redhat.com> + * arch/linux.c: add SX8_MAJOR1 and SX8_MAJOR2. + * arch/linux.c (_is_sx8_major): determine if given major number falls + in the range of Promise SX8 devices. + * arch/linux.c (_device_stat): check for SX8 devices. + +2006-05-25 Leslie P. Polzer <polzer@gnu.org> + * fs/hfs/hfs.c (hfs_resize, hfsplus_resize): make hgee/hgms assertion + only when debugging is turned on. + + * fs/ext2/ext2.c (ext2_open): removed call to ext2_determine_itoffset; + also moving it from this file to ext2_resize.c. + * fs/ext2/ext2_resize.c (ext2_resize): added call to + ext2_determine_itoffset and show a warning if not successful. + + * labels/mac.c (strncasestr): search the whole type string for + 'driver', not just the strlen of 'driver'. This fixes the detection of + driver partitions on mac disklabels. + + * labels/mac.c (mac_partition_get_flag): added 'set 2 raid on/off' + capability for mac labels. + + * labels/mac.c (struct MacRawDisk, struct MacPartitionData): fixed + data types of some fields (do not need to be 64 bit wide) and updated + meaning of reserved spaces. + + * labels/mac.c (struct MacRawDisk, struct MacDeviceDriver, struct + MacDiskData, mac_alloc, _rawpart_has_driver, _rawpart_analyse, + mac_read, _pad_raw_part, _generate_raw_part, write_block_zero, + mac_write, mac_partition_new): fixes removal of driver partition; + this would previously crash MacOS 9. + +2006-05-15 Leslie P. Polzer <polzer@gnu.org> + * fs/ext2/ext2_block_relocator.c (ext2_block_relocator_mark): + turned exception into warning and let it provide additional info. + +2006-04-15 Leslie P. Polzer <polzer@gnu.org> + * labels/gpt.c (_parse_header): corrected endianness issue (one + conversion too much) + +2006-04-06 Jonathan duSaint <jon@rockgeeks.net> + * unit.c (ped_unit_get_size, parse_unit_suffix): added support for + binary units. + +2006-04-06 Leslie P. Polzer <polzer@gnu.org> + * arch/linux.c (_device_set_sector_size): handle sector size for + ioctl and printf correctly as "long long". + + * labels/dvh.c (dvh_probe), labels/bsd.c (bsd_probe), labels/mac.c + (mac_probe), labels/pc98.c (pc98_probe), labels/sun.c (sun_probe), + labels/dos.c (dos_probe), labels/loop.c (loop_probe): immediately + return negative probe when the device's logical sector size is not + equal to 512. + +2006-03-30 Leslie P. Polzer <polzer@gnu.org> + * fs/reiserfs/reiserfs.c: added missing block size initializer. + +2006-03-27 Leslie P. Polzer <polzer@gnu.org> + * arch/linux.c (linux_probe_all): probe standard devices before + /sys/block and /proc/partitions. + + * labels/gpt.c: added Apple GUID support. + +2006-03-08 Leslie P. Polzer <polzer@gnu.org> + * blkpg.h: moved to arch/. + +2006-03-03 Leslie P. Polzer <polzer@gnu.org> + * labels/Makefile.am (liblabels_la_SOURCES): added "dvh.h". + + * Makefile.am (libparted_la_SOURCES): fixed @OS@.c location. + + * arch/linux.c (_probe_standard_devices): probe IDE devices before + SCSI. + +2006-02-25 Leslie P. Polzer <polzer@gnu.org> + * labels/gpt.c (_read_header, gpt_read): fixed memory leaks. + +2006-02-23 Leslie P. Polzer <polzer@gnu.org> + * labels/gpt.c (pth_free): fixed illegal memory deallocation by + freeing reserved space before parent. + + * labels/gpt.c (pth_crc32): calculate crc32 from static size. + + * labels/gpt.c (pth_free): free reserved field before freeing parent + data structure. + + * labels/gpt.c (pth_crc32): calculate crc32 from static size. + +2006-02-20 Leslie P. Polzer <polzer@gnu.org> + * device.c: added ped_device_get_constraint. + + +----------------------------------------------------------------------------- + +This file is part of GNU Parted +Copyright (C) 1999 - 2006 Free Software Foundation Inc. + +This file may be modified and/or distributed without restriction. This is +not an invitation to misrepresent the history of GNU Parted. + diff --git a/libparted/Makefile.am b/libparted/Makefile.am new file mode 100644 index 0000000..52d3296 --- /dev/null +++ b/libparted/Makefile.am @@ -0,0 +1,40 @@ +# This file is part of GNU Parted +# Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +# +# This file may be modified and/or distributed without restriction. + +SUBDIRS = labels fs + +LIBS = @INTLLIBS@ @LIBS@ + +partedincludedir = -I$(top_srcdir)/include +lib_LTLIBRARIES = libparted.la +libparted_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -release $(LT_RELEASE) + +libparted_la_SOURCES = debug.c \ + device.c \ + exception.c \ + filesys.c \ + libparted.c \ + timer.c \ + unit.c \ + disk.c \ + cs/geom.c \ + cs/constraint.c \ + cs/natmath.c \ + arch/blkpg.h \ + arch/@OS@.c + +EXTRA_libparted_la_SOURCES = arch/linux.c \ + arch/gnu.c \ + arch/beos.c + +libparted_la_LIBADD = @OS_LIBS@ \ + @DL_LIBS@ \ + fs/libfs.la \ + labels/liblabels.la + +EXTRA_DIST = mbr.s + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/arch/beos.c b/libparted/arch/beos.c new file mode 100644 index 0000000..88f5dde --- /dev/null +++ b/libparted/arch/beos.c @@ -0,0 +1,641 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2006 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/beos.h> + +/* POSIX headers */ +#include <sys/stat.h> +#include <string.h> +#include <dirent.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +/* BeOS APIs */ +#include <drivers/Drivers.h> + +/* ZETA R1+ APIs */ +#if B_ZETA_VERSION >= B_ZETA_VERSION_1_0_0 +# include <device/ata_info.h> +#endif + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static void +_scan_for_disks(const char* path) +{ + char subdir[PATH_MAX]; + dirent_t* entp; + size_t pos; + DIR* dirp; + + if ((dirp=opendir(path)) != NULL) + { + /* Build first part of path */ + strcpy(subdir, path); + strcat(subdir, "/"); + pos = strlen(subdir); + + /* Check all directory entries.. */ + while((entp=readdir(dirp)) != NULL) + { + /* If they start with '.' just skip */ + if (entp->d_name[0] == '.') + continue; + + strcpy(subdir+pos, entp->d_name); + + /* /dev/disk/.../raw are the complete disks + we're interested in */ + if (strcmp(entp->d_name, "raw") == 0) + _ped_device_probe(subdir); + else /* If not 'raw', it most often will + be another subdir */ + _scan_for_disks(subdir); + } + + closedir(dirp); + } +} + +static void +_flush_cache(PedDevice* dev) +{ + int fd; + if ((fd=open(dev->path, O_RDONLY)) < 0) + { + ioctl(fd, B_FLUSH_DRIVE_CACHE); + close(fd); + } +} + +#if B_ZETA_VERSION >= B_ZETA_VERSION_1_0_0 +static int +_device_init_ata(PedDevice* dev) +{ + ata_device_infoblock ide_info; + int maxlen, len, idx, fd; + char buf[256]; + + /* Try and get information about device from ATA(PI) driver */ + if ((fd=open(dev->path, O_RDONLY)) < 0) + goto ata_error; + + if (ioctl(fd, B_ATA_GET_DEVICE_INFO, &ide_info, sizeof(ide_info)) < 0) + goto ata_error_close; + + close(fd); + + /* Copy 'logical' dimensions */ + dev->bios_geom.cylinders = ide_info.cylinders; + dev->bios_geom.heads = ide_info.heads; + dev->bios_geom.sectors = ide_info.sectors; + + /* Copy used dimensions */ + dev->hw_geom.cylinders = ide_info.current_cylinders; + dev->hw_geom.heads = ide_info.current_heads; + dev->hw_geom.sectors = ide_info.current_sectors; + + /* Copy total number of sectors */ + if (ide_info._48_bit_addresses_supported) + dev->length = ide_info.LBA48_total_sectors; + else if (ide_info.LBA_supported) + dev->length = ide_info.LBA_total_sectors; + else + dev->length = ide_info.cylinders * + ide_info.heads * + ide_info.sectors; + + dev->sector_size = + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + + /* Use sensible model */ + maxlen=sizeof(ide_info.model_number); + strncpy(buf, ide_info.model_number, maxlen); + buf[maxlen] = '\0'; + + for (len=-1, idx=maxlen-1; idx > 0 && len < 0; idx--) + if (buf[idx] > 0x20) + len = idx; + + buf[(len == -1) ? 0 : len+1] = '\0'; + + dev->model = strdup(buf); + + return PED_DEVICE_IDE; + +ata_error_close: + close(fd); + +ata_error: + return 0; +} +#endif + +static int +_device_init_generic_blkdev(PedDevice* dev) +{ + device_geometry bios, os; + int got_bios_info = 0; + int fd; + + /* Try and get information about device from ATA(PI) driver */ + if ((fd=open(dev->path, O_RDONLY)) < 0) + goto blkdev_error; + + /* B_GET_GEOMETRY is mandatory */ + if (ioctl(fd, B_GET_GEOMETRY, &os, sizeof(os)) < 0) + goto blkdev_error_close; + + /* B_GET_BIOS_GEOMETRY is optional */ + if (!ioctl(fd, B_GET_BIOS_GEOMETRY, &bios, sizeof(bios))) + got_bios_info = 1; + + close(fd); + + dev->hw_geom.cylinders = os.cylinder_count; + dev->hw_geom.heads = os.head_count; + dev->hw_geom.sectors = os.sectors_per_track; + + dev->sector_size = + dev->phys_sector_size = os.bytes_per_sector; + + if (got_bios_info) + { + dev->bios_geom.cylinders = bios.cylinder_count; + dev->bios_geom.heads = bios.head_count; + dev->bios_geom.sectors = bios.sectors_per_track; + } + else + dev->bios_geom = dev->hw_geom; + + dev->model = strdup(""); + + return PED_DEVICE_IDE; + +blkdev_error_close: + close(fd); + +blkdev_error: + return 0; +} + +static int +_device_init_blkdev(PedDevice* dev) +{ + int type; + +#if B_ZETA_VERSION >= B_ZETA_VERSION_1_0_0 + if (!(type=_device_init_ata(dev))) +#endif + type = _device_init_generic_blkdev(dev); + + return type; +} + +static int +_device_init_file(PedDevice* dev, struct stat* dev_statp) +{ + if (!dev_statp->st_size) + return 0; + + dev->sector_size = + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + + dev->length = dev_statp->st_size / PED_SECTOR_SIZE_DEFAULT; + + dev->bios_geom.cylinders = dev->length / (4 * 32); + dev->bios_geom.heads = 4; + dev->bios_geom.sectors = 32; + dev->hw_geom = dev->bios_geom; + + dev->model = strdup(_("Disk Image")); + + return PED_DEVICE_FILE; +} + +static int +_device_init(PedDevice* dev) +{ + struct stat dev_stat; + int type = 0; + + /* Check if we're a regular file */ + if (stat(dev->path, &dev_stat) < 0) + goto done; + + if (S_ISBLK(dev_stat.st_mode) || S_ISCHR(dev_stat.st_mode)) + type = _device_init_blkdev(dev); + else if (S_ISREG(dev_stat.st_mode)) + type = _device_init_file(dev,&dev_stat); + +done: + return type; +} + + +static PedDevice* +beos_new (const char* path) +{ + struct stat stat_info; + PedDevice* dev; + + PED_ASSERT(path != NULL, return NULL); + + dev = (PedDevice*) ped_malloc (sizeof (PedDevice)); + if (!dev) + goto error; + + dev->path = strdup(path); + if (!dev->path) + goto error_free_dev; + + dev->arch_specific + = (BEOSSpecific*) ped_malloc(sizeof(BEOSSpecific)); + if (dev->arch_specific == NULL) + goto error_free_path; + + dev->open_count = 0; + dev->read_only = 0; + dev->external_mode = 0; + dev->dirty = 0; + dev->boot_dirty = 0; + + if ((dev->type=_device_init(dev)) <= 0) + goto error_free_arch_specific; + + /* All OK! */ + return dev; + +error_free_arch_specific: + ped_free (dev->arch_specific); + +error_free_path: + ped_free (dev->path); + +error_free_dev: + ped_free (dev); + +error: + return NULL; +} + +static void +beos_destroy (PedDevice* dev) +{ + ped_free (dev->arch_specific); + ped_free (dev->path); + ped_free (dev->model); + ped_free (dev); +} + +static int +beos_is_busy (PedDevice* dev) +{ + return 1; +} + +static int +beos_open (PedDevice* dev) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + +retry: + arch_specific->fd = open(dev->path, O_RDWR); + if (arch_specific->fd == -1) { + char* rw_error_msg = strerror(errno); + + arch_specific->fd = open (dev->path, O_RDONLY); + if (arch_specific->fd == -1) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Error opening %s: %s"), + dev->path, strerror (errno)) + != PED_EXCEPTION_RETRY) { + return 0; + } else { + goto retry; + } + } else { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Unable to open %s read-write (%s). %s has " + "been opened read-only."), + dev->path, rw_error_msg, dev->path); + dev->read_only = 1; + } + } else { + dev->read_only = 0; + } + + _flush_cache (dev); + + return 1; +} + +static int +beos_refresh_open (PedDevice* dev) +{ + return 1; +} + +static int +beos_close (PedDevice* dev) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + + if (dev->dirty) + _flush_cache (dev); + + close (arch_specific->fd); + + return 1; +} + +static int +beos_refresh_close (PedDevice* dev) +{ + if (dev->dirty) + _flush_cache (dev); + + return 1; +} + +static int +beos_read (const PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + int status; + PedExceptionOption ex_status; + size_t read_length = count * dev->sector_size; + + PED_ASSERT(dev->sector_size % 512 == 0, return 0); + + /* First, try to seek */ + while(1) + { + if (lseek(arch_specific->fd, start * dev->sector_size, SEEK_SET) + == start * dev->sector_size) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for read on %s"), + strerror (errno), dev->path); + + switch (ex_status) + { + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_RETRY: + break /* out of switch */; + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + /* If we seeked ok, now is the time to read */ + while (1) + { + status = read(arch_specific->fd, buffer, read_length); + if (status == count * dev->sector_size) + break; + + if (status > 0) + { + read_length -= status; + buffer += status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (errno), + dev->path); + + switch (ex_status) + { + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_RETRY: + break; + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + return 1; +} + +static int +beos_write (PedDevice* dev, const void* buffer, PedSector start, + PedSector count) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + int status; + PedExceptionOption ex_status; + size_t write_length = count * dev->sector_size; + + PED_ASSERT(dev->sector_size % 512 == 0, return 0); + + if (dev->read_only) + { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't write to %s, because it is opened read-only."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + else + return 1; + } + + while(1) + { + if (lseek(arch_specific->fd,start * dev->sector_size,SEEK_SET) + == start * dev->sector_size) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for write on %s"), + strerror (errno), dev->path); + + switch (ex_status) + { + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_RETRY: + break; + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + +#ifdef READ_ONLY + printf ("ped_device_write (\"%s\", %p, %d, %d)\n", + dev->path, buffer, (int) start, (int) count); +#else + dev->dirty = 1; + while(1) + { + status = write (arch_specific->fd, buffer, write_length); + if (status == count * dev->sector_size) + break; + + if (status > 0) + { + write_length -= status; + buffer += status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + switch (ex_status) + { + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_RETRY: + break; + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } +#endif /* !READ_ONLY */ + + return 1; +} + +static PedSector +beos_check (PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + PedSector done = 0; + int status; + + PED_ASSERT(dev != NULL, return 0); + + if (lseek(arch_specific->fd, start * dev->sector_size, SEEK_SET) + != start * dev->sector_size) + return 0; + + for (done = 0; done < count; done += status / dev->sector_size) + { + status = read (arch_specific->fd, buffer, + (size_t) ((count-done) * dev->sector_size)); + if (status < 0) + break; + } + + return done; +} + +static int +beos_sync (PedDevice* dev) +{ + return 1; +} + +static int +beos_sync_fast (PedDevice* dev) +{ + return 1; +} + +/* Probe for all available disks */ +static void +beos_probe_all () +{ + /* For BeOS/ZETA/Haiku, all disks are published under /dev/disk */ + _scan_for_disks("/dev/disk"); +} + +static char* +beos_partition_get_path (const PedPartition* part) +{ + return NULL; +} + +static int +beos_partition_is_busy (const PedPartition* part) +{ + return 0; +} + +static int +beos_disk_commit (PedDisk* disk) +{ + return 0; +} + +static PedDeviceArchOps beos_dev_ops = { + _new: beos_new, + destroy: beos_destroy, + is_busy: beos_is_busy, + open: beos_open, + refresh_open: beos_refresh_open, + close: beos_close, + refresh_close: beos_refresh_close, + read: beos_read, + write: beos_write, + check: beos_check, + sync: beos_sync, + sync_fast: beos_sync_fast, + probe_all: beos_probe_all +}; + +static PedDiskArchOps beos_disk_ops = { + partition_get_path: beos_partition_get_path, + partition_is_busy: beos_partition_is_busy, + disk_commit: beos_disk_commit +}; + +PedArchitecture ped_beos_arch = { + dev_ops: &beos_dev_ops, + disk_ops: &beos_disk_ops +}; diff --git a/libparted/arch/blkpg.h b/libparted/arch/blkpg.h new file mode 100644 index 0000000..2a5e4fb --- /dev/null +++ b/libparted/arch/blkpg.h @@ -0,0 +1,64 @@ +#ifndef _LINUX_BLKPG_H +#define _LINUX_BLKPG_H + +/* + * Partition table and disk geometry handling + * + * A single ioctl with lots of subfunctions: + * + * Device number stuff: + * get_whole_disk() (given the device number of a partition, + * find the device number of the encompassing disk) + * get_all_partitions() (given the device number of a disk, return the + * device numbers of all its known partitions) + * + * Partition stuff: + * add_partition() + * delete_partition() + * test_partition_in_use() (also for test_disk_in_use) + * + * Geometry stuff: + * get_geometry() + * set_geometry() + * get_bios_drivedata() + * + * For today, only the partition stuff - aeb, 990515 + */ +#include <linux/ioctl.h> + +#define BLKPG _IO(0x12,105) + +/* The argument structure */ +struct blkpg_ioctl_arg { + int op; + int flags; + int datalen; + void *data; +}; + +/* The subfunctions (for the op field) */ +#define BLKPG_ADD_PARTITION 1 +#define BLKPG_DEL_PARTITION 2 + +/* Sizes of name fields. Unused at present. */ +#define BLKPG_DEVNAMELTH 64 +#define BLKPG_VOLNAMELTH 64 + +/* The data structure for ADD_PARTITION and DEL_PARTITION */ +struct blkpg_partition { + long long start; /* starting offset in bytes */ + long long length; /* length in bytes */ + int pno; /* partition number */ + char devname[BLKPG_DEVNAMELTH]; /* partition name, like sda5 or c0d1p2, + to be used in kernel messages */ + char volname[BLKPG_VOLNAMELTH]; /* volume label */ +}; + +#ifdef __KERNEL__ + +extern char * partition_name(kdev_t dev); +extern int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_BLKPG_H */ diff --git a/libparted/arch/gnu.c b/libparted/arch/gnu.c new file mode 100644 index 0000000..c83230b --- /dev/null +++ b/libparted/arch/gnu.c @@ -0,0 +1,878 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000, 2001, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#define _GNU_SOURCE 1 + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/gnu.h> + +#include <string.h> +#include <errno.h> +#include <hurd.h> +#include <hurd/fs.h> +#include <hurd/store.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static int +_device_get_sector_size (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + size_t store_block_size = arch_specific->store->block_size; + + return PED_SECTOR_SIZE_DEFAULT; +} + +static PedSector +_device_get_length (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + size_t store_blocks = arch_specific->store->blocks; + size_t store_block_size = arch_specific->store->block_size; + + return ((long long) store_blocks * store_block_size) / PED_SECTOR_SIZE_DEFAULT; +} + +static int +_device_probe_geometry (PedDevice* dev) +{ + PedSector cyl_size; + + dev->length = _device_get_length (dev); + if (!dev->length) + return 0; + + dev->sector_size = _device_get_sector_size (dev); + if (!dev->sector_size) + return 0; + + /* XXX: We have no way to get this! */ + dev->bios_geom.sectors = 63; + dev->bios_geom.heads = 255; + cyl_size = dev->bios_geom.sectors * dev->bios_geom.heads; + dev->bios_geom.cylinders = dev->length / cyl_size + * (dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->hw_geom = dev->bios_geom; + + return 1; +} + +static int +init_file (PedDevice* dev) +{ + PedExceptionOption ex_status; + +retry_open: + if (!ped_device_open (dev)) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_RETRY_CANCEL, + _("Unable to open %s."), + dev->path); + switch (ex_status) { + case PED_EXCEPTION_RETRY: + goto retry_open; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + goto error; + } + + return 0; + } + +retry_probe: + if (!_device_probe_geometry (dev)) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_RETRY_CANCEL, + _("Unable to probe store.")); + switch (ex_status) { + case PED_EXCEPTION_RETRY: + goto retry_probe; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + } + + return 0; + } + + dev->model = ""; + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +static void +_flush_cache (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + + if (dev->read_only) + return; + + /* Wait for a complete sync to finish. */ + file_sync (arch_specific->store->source, 1, 0); +} + +/* Initialize by allocating memory and filling in a few defaults, a + PedDevice structure. */ +static PedDevice* +_init_device (const char *path) +{ + PedDevice *dev; + GNUSpecific* arch_specific; + + dev = (PedDevice*) ped_malloc (sizeof (PedDevice)); + if (!dev) + goto error; + + dev->path = strdup (path); + if (!dev->path) + goto error_free_dev; + + dev->arch_specific + = (GNUSpecific*) ped_malloc (sizeof (GNUSpecific)); + if (!dev->arch_specific) + goto error_free_path; + + dev->type = PED_DEVICE_FILE; /* FIXME? */ + dev->open_count = 0; + dev->read_only = 0; + dev->external_mode = 0; + dev->dirty = 0; + dev->boot_dirty = 0; + + return dev; + +error_free_arch_specific: + ped_free (dev->arch_specific); +error_free_path: + ped_free (dev->path); +error_free_dev: + ped_free (dev); +error: + return NULL; +} + +static int +_kernel_reread_part_table (PedDevice* dev) +{ + /* XXX: We must wait for partfs to be finished. */ + return 1; +} + +/* Free the memory associated with a PedDevice structure. */ +static void +_done_device (PedDevice *dev) +{ + ped_free (dev->arch_specific); + ped_free (dev->path); + ped_free (dev); +} + +/* Release all resources that libparted owns in DEV. */ +static void +gnu_destroy (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + + if (arch_specific->consume) + store_free (arch_specific->store); + + _done_device (dev); +} + +static PedDevice* +gnu_new (const char* path) +{ + PedDevice* dev; + GNUSpecific* arch_specific; + error_t ro_err, rw_err; + int ispath; + + PED_ASSERT (path != NULL, return NULL); + + dev = _init_device (path); + if (!dev) + return NULL; + + arch_specific = GNU_SPECIFIC (dev); + arch_specific->consume = 1; + + retry_open: + /* Try read-write. */ + if (strchr (dev->path, '/') != NULL) { + /* We set this to prevent having to use strchr more then once. */ + ispath = 1; + + rw_err = store_open (dev->path, 0, NULL, &arch_specific->store); + } else { + rw_err = store_typed_open (dev->path, 0, NULL, &arch_specific->store); + } + + /* Try readonly. */ + if (rw_err) { + if (ispath) { + ro_err = store_open (dev->path, STORE_READONLY, NULL, + &arch_specific->store); + } else { + ro_err = store_typed_open (dev->path, STORE_READONLY, NULL, + &arch_specific->store); + } + + if (ro_err) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Error opening %s: %s"), + dev->path, strerror (ro_err)) + != PED_EXCEPTION_RETRY) { + return NULL; + } else + goto retry_open; + } else { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Unable to open %s read-write (%s). %s has " + "been opened read-only."), + dev->path, strerror (rw_err), dev->path); + dev->read_only = 1; + } + } else { + dev->read_only = 0; + } + + _flush_cache (dev); + + if (!init_file (dev)) { + gnu_destroy(dev); + return NULL; + } + + return dev; +} + +PedDevice* +ped_device_new_from_store (struct store *source) +{ + PedDevice* dev; + GNUSpecific* arch_specific; + + PED_ASSERT (source != NULL, return NULL); + + dev = _init_device (source->name ?: "(unknown)"); + if (!dev) + return NULL; + + arch_specific = GNU_SPECIFIC (dev); + arch_specific->store = source; + arch_specific->consume = 0; + + dev->read_only = source->flags & (STORE_READONLY|STORE_HARD_READONLY); + + if (!init_file (dev)) { + _done_device (dev); + return NULL; + } + + return dev; +} + +static int +gnu_is_busy (PedDevice* dev) +{ + return 0; +} + +static int +gnu_open (PedDevice* dev) +{ + return 1; +} + +static int +gnu_refresh_open (PedDevice* dev) +{ + return 1; +} + +static int +gnu_close (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + + _flush_cache (dev); + + if (dev->dirty && dev->type != PED_DEVICE_FILE) { + if (_kernel_reread_part_table (dev)) + dev->dirty = 0; + } + +#if 0 + if (dev->dirty && dev->boot_dirty && dev->type != PED_DEVICE_FILE) { + /* ouch! */ + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("The partition table cannot be re-read. This means " + "you need to reboot before mounting any " + "modified partitions. You also need to reinstall " + "your boot loader before you reboot (which may " + "require mounting modified partitions). It is " + "impossible do both things! So you'll need to " + "boot off a rescue disk, and reinstall your boot " + "loader from the rescue disk. Read section 4 of " + "the Parted User documentation for more " + "information.")); + return 1; + } + + if (dev->dirty && dev->type != PED_DEVICE_FILE) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("The partition table on %s cannot be re-read " + "(%s). This means the Hurd knows nothing about any " + "modifications you made. You should reboot your " + "computer before doing anything with %s."), + dev->path, strerror (errno), dev->path); + } + + if (dev->boot_dirty && dev->type != PED_DEVICE_FILE) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("You should reinstall your boot loader before " + "rebooting. Read section 4 of the Parted User " + "documentation for more information.")); + } +#endif + + return 1; +} + +static int +gnu_refresh_close (PedDevice* dev) +{ + _flush_cache (dev); + return 1; +} + +static int +gnu_read (PedDevice* dev, void* user_buffer, + PedSector device_start, PedSector count) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + error_t err; + PedExceptionOption ex_status; + size_t start; + size_t store_start_block; + /* In bytes. This can be larger than COUNT when store pages are + larger than PED_SECTOR_SIZE_DEFAULT. */ + size_t store_read_length; + char local_buffer[PED_SECTOR_SIZE_DEFAULT]; + void * store_read_buffer; + size_t have_read; + size_t read_offset; + size_t device_read_length = count * PED_SECTOR_SIZE_DEFAULT; + + start = device_start * PED_SECTOR_SIZE_DEFAULT; + if (PED_SECTOR_SIZE_DEFAULT != arch_specific->store->block_size) { + store_start_block = start / arch_specific->store->block_size; + store_read_length = (device_read_length + + arch_specific->store->block_size - 1) + / arch_specific->store->block_size; + } else { + store_start_block = device_start; + store_read_length = device_read_length; + } + + read_offset = start + - store_start_block * arch_specific->store->block_size; + + if (store_read_length % arch_specific->store->block_size != 0) + store_read_length = store_read_length + + arch_specific->store->block_size + - store_read_length % arch_specific->store->block_size; + +retry: + have_read = 0; + while (1) { + size_t did_read; + size_t offset; + + store_read_buffer = local_buffer; + did_read = sizeof (local_buffer); + + err = store_read (arch_specific->store, store_start_block, + store_read_length - have_read, + &store_read_buffer, &did_read); + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (err), + dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + goto retry; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + memcpy (user_buffer + have_read - read_offset, + store_read_buffer + + (have_read >= read_offset + ? 0 : read_offset - have_read), + have_read + did_read > device_read_length + read_offset + ? device_read_length + read_offset - have_read + : did_read); + + if (store_read_buffer != local_buffer) + vm_deallocate (mach_task_self (), + (long) store_read_buffer, did_read); + + have_read += did_read; + store_start_block += did_read + / arch_specific->store->block_size; + + if (have_read >= device_read_length) + break; + } + + return 1; +} + +static int +gnu_write (PedDevice* dev, const void* buffer, PedSector start, PedSector count) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + error_t err; + PedExceptionOption ex_status; + void * temp; + char local_buffer[PED_SECTOR_SIZE_DEFAULT]; + size_t did_read; + size_t did_write; + + /* Map a disk sector to a store sector. */ + #define PED_TO_STORE(store, sector) (((sector) * PED_SECTOR_SIZE_DEFAULT) \ + / (store)->block_size) + + if (dev->read_only) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't write to %s, because it is opened read-only."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + else + return 1; + } + +#ifdef READ_ONLY + printf ("ped_device_write (\"%s\", %p, %d, %d)\n", + dev->path, buffer, (int) start, (int) count); +#else + dev->dirty = 1; + + /* If the first ``device'' block (PedSector) is not aligned on a + store block, then we need to fetch the old block, copy in the + overlaping area and finally, write the modified data out to the + store. */ + if ((PED_SECTOR_SIZE_DEFAULT * start) % arch_specific->store->block_size + != 0) { + size_t write_offset; + size_t flushing; + +doggy_first_block_read: + /* We do not bother looping as we are only reading a + single block. */ + temp = local_buffer; + did_read = sizeof (local_buffer); + err = store_read (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + arch_specific->store->block_size, &temp, + &did_read); + if (! err && did_read != arch_specific->store->block_size) + err = EIO; + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + goto doggy_first_block_read; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + write_offset = (start * PED_SECTOR_SIZE_DEFAULT) + % arch_specific->store->block_size; + flushing = arch_specific->store->block_size - write_offset; + if (flushing > count * PED_SECTOR_SIZE_DEFAULT) + flushing = count * PED_SECTOR_SIZE_DEFAULT; + + memcpy (temp + write_offset, buffer, flushing); + +doggy_first_block_write: + err = store_write (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + temp, arch_specific->store->block_size, + &did_write); + if (! err && did_write != arch_specific->store->block_size) + err = EIO; + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + goto doggy_first_block_write; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + if (temp != local_buffer) + vm_deallocate ( + mach_task_self (), + (long) temp, + did_read); + return 0; + } + } + + start += flushing / PED_SECTOR_SIZE_DEFAULT; + count -= flushing / PED_SECTOR_SIZE_DEFAULT; + buffer += write_offset; + + if (temp != local_buffer) + vm_deallocate (mach_task_self (), (long) temp, + did_read); + + if (count == 0) + return 1; + } + + while (count > 0 + && count >= arch_specific->store->block_size / PED_SECTOR_SIZE_DEFAULT) { + err = store_write (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + buffer, count * PED_SECTOR_SIZE_DEFAULT, + &did_write); + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + continue; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + start += did_write / PED_SECTOR_SIZE_DEFAULT; + count -= did_write / PED_SECTOR_SIZE_DEFAULT; + buffer += did_write; + } + + if (count == 0) + return 1; + + /* We are now left with (strictly) less then a store block to write + to disk. Thus, we read the block, overlay the buffer and flush. */ + PED_ASSERT (count * PED_SECTOR_SIZE + < arch_specific->store->block_size, return 0); + +doggy_last_block_read: + /* We do not bother looping as we are only reading a + single block. */ + temp = local_buffer; + did_read = sizeof (local_buffer); + err = store_read (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + arch_specific->store->block_size, &temp, + &did_read); + if (! err && did_read != arch_specific->store->block_size) + err = EIO; + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + goto doggy_last_block_read; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + memcpy (temp, buffer, count * PED_SECTOR_SIZE_DEFAULT); + +doggy_last_block_write: + err = store_write (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + temp, arch_specific->store->block_size, + &did_write); + if (! err && did_write != arch_specific->store->block_size) + err = EIO; + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + goto doggy_last_block_write; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + if (temp != local_buffer) + vm_deallocate (mach_task_self (), + (long) temp, + did_read); + return 0; + } + } + +#endif /* !READ_ONLY */ + return 1; +} + +/* TODO: returns the number of sectors that are ok. + */ +static PedSector +gnu_check (PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + int status; + int done = 0; + + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + PED_ASSERT (buffer != NULL, return 0); + + return count; +} + +static int +gnu_sync (PedDevice* dev) +{ + GNUSpecific* arch_specific; + error_t err; + PedExceptionOption ex_status; + static char *last_failure = NULL; + + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + + arch_specific = GNU_SPECIFIC (dev); + + if (dev->read_only || ! dev->dirty) + return 1; + + while (1) { + err = file_sync (arch_specific->store->source, 1, 0); + if (! err || err == EOPNOTSUPP || err == EPERM + || (last_failure && strcmp (last_failure, dev->path) == 0)) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s trying to sync %s to disk"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + if (last_failure) + ped_free (last_failure); + last_failure = strdup (dev->path); + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + return 1; +} + +static int +probe_standard_devices () +{ + _ped_device_probe ("/dev/sd0"); + _ped_device_probe ("/dev/sd1"); + _ped_device_probe ("/dev/sd2"); + _ped_device_probe ("/dev/sd3"); + _ped_device_probe ("/dev/sd4"); + _ped_device_probe ("/dev/sd5"); + + _ped_device_probe ("/dev/hd0"); + _ped_device_probe ("/dev/hd1"); + _ped_device_probe ("/dev/hd2"); + _ped_device_probe ("/dev/hd3"); + _ped_device_probe ("/dev/hd4"); + _ped_device_probe ("/dev/hd5"); + _ped_device_probe ("/dev/hd6"); + _ped_device_probe ("/dev/hd7"); + + return 1; +} + +static void +gnu_probe_all () +{ + probe_standard_devices (); +} + +static char* +gnu_partition_get_path (const PedPartition* part) +{ + const char* dev_path = part->disk->dev->path; + int result_len = strlen (dev_path) + 16; + char* result; + + result = (char*) ped_malloc (result_len); + if (!result) + return NULL; + snprintf (result, result_len, "%s%d", dev_path, part->num); + return result; +} + +static int +gnu_partition_is_busy (const PedPartition* part) +{ + return 0; +} + +static int +gnu_disk_commit (PedDisk* disk) +{ + return 1; +} + +static PedDeviceArchOps gnu_dev_ops = { + _new: gnu_new, + destroy: gnu_destroy, + is_busy: gnu_is_busy, + open: gnu_open, + refresh_open: gnu_refresh_open, + close: gnu_close, + refresh_close: gnu_refresh_close, + read: gnu_read, + write: gnu_write, + check: gnu_check, + sync: gnu_sync, + sync_fast: gnu_sync, + probe_all: gnu_probe_all +}; + +static PedDiskArchOps gnu_disk_ops = { + partition_get_path: gnu_partition_get_path, + partition_is_busy: gnu_partition_is_busy, + disk_commit: gnu_disk_commit +}; + +PedArchitecture ped_gnu_arch = { + dev_ops: &gnu_dev_ops, + disk_ops: &gnu_disk_ops +}; + diff --git a/libparted/arch/linux.c b/libparted/arch/linux.c new file mode 100644 index 0000000..cf480e2 --- /dev/null +++ b/libparted/arch/linux.c @@ -0,0 +1,2105 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999 - 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/* we need this for O_DIRECT on Linux, it's in the glibc headers */ +#define __GNU_SOURCE + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/linux.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syscall.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> /* for uname() */ +#include <scsi/scsi.h> + +#include "blkpg.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#ifndef __NR__llseek +#define __NR__llseek 140 +#endif + +#ifndef SCSI_IOCTL_SEND_COMMAND +#define SCSI_IOCTL_SEND_COMMAND 1 +#endif + +/* from <linux/hdreg.h> */ +#define HDIO_GETGEO 0x0301 /* get device geometry */ +#define HDIO_GET_IDENTITY 0x030d /* get IDE identification info */ + +#include <parted/vtoc.h> +#include <parted/fdasd.h> + +#if defined(O_DIRECT) && (!defined(__s390__) || !defined(__s390x__)) +#define RD_MODE (O_RDONLY | O_DIRECT) +#define WR_MODE (O_WRONLY | O_DIRECT) +#define RW_MODE (O_RDWR | O_DIRECT) +#else +#define RD_MODE (O_RDONLY) +#define WR_MODE (O_WRONLY) +#define RW_MODE (O_RDWR) +#endif + +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; + +struct ata7_sectinfo { + int valid1:1; + int valid2:1; + int rsv:26; + int multiplier:4; +}; + +/* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */ +struct hd_driveid { + unsigned short config; /* lots of obsolete bit flags */ + unsigned short cyls; /* "physical" cyls */ + unsigned short reserved2; /* reserved (word 2) */ + unsigned short heads; /* "physical" heads */ + unsigned short track_bytes; /* unformatted bytes per track */ + unsigned short sector_bytes; /* unformatted bytes per sector */ + unsigned short sectors; /* "physical" sectors per track */ + unsigned short vendor0; /* vendor unique */ + unsigned short vendor1; /* vendor unique */ + unsigned short vendor2; /* vendor unique */ + unsigned char serial_no[20]; /* 0 = not_specified */ + unsigned short buf_type; + unsigned short buf_size; /* 512 byte increments; + 0 = not_specified */ + unsigned short ecc_bytes; /* for r/w long cmds; + 0 = not_specified */ + unsigned char fw_rev[8]; /* 0 = not_specified */ + char model[40]; /* 0 = not_specified */ + unsigned char max_multsect; /* 0=not_implemented */ + unsigned char vendor3; /* vendor unique */ + unsigned short dword_io; /* 0=not_implemented; 1=implemented */ + unsigned char vendor4; /* vendor unique */ + unsigned char capability; /* bits 0:DMA 1:LBA 2:IORDYsw + 3:IORDYsup*/ + unsigned short reserved50; /* reserved (word 50) */ + unsigned char vendor5; /* vendor unique */ + unsigned char tPIO; /* 0=slow, 1=medium, 2=fast */ + unsigned char vendor6; /* vendor unique */ + unsigned char tDMA; /* 0=slow, 1=medium, 2=fast */ + unsigned short field_valid; /* bits 0:cur_ok 1:eide_ok */ + unsigned short cur_cyls; /* logical cylinders */ + unsigned short cur_heads; /* logical heads */ + unsigned short cur_sectors; /* logical sectors per track */ + unsigned short cur_capacity0; /* logical total sectors on drive */ + unsigned short cur_capacity1; /* (2 words, misaligned int) */ + unsigned char multsect; /* current multiple sector count */ + unsigned char multsect_valid; /* when (bit0==1) multsect is ok */ + unsigned int lba_capacity; /* total number of sectors */ + unsigned short dma_1word; /* single-word dma info */ + unsigned short dma_mword; /* multiple-word dma info */ + unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */ + unsigned short eide_dma_min; /* min mword dma cycle time (ns) */ + unsigned short eide_dma_time; /* recommended mword dma cycle + time (ns) */ + unsigned short eide_pio; /* min cycle time (ns), no IORDY */ + unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */ + unsigned short words69_70[2]; /* reserved words 69-70 */ + /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */ + unsigned short words71_74[4]; /* reserved words 71-74 */ + unsigned short queue_depth; /* */ + unsigned short words76_79[4]; /* reserved words 76-79 */ + unsigned short major_rev_num; /* */ + unsigned short minor_rev_num; /* */ + unsigned short command_set_1; /* bits 0:Smart 1:Security 2:Removable + 3:PM */ + unsigned short command_set_2; /* bits 14:Smart Enabled 13:0 zero */ + unsigned short cfsse; /* command set-feature supported + extensions */ + unsigned short cfs_enable_1; /* command set-feature enabled */ + unsigned short cfs_enable_2; /* command set-feature enabled */ + unsigned short csf_default; /* command set-feature default */ + unsigned short dma_ultra; /* */ + unsigned short word89; /* reserved (word 89) */ + unsigned short word90; /* reserved (word 90) */ + unsigned short CurAPMvalues; /* current APM values */ + unsigned short word92; /* reserved (word 92) */ + unsigned short hw_config; /* hardware config */ + unsigned short words94_105[12];/* reserved words 94-105 */ + struct ata7_sectinfo ata7_sectinfo; /* ATAPI/ATA7 physical and logical + sector size */ + unsigned short words107_116[10];/* reserved words 107-116 */ + unsigned int logical_sectsize;/* ATAPI/ATA7 logical sector size */ + unsigned short words119_125[7];/* reserved words 119-125 */ + unsigned short last_lun; /* reserved (word 126) */ + unsigned short word127; /* reserved (word 127) */ + unsigned short dlf; /* device lock function + * 15:9 reserved + * 8 security level 1:max 0:high + * 7:6 reserved + * 5 enhanced erase + * 4 expire + * 3 frozen + * 2 locked + * 1 en/disabled + * 0 capability + */ + unsigned short csfo; /* current set features options + * 15:4 reserved + * 3 auto reassign + * 2 reverting + * 1 read-look-ahead + * 0 write cache + */ + unsigned short words130_155[26];/* reserved vendor words 130-155 */ + unsigned short word156; + unsigned short words157_159[3]; /* reserved vendor words 157-159 */ + unsigned short words160_255[95];/* reserved words 160-255 */ +}; + +/* from <linux/fs.h> */ +#define BLKRRPART _IO(0x12,95) /* re-read partition table */ +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +#define BLKSSZGET _IO(0x12,104) /* get block device sector size */ +#define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */ +#define BLKSETLASTSECT _IO(0x12,109) /* set last sector of block device */ + +/* return device size in bytes (u64 *arg) */ +#define BLKGETSIZE64 _IOR(0x12,114,size_t) + +struct blkdev_ioctl_param { + unsigned int block; + size_t content_length; + char * block_contents; +}; + +/* from <linux/major.h> */ +#define IDE0_MAJOR 3 +#define IDE1_MAJOR 22 +#define IDE2_MAJOR 33 +#define IDE3_MAJOR 34 +#define IDE4_MAJOR 56 +#define IDE5_MAJOR 57 +#define SCSI_CDROM_MAJOR 11 +#define SCSI_DISK0_MAJOR 8 +#define SCSI_DISK1_MAJOR 65 +#define SCSI_DISK2_MAJOR 66 +#define SCSI_DISK3_MAJOR 67 +#define SCSI_DISK4_MAJOR 68 +#define SCSI_DISK5_MAJOR 69 +#define SCSI_DISK6_MAJOR 70 +#define SCSI_DISK7_MAJOR 71 +#define COMPAQ_SMART2_MAJOR 72 +#define COMPAQ_SMART2_MAJOR1 73 +#define COMPAQ_SMART2_MAJOR2 74 +#define COMPAQ_SMART2_MAJOR3 75 +#define COMPAQ_SMART2_MAJOR4 76 +#define COMPAQ_SMART2_MAJOR5 77 +#define COMPAQ_SMART2_MAJOR6 78 +#define COMPAQ_SMART2_MAJOR7 79 +#define COMPAQ_SMART_MAJOR 104 +#define COMPAQ_SMART_MAJOR1 105 +#define COMPAQ_SMART_MAJOR2 106 +#define COMPAQ_SMART_MAJOR3 107 +#define COMPAQ_SMART_MAJOR4 108 +#define COMPAQ_SMART_MAJOR5 109 +#define COMPAQ_SMART_MAJOR6 110 +#define COMPAQ_SMART_MAJOR7 111 +#define DAC960_MAJOR 48 +#define ATARAID_MAJOR 114 +#define I2O_MAJOR1 80 +#define I2O_MAJOR2 81 +#define I2O_MAJOR3 82 +#define I2O_MAJOR4 83 +#define I2O_MAJOR5 84 +#define I2O_MAJOR6 85 +#define I2O_MAJOR7 86 +#define I2O_MAJOR8 87 +#define UBD_MAJOR 98 +#define DASD_MAJOR 94 +#define VIODASD_MAJOR 112 +#define SX8_MAJOR1 160 +#define SX8_MAJOR2 161 + +#define SCSI_BLK_MAJOR(M) ( \ + (M) == SCSI_DISK0_MAJOR \ + || (M) == SCSI_CDROM_MAJOR \ + || ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR)) + +static char* _device_get_part_path (PedDevice* dev, int num); +static int _partition_is_mounted_by_path (const char* path); + +static int +_is_ide_major (int major) +{ + switch (major) { + case IDE0_MAJOR: + case IDE1_MAJOR: + case IDE2_MAJOR: + case IDE3_MAJOR: + case IDE4_MAJOR: + case IDE5_MAJOR: + return 1; + + default: + return 0; + } +} + +static int +_is_cpqarray_major (int major) +{ + return ((COMPAQ_SMART2_MAJOR <= major && major <= COMPAQ_SMART2_MAJOR7) + || (COMPAQ_SMART_MAJOR <= major && major <= COMPAQ_SMART_MAJOR7)); +} + +static int +_is_i2o_major (int major) +{ + return (I2O_MAJOR1 <= major && major <= I2O_MAJOR8); +} + +static int +_is_sx8_major (int major) +{ + return (SX8_MAJOR1 <= major && major <= SX8_MAJOR2); +} + +static int +_device_stat (PedDevice* dev, struct stat * dev_stat) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + + while (1) { + if (!stat (dev->path, dev_stat)) { + return 1; + } else { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Could not stat device %s - %s."), + dev->path, + strerror (errno)) + != PED_EXCEPTION_RETRY) + return 0; + } + } +} + +static int +_device_probe_type (PedDevice* dev) +{ + struct stat dev_stat; + int dev_major; + int dev_minor; + PedExceptionOption ex_status; + + if (!_device_stat (dev, &dev_stat)) + return 0; + + if (!S_ISBLK(dev_stat.st_mode)) { + dev->type = PED_DEVICE_FILE; + return 1; + } + + dev_major = major (dev_stat.st_rdev); + dev_minor = minor (dev_stat.st_rdev); + + if (SCSI_BLK_MAJOR (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_SCSI; + } else if (_is_ide_major (dev_major) && (dev_minor % 0x40 == 0)) { + dev->type = PED_DEVICE_IDE; + } else if (dev_major == DAC960_MAJOR && (dev_minor % 0x8 == 0)) { + dev->type = PED_DEVICE_DAC960; + } else if (dev_major == ATARAID_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_ATARAID; + } else if (dev_major == DASD_MAJOR && (dev_minor % 0x4 == 0)) { + dev->type = PED_DEVICE_DASD; + } else if (dev_major == VIODASD_MAJOR && (dev_minor % 0x8 == 0)) { + dev->type = PED_DEVICE_VIODASD; + } else if (_is_sx8_major(dev_major) && (dev_minor % 0x20 == 0)) { + dev->type = PED_DEVICE_SX8; + } else if (_is_i2o_major (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_I2O; + } else if (_is_cpqarray_major (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_CPQARRAY; + } else if (dev_major == UBD_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_UBD; + } else { + dev->type = PED_DEVICE_UNKNOWN; + } + + return 1; +} + +static int +_get_linux_version () +{ + static int kver = -1; + + struct utsname uts; + int major; + int minor; + int teeny; + + if (kver != -1) + return kver; + + if (uname (&uts)) + return kver = 0; + if (sscanf (uts.release, "%u.%u.%u", &major, &minor, &teeny) != 3) + return kver = 0; + + return kver = KERNEL_VERSION (major, minor, teeny); +} + +static int +_have_devfs () +{ + static int have_devfs = -1; + struct stat sb; + + if (have_devfs != -1) + return have_devfs; + + /* the presence of /dev/.devfsd implies that DevFS is active */ + if (stat("/dev/.devfsd", &sb) < 0) + return have_devfs = 0; + + return have_devfs = S_ISCHR(sb.st_mode) ? 1 : 0; +} + +static void +_device_set_sector_size (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int sector_size; + + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + + PED_ASSERT (dev->open_count, return); + + if (_get_linux_version() < KERNEL_VERSION (2,3,0)) { + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + return; + } + + if (ioctl (arch_specific->fd, BLKSSZGET, §or_size)) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Could not determine sector size for %s: %s.\n" + "Using the default sector size (%lld)."), + dev->path, strerror (errno), PED_SECTOR_SIZE_DEFAULT); + } else { + dev->sector_size = (long long)sector_size; + } + + /* Return PED_SECTOR_SIZE_DEFAULT for DASDs. */ + if (dev->type == PED_DEVICE_DASD) { + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + } + + if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Device %s has a logical sector size of %lld. Not " + "all parts of GNU Parted support this at the moment, " + "and the working code is HIGHLY EXPERIMENTAL.\n"), + dev->path, dev->sector_size); + } +} + +static int +_kernel_has_blkgetsize64(void) +{ + int version = _get_linux_version(); + + if (version >= KERNEL_VERSION (2,5,4)) return 1; + if (version < KERNEL_VERSION (2,5,0) && + version >= KERNEL_VERSION (2,4,18)) return 1; + return 0; +} + +/* TODO: do a binary search if BLKGETSIZE doesn't work?! */ +static PedSector +_device_get_length (PedDevice* dev) +{ + unsigned long size; + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + uint64_t bytes=0; + + + PED_ASSERT (dev->open_count > 0, return 0); + PED_ASSERT (dev->sector_size % 512 == 0, return 0); + + if (_kernel_has_blkgetsize64()) { + if (ioctl(arch_specific->fd, BLKGETSIZE64, &bytes) == 0) { + return bytes / dev->sector_size; + } + } + + if (ioctl (arch_specific->fd, BLKGETSIZE, &size)) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unable to determine the size of %s (%s)."), + dev->path, + strerror (errno)); + return 0; + } + + return size; +} + +static int +_device_probe_geometry (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct stat dev_stat; + struct hd_geometry geometry; + + if (!_device_stat (dev, &dev_stat)) + return 0; + PED_ASSERT (S_ISBLK (dev_stat.st_mode), return 0); + + _device_set_sector_size (dev); + + dev->length = _device_get_length (dev); + if (!dev->length) + return 0; + + /* The GETGEO ioctl is no longer useful (as of linux 2.6.x). We could + * still use it in 2.4.x, but this is contentious. Perhaps we should + * move to EDD. */ + dev->bios_geom.sectors = 63; + dev->bios_geom.heads = 255; + dev->bios_geom.cylinders + = dev->length / (63 * 255); + + /* FIXME: what should we put here? (TODO: discuss on linux-kernel) */ + if (!ioctl (arch_specific->fd, HDIO_GETGEO, &geometry) + && geometry.sectors && geometry.heads) { + dev->hw_geom.sectors = geometry.sectors; + dev->hw_geom.heads = geometry.heads; + dev->hw_geom.cylinders + = dev->length / (dev->hw_geom.heads + * dev->hw_geom.sectors); + } else { + dev->hw_geom = dev->bios_geom; + } + + return 1; +} + +static char* +strip_name(char* str) +{ + int i; + int end = 0; + + for (i = 0; str[i] != 0; i++) { + if (!isspace (str[i]) + || (isspace (str[i]) && !isspace (str[i+1]) && str[i+1])) { + str [end] = str[i]; + end++; + } + } + str[end] = 0; + return strdup (str); +} + +static int +init_ide (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct stat dev_stat; + int dev_major; + struct hd_driveid hdi; + PedExceptionOption ex_status; + char hdi_buf[41]; + int sector_multiplier = 0; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + dev_major = major (dev_stat.st_rdev); + + if (!ped_device_open (dev)) + goto error; + + if (ioctl (arch_specific->fd, HDIO_GET_IDENTITY, &hdi)) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Could not get identity of device %s - %s"), + dev->path, strerror (errno)); + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_IGNORE: + dev->model = strdup(_("Generic IDE")); + } + } else { + /* hdi.model is not guaranteed to be NULL terminated */ + memcpy (hdi_buf, hdi.model, 40); + hdi_buf[40] = '\0'; + dev->model = strip_name (hdi_buf); + + if (!hdi.ata7_sectinfo.valid1 && hdi.ata7_sectinfo.valid2) + sector_multiplier = hdi.ata7_sectinfo.multiplier; + else + sector_multiplier = 1; + + if (sector_multiplier != 1) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Device %s has multiple (%d) logical sectors " + "per physical sector.\n" + "GNU Parted supports this EXPERIMENTALLY for " + "some special disk label/file system " + "combinations, e.g. GPT and ext2/3.\n" + "Please consult the web site for up-to-date " + "information."), + dev->path, sector_multiplier); + + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_IGNORE: + ; + } + } + + /* XXX sector_size has not been set yet! */ + /* dev->phys_sector_size = dev->sector_size + * sector_multiplier;*/ + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + } + + if (!_device_probe_geometry (dev)) + goto error_close_dev; + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +/* This function reads the /sys entry named "file" for device "dev". */ +static char * +read_device_sysfs_file (PedDevice *dev, const char *file) +{ + FILE *f; + char name_buf[128]; + char buf[256]; + + snprintf (name_buf, 127, "/sys/block/%s/device/%s", + basename (dev->path), file); + + if ((f = fopen (name_buf, "r")) == NULL) + return NULL; + + fgets (buf, 255, f); + fclose (f); + return strip_name (buf); +} + +/* This function sends a query to a SCSI device for vendor and product + * information. It uses the deprecated SCSI_IOCTL_SEND_COMMAND to + * issue this query. + */ +static int +scsi_query_product_info (PedDevice* dev, char **vendor, char **product) +{ + /* The following are defined by the SCSI-2 specification. */ + typedef struct _scsi_inquiry_cmd + { + uint8_t op; + uint8_t lun; /* bits 5-7 denote the LUN */ + uint8_t page_code; + uint8_t reserved; + uint8_t alloc_length; + uint8_t control; + } __attribute__((packed)) scsi_inquiry_cmd_t; + + typedef struct _scsi_inquiry_data + { + uint8_t peripheral_info; + uint8_t device_info; + uint8_t version_info; + uint8_t _field1; + uint8_t additional_length; + uint8_t _reserved1; + uint8_t _reserved2; + uint8_t _field2; + uint8_t vendor_id[8]; + uint8_t product_id[16]; + uint8_t product_revision[4]; + uint8_t vendor_specific[20]; + uint8_t _reserved3[40]; + } __attribute__((packed)) scsi_inquiry_data_t; + + struct scsi_arg + { + unsigned int inlen; + unsigned int outlen; + + union arg_data + { + scsi_inquiry_data_t out; + scsi_inquiry_cmd_t in; + } data; + } arg; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + char buf[32]; + + *vendor = NULL; + *product = NULL; + + memset (&arg, 0x00, sizeof(struct scsi_arg)); + arg.inlen = 0; + arg.outlen = sizeof(scsi_inquiry_data_t); + arg.data.in.op = INQUIRY; + arg.data.in.lun = dev->host << 5; + arg.data.in.alloc_length = sizeof(scsi_inquiry_data_t); + arg.data.in.page_code = 0; + arg.data.in.reserved = 0; + arg.data.in.control = 0; + + if (ioctl (arch_specific->fd, SCSI_IOCTL_SEND_COMMAND, &arg) < 0) + return 0; + + memcpy (buf, arg.data.out.vendor_id, 8); + buf[8] = '\0'; + *vendor = strip_name (buf); + + memcpy (buf, arg.data.out.product_id, 16); + buf[16] = '\0'; + *product = strip_name (buf); + + return 1; +} + +/* This function provides the vendor and product name for a SCSI device. + * It supports both the modern /sys interface and direct queries + * via the deprecated ioctl, SCSI_IOCTL_SEND_COMMAND. + */ +static int +scsi_get_product_info (PedDevice* dev, char **vendor, char **product) +{ + *vendor = read_device_sysfs_file (dev, "vendor"); + *product = read_device_sysfs_file (dev, "model"); + if (*vendor && *product) + return 1; + + return scsi_query_product_info (dev, vendor, product); +} + +static int +init_scsi (PedDevice* dev) +{ + struct scsi_idlun + { + uint32_t dev_id; + uint32_t host_unique_id; + } idlun; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + char* vendor; + char* product; + + if (!ped_device_open (dev)) + goto error; + + if (ioctl (arch_specific->fd, SCSI_IOCTL_GET_IDLUN, &idlun) < 0) { + dev->host = 0; + dev->did = 0; + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Error initialising SCSI device %s - %s"), + dev->path, strerror (errno)) + != PED_EXCEPTION_IGNORE) + goto error_close_dev; + if (!_device_probe_geometry (dev)) + goto error_close_dev; + ped_device_close (dev); + return 1; + } + + dev->host = idlun.host_unique_id; + dev->did = idlun.dev_id; + + dev->model = (char*) ped_malloc (8 + 16 + 2); + if (!dev->model) + goto error_close_dev; + + if (scsi_get_product_info (dev, &vendor, &product)) { + sprintf (dev->model, "%.8s %.16s", vendor, product); + ped_free (vendor); + ped_free (product); + } else { + strcpy (dev->model, "Generic SCSI"); + } + + if (!_device_probe_geometry (dev)) + goto error_close_dev; + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +static int +init_file (PedDevice* dev) +{ + struct stat dev_stat; + + if (!_device_stat (dev, &dev_stat)) + goto error; + if (!ped_device_open (dev)) + goto error; + + if (S_ISBLK(dev_stat.st_mode)) + dev->length = _device_get_length (dev); + else + dev->length = dev_stat.st_size / 512; + if (dev->length <= 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The device %s has zero length, and can't possibly " + "store a file system or partition table. Perhaps " + "you selected the wrong device?"), + dev->path); + goto error_close_dev; + } + + ped_device_close (dev); + + dev->bios_geom.cylinders = dev->length / 4 / 32; + dev->bios_geom.heads = 4; + dev->bios_geom.sectors = 32; + dev->hw_geom = dev->bios_geom; + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + dev->model = strdup (""); + + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +static int +init_dasd (PedDevice* dev, char* model_name) +{ + struct stat dev_stat; + PedExceptionOption ex_status; + dasd_information_t dasd_info; + struct hd_geometry geo; + int f, blksize = 0; + char *errstr = 0; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + if (!ped_device_open (dev)) + goto error; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + + PED_ASSERT (S_ISBLK (dev_stat.st_mode), return 0); + + _device_set_sector_size (dev); + if (!dev->sector_size) + goto error_close_dev; + + dev->length = _device_get_length (dev); + if (!dev->length) + goto error_close_dev; + + if (!ioctl (arch_specific->fd, HDIO_GETGEO, &geo)) { + dev->hw_geom.sectors = geo.sectors; + dev->hw_geom.heads = geo.heads; + dev->hw_geom.cylinders = dev->length + / (dev->hw_geom.heads * dev->hw_geom.sectors) + / (dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->bios_geom = dev->hw_geom; + } else { + dev->bios_geom.sectors = 12; + dev->bios_geom.heads = 15; + dev->bios_geom.cylinders = dev->length + / (dev->hw_geom.heads * dev->hw_geom.sectors) + / (dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->hw_geom = dev->bios_geom; + } + + dev->model = strdup (model_name); + + ped_device_close (dev); + return 1; + +except: + ped_exception_throw ( PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + errstr ); + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +static int +init_generic (PedDevice* dev, char* model_name) +{ + struct stat dev_stat; + PedExceptionOption ex_status; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + if (!ped_device_open (dev)) + goto error; + + ped_exception_fetch_all (); + if (_device_probe_geometry (dev)) { + ped_exception_leave_all (); + } else { + /* hack to allow use of files, for testing */ + ped_exception_catch (); + ped_exception_leave_all (); + + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Unable to determine geometry of " + "file/device %s. You should not use Parted " + "unless you REALLY know what you're doing!"), + dev->path); + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_IGNORE: + ; // just workaround for gcc 3.0 + } + + /* what should we stick in here? */ + dev->length = dev_stat.st_size / PED_SECTOR_SIZE_DEFAULT; + dev->bios_geom.cylinders = dev->length / 4 / 32; + dev->bios_geom.heads = 4; + dev->bios_geom.sectors = 32; + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + } + + dev->model = strdup (model_name); + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +static PedDevice* +linux_new (const char* path) +{ + PedDevice* dev; + + PED_ASSERT (path != NULL, return NULL); + + dev = (PedDevice*) ped_malloc (sizeof (PedDevice)); + if (!dev) + goto error; + + dev->path = strdup (path); + if (!dev->path) + goto error_free_dev; + + dev->arch_specific + = (LinuxSpecific*) ped_malloc (sizeof (LinuxSpecific)); + if (!dev->arch_specific) + goto error_free_path; + + dev->open_count = 0; + dev->read_only = 0; + dev->external_mode = 0; + dev->dirty = 0; + dev->boot_dirty = 0; + + if (!_device_probe_type (dev)) + goto error_free_arch_specific; + + switch (dev->type) { + case PED_DEVICE_IDE: + if (!init_ide (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_SCSI: + if (!init_scsi (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_DAC960: + if (!init_generic (dev, _("DAC960 RAID controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_SX8: + if (!init_generic (dev, _("Promise SX8 SATA Device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_DASD: + if (!init_dasd (dev, _("IBM S390 DASD drive"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_VIODASD: + if (!init_generic (dev, _("IBM iSeries Virtual DASD"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_CPQARRAY: + if (!init_generic (dev, _("Compaq Smart Array"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_ATARAID: + if (!init_generic (dev, _("ATARAID Controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_I2O: + if (!init_generic (dev, _("I2O Controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_UBD: + if (!init_generic (dev, _("User-Mode Linux UBD"))) + goto error_free_dev; + break; + + case PED_DEVICE_FILE: + if (!init_file (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_UNKNOWN: + if (!init_generic (dev, _("Unknown"))) + goto error_free_arch_specific; + break; + + default: + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("ped_device_new() Unsupported device type")); + goto error_free_arch_specific; + } + return dev; + +error_free_arch_specific: + ped_free (dev->arch_specific); +error_free_path: + ped_free (dev->path); +error_free_dev: + ped_free (dev); +error: + return NULL; +} + +static void +linux_destroy (PedDevice* dev) +{ + ped_free (dev->arch_specific); + ped_free (dev->path); + ped_free (dev->model); + ped_free (dev); +} + +static int +linux_is_busy (PedDevice* dev) +{ + int i; + char* part_name; + + if (_partition_is_mounted_by_path (dev->path)) + return 1; + + for (i = 0; i < 32; i++) { + int status; + + part_name = _device_get_part_path (dev, i); + if (!part_name) + return 1; + status = _partition_is_mounted_by_path (part_name); + ped_free (part_name); + + if (status) + return 1; + } + + return 0; +} + +/* we need to flush the master device, and all the partition devices, + * because there is no coherency between the caches. + * We should only flush unmounted partition devices, because: + * - there is never a need to flush them (we're not doing IO there) + * - flushing a device that is mounted causes unnecessary IO, and can + * even screw journaling & friends up. Even cause oopsen! + */ +static void +_flush_cache (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int i; + + if (dev->read_only) + return; + dev->dirty = 0; + + ioctl (arch_specific->fd, BLKFLSBUF); + + for (i = 1; i < 16; i++) { + char* name; + int fd; + + name = _device_get_part_path (dev, i); + if (!name) + break; + if (!_partition_is_mounted_by_path (name)) { + fd = open (name, WR_MODE, 0); + if (fd > 0) { + ioctl (fd, BLKFLSBUF); + close (fd); + } + } + ped_free (name); + } +} + +static int +linux_open (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + +retry: + arch_specific->fd = open (dev->path, RW_MODE); + + if (arch_specific->fd == -1) { + char* rw_error_msg = strerror (errno); + + arch_specific->fd = open (dev->path, RD_MODE); + + if (arch_specific->fd == -1) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Error opening %s: %s"), + dev->path, strerror (errno)) + != PED_EXCEPTION_RETRY) { + return 0; + } else { + goto retry; + } + } else { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Unable to open %s read-write (%s). %s has " + "been opened read-only."), + dev->path, rw_error_msg, dev->path); + dev->read_only = 1; + } + } else { + dev->read_only = 0; + } + + _flush_cache (dev); + + return 1; +} + +static int +linux_refresh_open (PedDevice* dev) +{ + return 1; +} + +static int +linux_close (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + + if (dev->dirty) + _flush_cache (dev); + close (arch_specific->fd); + return 1; +} + +static int +linux_refresh_close (PedDevice* dev) +{ + if (dev->dirty) + _flush_cache (dev); + return 1; +} + +#if SIZEOF_OFF_T < 8 + +static _syscall5(int,_llseek, + unsigned int, fd, + unsigned long, offset_high, + unsigned long, offset_low, + loff_t*, result, + unsigned int, origin) + +loff_t +llseek (unsigned int fd, loff_t offset, unsigned int whence) +{ + loff_t result; + int retval; + + retval = _llseek(fd, + ((unsigned long long)offset) >> 32, + ((unsigned long long)offset) & 0xffffffff, + &result, + whence); + return (retval==-1 ? (loff_t) retval : result); +} + +#endif /* SIZEOF_OFF_T < 8 */ + +static int +_device_seek (const PedDevice* dev, PedSector sector) +{ + LinuxSpecific* arch_specific; + + PED_ASSERT (dev->sector_size % 512 == 0, return 0); + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + + arch_specific = LINUX_SPECIFIC (dev); + +#if SIZEOF_OFF_T < 8 + if (sizeof (off_t) < 8) { + loff_t pos = (loff_t)(sector * dev->sector_size); + return llseek (arch_specific->fd, pos, SEEK_SET) == pos; + } else +#endif + { + off_t pos = sector * dev->sector_size; + return lseek (arch_specific->fd, pos, SEEK_SET) == pos; + } +} + +static int +_read_lastoddsector (const PedDevice* dev, void* buffer) +{ + LinuxSpecific* arch_specific; + struct blkdev_ioctl_param ioctl_param; + + PED_ASSERT(dev != NULL, return 0); + PED_ASSERT(buffer != NULL, return 0); + + arch_specific = LINUX_SPECIFIC (dev); + +retry: + ioctl_param.block = 0; /* read the last sector */ + ioctl_param.content_length = dev->sector_size; + ioctl_param.block_contents = buffer; + + if (ioctl(arch_specific->fd, BLKGETLASTSECT, &ioctl_param) == -1) { + PedExceptionOption opt; + opt = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (errno), dev->path); + + if (opt == PED_EXCEPTION_CANCEL) + return 0; + if (opt == PED_EXCEPTION_RETRY) + goto retry; + } + + return 1; +} + +static int +linux_read (const PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int status; + PedExceptionOption ex_status; + size_t read_length = count * dev->sector_size; + void* diobuf; + + PED_ASSERT (dev->sector_size % 512 == 0, return 0); + + if (_get_linux_version() < KERNEL_VERSION (2,6,0)) { + /* Kludge. This is necessary to read/write the last + block of an odd-sized disk, until Linux 2.5.x kernel fixes. + */ + if (dev->type != PED_DEVICE_FILE && (dev->length & 1) + && start + count - 1 == dev->length - 1) + return ped_device_read (dev, buffer, start, count - 1) + && _read_lastoddsector ( + dev, buffer + (count-1) * 512); + } + while (1) { + if (_device_seek (dev, start)) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for read on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + + if (posix_memalign(&diobuf, PED_SECTOR_SIZE_DEFAULT, + count * PED_SECTOR_SIZE_DEFAULT) != 0) + return 0; + + while (1) { + status = read (arch_specific->fd, diobuf, read_length); + if (status > 0) + memcpy(buffer, diobuf, status); + if (status == count * dev->sector_size) break; + if (status > 0) { + read_length -= status; + buffer += status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (errno), + dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + free(diobuf); + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + free(diobuf); + return 0; + } + } + free(diobuf); + + return 1; +} + +static int +_write_lastoddsector (PedDevice* dev, const void* buffer) +{ + LinuxSpecific* arch_specific; + struct blkdev_ioctl_param ioctl_param; + + PED_ASSERT(dev != NULL, return 0); + PED_ASSERT(buffer != NULL, return 0); + + arch_specific = LINUX_SPECIFIC (dev); + +retry: + ioctl_param.block = 0; /* write the last sector */ + ioctl_param.content_length = dev->sector_size; + ioctl_param.block_contents = (void*) buffer; + + if (ioctl(arch_specific->fd, BLKSETLASTSECT, &ioctl_param) == -1) { + PedExceptionOption opt; + opt = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + if (opt == PED_EXCEPTION_CANCEL) + return 0; + if (opt == PED_EXCEPTION_RETRY) + goto retry; + } + + return 1; +} + +static int +linux_write (PedDevice* dev, const void* buffer, PedSector start, + PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int status; + PedExceptionOption ex_status; + size_t write_length = count * dev->sector_size; + void* diobuf; + void* diobuf_start; + + PED_ASSERT(dev->sector_size % 512 == 0, return 0); + + if (dev->read_only) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't write to %s, because it is opened read-only."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + else + return 1; + } + + if (_get_linux_version() < KERNEL_VERSION (2,6,0)) { + /* Kludge. This is necessary to read/write the last + block of an odd-sized disk, until Linux 2.5.x kernel fixes. + */ + if (dev->type != PED_DEVICE_FILE && (dev->length & 1) + && start + count - 1 == dev->length - 1) + return ped_device_write (dev, buffer, start, count - 1) + && _write_lastoddsector ( + dev, buffer + (count-1) * 512); + } + while (1) { + if (_device_seek (dev, start)) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + +#ifdef READ_ONLY + printf ("ped_device_write (\"%s\", %p, %d, %d)\n", + dev->path, buffer, (int) start, (int) count); +#else + dev->dirty = 1; + if (posix_memalign(&diobuf, PED_SECTOR_SIZE_DEFAULT, + count * PED_SECTOR_SIZE_DEFAULT) != 0) + return 0; + memcpy(diobuf, buffer, count * PED_SECTOR_SIZE_DEFAULT); + diobuf_start = diobuf; + while (1) { + status = write (arch_specific->fd, diobuf, write_length); + if (status == count * dev->sector_size) break; + if (status > 0) { + write_length -= status; + diobuf += status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + free(diobuf_start); + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + free(diobuf_start); + return 0; + } + } + free(diobuf_start); +#endif /* !READ_ONLY */ + return 1; +} + +/* returns the number of sectors that are ok. + */ +static PedSector +linux_check (PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + PedSector done = 0; + int status; + void* diobuf; + + PED_ASSERT(dev != NULL, return 0); + + if (!_device_seek (dev, start)) + return 0; + + if (posix_memalign(&diobuf, PED_SECTOR_SIZE_DEFAULT, + count * PED_SECTOR_SIZE_DEFAULT) != 0) + return 0; + + for (done = 0; done < count; done += status / dev->sector_size) { + status = read (arch_specific->fd, diobuf, + (size_t) ((count-done) * dev->sector_size)); + if (status > 0) + memcpy(buffer, diobuf, status); + if (status < 0) + break; + } + free(diobuf); + + return done; +} + +static int +_do_fsync (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int status; + PedExceptionOption ex_status; + + while (1) { + status = fsync (arch_specific->fd); + if (status >= 0) break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + return 1; +} + +static int +linux_sync (PedDevice* dev) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + + if (dev->read_only) + return 1; + if (!_do_fsync (dev)) + return 0; + _flush_cache (dev); + return 1; +} + +static int +linux_sync_fast (PedDevice* dev) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + + if (dev->read_only) + return 1; + if (!_do_fsync (dev)) + return 0; + /* no cache flush... */ + return 1; +} + +static inline int +_compare_digit_state (char ch, int need_digit) +{ + return !!isdigit (ch) == need_digit; +} + +/* matches the regexp "[^0-9]+[0-9]+[^0-9]+[0-9]+$". + * Motivation: accept devices looking like /dev/rd/c0d0, but + * not looking like /dev/hda1 and /dev/rd/c0d0p1 + */ +static int +_match_rd_device (const char* name) +{ + const char* pos; + int state; + + /* exclude directory names from test */ + pos = strrchr(name, '/') ?: name; + + /* states: + * 0 non-digits + * 1 digits + * 2 non-digits + * 3 digits + */ + for (state = 0; state < 4; state++) { + int want_digits = (state % 2 == 1); + do { + if (!*pos) + return 0; + if (!_compare_digit_state (*pos, want_digits)) + return 0; + pos++; + } while (_compare_digit_state (*pos, want_digits)); + } + + return *pos == 0; +} + +static int +_probe_proc_partitions () +{ + FILE* proc_part_file; + int major, minor, size; + char buf [512]; + char part_name [256]; + char dev_name [256]; + + proc_part_file = fopen ("/proc/partitions", "r"); + if (!proc_part_file) + return 0; + + fgets (buf, 256, proc_part_file); + fgets (buf, 256, proc_part_file); + + while (fgets (buf, 512, proc_part_file) + && sscanf (buf, "%d %d %d %255s", &major, &minor, &size, + part_name) == 4) { + /* Heuristic for telling partitions and devices apart + * Probably needs to be improved + */ + if (!_match_rd_device (part_name) + && isdigit (part_name [strlen (part_name) - 1])) + continue; + + strcpy (dev_name, "/dev/"); + strcat (dev_name, part_name); + _ped_device_probe (dev_name); + } + + fclose (proc_part_file); + return 1; +} + +struct _entry { + const char *name; + size_t len; +}; + +static int +_skip_entry (const char *name) +{ + struct _entry *i; + static struct _entry entries[] = { + { ".", sizeof (".") - 1 }, + { "..", sizeof ("..") - 1 }, + { "dm-", sizeof ("dm-") - 1 }, + { "loop", sizeof ("loop") - 1 }, + { "ram", sizeof ("ram") - 1 }, + { 0, 0 }, + }; + + for (i = entries; i->name != 0; i++) { + if (strncmp (name, i->name, i->len) == 0) + return 1; + } + + return 0; +} + +static int +_probe_sys_block () +{ + DIR *blockdir; + struct dirent *dirent; + char dev_name [256]; + char *ptr; + + if (!(blockdir = opendir ("/sys/block"))) + return 0; + while ((dirent = readdir (blockdir))) { + if (_skip_entry (dirent->d_name)) + continue; + + if (strlen (dirent->d_name) > sizeof (dev_name) - 6) + continue; /* device name too long! */ + + strcpy (dev_name, "/dev/"); + strcat (dev_name, dirent->d_name); + /* in /sys/block, '/'s are replaced with '!' or '.' */ + for (ptr = dev_name; *ptr != '\0'; ptr++) { + if (*ptr == '!' || *ptr == '.') + *ptr = '/'; + } + _ped_device_probe (dev_name); + } + + closedir (blockdir); + return 1; +} + +static int +_probe_standard_devices () +{ + _ped_device_probe ("/dev/hda"); + _ped_device_probe ("/dev/hdb"); + _ped_device_probe ("/dev/hdc"); + _ped_device_probe ("/dev/hdd"); + _ped_device_probe ("/dev/hde"); + _ped_device_probe ("/dev/hdf"); + _ped_device_probe ("/dev/hdg"); + _ped_device_probe ("/dev/hdh"); + + _ped_device_probe ("/dev/sda"); + _ped_device_probe ("/dev/sdb"); + _ped_device_probe ("/dev/sdc"); + _ped_device_probe ("/dev/sdd"); + _ped_device_probe ("/dev/sde"); + _ped_device_probe ("/dev/sdf"); + + return 1; +} + +static void +linux_probe_all () +{ + /* we should probe the standard devs too, even with /proc/partitions, + * because /proc/partitions might return devfs stuff, and we might not + * have devfs available + */ + _probe_standard_devices (); + + /* /sys/block is more reliable and consistent; fall back to using + * /proc/partitions if the former is unavailable, however. + */ + if (!_probe_sys_block ()) + _probe_proc_partitions (); +} + +static char* +_device_get_part_path (PedDevice* dev, int num) +{ + int path_len = strlen (dev->path); + int result_len = path_len + 16; + char* result; + + result = (char*) ped_malloc (result_len); + if (!result) + return NULL; + + /* Check for devfs-style /disc => /partN transformation + unconditionally; the system might be using udev with devfs rules, + and if not the test is harmless. */ + if (!strcmp (dev->path + path_len - 5, "/disc")) { + /* replace /disc with /path%d */ + strcpy (result, dev->path); + snprintf (result + path_len - 5, 16, "/part%d", num); + } else if (dev->type == PED_DEVICE_DAC960 + || dev->type == PED_DEVICE_CPQARRAY + || dev->type == PED_DEVICE_ATARAID + || isdigit (dev->path[path_len - 1])) + snprintf (result, result_len, "%sp%d", dev->path, num); + else + snprintf (result, result_len, "%s%d", dev->path, num); + + return result; +} + +static char* +linux_partition_get_path (const PedPartition* part) +{ + return _device_get_part_path (part->disk->dev, part->num); +} + +static dev_t +_partition_get_part_dev (const PedPartition* part) +{ + struct stat dev_stat; + int dev_major, dev_minor; + + if (!_device_stat (part->disk->dev, &dev_stat)) + return (dev_t)0; + dev_major = major (dev_stat.st_rdev); + dev_minor = minor (dev_stat.st_rdev); + return (dev_t)makedev (dev_major, dev_minor + part->num); +} + +static int +_mount_table_search (const char* file_name, dev_t dev) +{ + struct stat part_stat; + char line[512]; + char part_name[512]; + FILE* file; + int junk; + + file = fopen (file_name, "r"); + if (!file) + return 0; + while (fgets (line, 512, file)) { + junk = sscanf (line, "%s", part_name); + if (stat (part_name, &part_stat) == 0) { + if (part_stat.st_rdev == dev) { + fclose (file); + return 1; + } + } + } + fclose (file); + return 0; +} + +static int +_partition_is_mounted_by_dev (dev_t dev) +{ + return _mount_table_search( "/proc/mounts", dev) + || _mount_table_search( "/proc/swaps", dev) + || _mount_table_search( "/etc/mtab", dev); +} + +static int +_partition_is_mounted_by_path (const char *path) +{ + struct stat part_stat; + if (stat (path, &part_stat) != 0) + return 0; + if (!S_ISBLK(part_stat.st_mode)) + return 0; + return _partition_is_mounted_by_dev (part_stat.st_rdev); +} + +static int +_partition_is_mounted (const PedPartition *part) +{ + dev_t dev; + if (!ped_partition_is_active (part)) + return 0; + dev = _partition_get_part_dev (part); + return _partition_is_mounted_by_dev (dev); +} + +static int +linux_partition_is_busy (const PedPartition* part) +{ + PedPartition* walk; + + PED_ASSERT (part != NULL, return 0); + + if (_partition_is_mounted (part)) + return 1; + if (part->type == PED_PARTITION_EXTENDED) { + for (walk = part->part_list; walk; walk = walk->next) { + if (linux_partition_is_busy (walk)) + return 1; + } + } + return 0; +} + +static int +_blkpg_part_command (PedDevice* dev, struct blkpg_partition* part, int op) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct blkpg_ioctl_arg ioctl_arg; + + ioctl_arg.op = op; + ioctl_arg.flags = 0; + ioctl_arg.datalen = sizeof (struct blkpg_partition); + ioctl_arg.data = (void*) part; + + return ioctl (arch_specific->fd, BLKPG, &ioctl_arg) == 0; +} + +static int +_blkpg_add_partition (PedDisk* disk, PedPartition* part) +{ + struct blkpg_partition linux_part; + const char* vol_name; + char* dev_name; + + PED_ASSERT(disk != NULL, return 0); + PED_ASSERT(disk->dev->sector_size % 512 == 0, return 0); + + if (ped_disk_type_check_feature (disk->type, + PED_DISK_TYPE_PARTITION_NAME)) + vol_name = ped_partition_get_name (part); + else + vol_name = NULL; + + dev_name = _device_get_part_path (disk->dev, part->num); + if (!dev_name) + return 0; + + memset (&linux_part, 0, sizeof (linux_part)); + linux_part.start = part->geom.start * disk->dev->sector_size; + linux_part.length = part->geom.length * disk->dev->sector_size; + linux_part.pno = part->num; + strncpy (linux_part.devname, dev_name, BLKPG_DEVNAMELTH); + if (vol_name) + strncpy (linux_part.volname, vol_name, BLKPG_VOLNAMELTH); + + ped_free (dev_name); + + if (!_blkpg_part_command (disk->dev, &linux_part, + BLKPG_ADD_PARTITION)) { + return ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Error informing the kernel about modifications to " + "partition %s -- %s. This means Linux won't know " + "about any changes you made to %s until you reboot " + "-- so you shouldn't mount it or use it in any way " + "before rebooting."), + linux_part.devname, + strerror (errno), + linux_part.devname) + == PED_EXCEPTION_IGNORE; + } + + return 1; +} + +static int +_blkpg_remove_partition (PedDisk* disk, int n) +{ + struct blkpg_partition linux_part; + + memset (&linux_part, 0, sizeof (linux_part)); + linux_part.pno = n; + return _blkpg_part_command (disk->dev, &linux_part, + BLKPG_DEL_PARTITION); +} + +static int +_disk_sync_part_table (PedDisk* disk) +{ + int i; + int last = PED_MAX (ped_disk_get_last_partition_num (disk), 16); + int* rets = ped_malloc(sizeof(int) * last); + int* errnums = ped_malloc(sizeof(int) * last); + int ret = 1; + + for (i = 1; i <= last; i++) { + rets[i - 1] = _blkpg_remove_partition (disk, i); + errnums[i - 1] = errno; + } + + for (i = 1; i <= last; i++) { + PedPartition* part; + + part = ped_disk_get_partition (disk, i); + if (part) { + /* extended partitions have no business in the kernel! + * blkpg doesn't like overlapping partitions. Hmmm, + * LILO isn't going to like this. + */ + if (part->type & PED_PARTITION_EXTENDED) + continue; + + /* busy... so we won't (can't!) disturb ;) Prolly + * doesn't matter anyway, because users shouldn't be + * changing mounted partitions anyway... + */ + if (!rets[i - 1] && errnums[i - 1] == EBUSY) + continue; + + /* add the (possibly modified or new) partition */ + if (!_blkpg_add_partition (disk, part)) + ret = 0; + } + } + + return ret; +} + +static int +_kernel_reread_part_table (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int retry_count = 5; + + sync(); + while (ioctl (arch_specific->fd, BLKRRPART)) { + retry_count--; + sync(); + if (!retry_count) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("The kernel was unable to re-read the partition " + "table on %s (%s). This means Linux won't know " + "anything about the modifications you made " + "until you reboot. You should reboot your computer " + "before doing anything with %s."), + dev->path, strerror (errno), dev->path); + return 0; + } + } + + return 1; +} + +static int +_have_blkpg () +{ + static int have_blkpg = -1; + int kver; + + if (have_blkpg != -1) + return have_blkpg; + + kver = _get_linux_version(); + return have_blkpg = kver >= KERNEL_VERSION (2,4,0) ? 1 : 0; +} + +static int +linux_disk_commit (PedDisk* disk) +{ + if (disk->dev->type != PED_DEVICE_FILE) { + /* The ioctl() command BLKPG_ADD_PARTITION does not notify + * the devfs system; consequently, /proc/partitions will not + * be up to date, and the proper links in /dev are not + * created. Therefore, if using DevFS, we must get the kernel + * to re-read and grok the partition table. + */ + /* Work around kernel dasd problem so we really do BLKRRPART */ + if (disk->dev->type != PED_DEVICE_DASD && + _have_blkpg () && !_have_devfs ()) { + if (_disk_sync_part_table (disk)) + return 1; + } + return _kernel_reread_part_table (disk->dev); + } + return 1; +} + +static PedDeviceArchOps linux_dev_ops = { + _new: linux_new, + destroy: linux_destroy, + is_busy: linux_is_busy, + open: linux_open, + refresh_open: linux_refresh_open, + close: linux_close, + refresh_close: linux_refresh_close, + read: linux_read, + write: linux_write, + check: linux_check, + sync: linux_sync, + sync_fast: linux_sync_fast, + probe_all: linux_probe_all +}; + +PedDiskArchOps linux_disk_ops = { + partition_get_path: linux_partition_get_path, + partition_is_busy: linux_partition_is_busy, + disk_commit: linux_disk_commit +}; + +PedArchitecture ped_linux_arch = { + dev_ops: &linux_dev_ops, + disk_ops: &linux_disk_ops +}; diff --git a/libparted/cs/constraint.c b/libparted/cs/constraint.c new file mode 100644 index 0000000..7fa7f24 --- /dev/null +++ b/libparted/cs/constraint.c @@ -0,0 +1,530 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + * \addtogroup PedConstraint + * + * \brief Constraint solver interface. + * + * Constraints are used to communicate restrictions on operations Constraints + * are restrictions on the location and alignment of the start and end of a + * partition, and the minimum and maximum size. + * + * Constraints are closed under intersection (for the proof see the source + * code). For background information see the Chinese Remainder Theorem. + * + * This interface consists of construction constraints, finding the intersection + * of constraints, and finding solutions to constraints. + * + * The constraint solver allows you to specify constraints on where a partition + * or file system (or any PedGeometry) may be placed/resized/etc. For example, + * you might want to make sure that a file system is at least 10 Gb, or that it + * starts at the beginning of new cylinder. + * + * The constraint solver in this file unifies solver in geom.c (which allows you + * to specify constraints on ranges) and natmath.c (which allows you to specify + * alignment constraints). + * + * @{ + */ + +#include <parted/parted.h> +#include <parted/debug.h> + +/** + * Initializes a pre-allocated piece of memory to contain a constraint + * with the supplied default values. + * + * \return \c 0 on failure. + */ +int +ped_constraint_init ( + PedConstraint* constraint, + const PedAlignment* start_align, + const PedAlignment* end_align, + const PedGeometry* start_range, + const PedGeometry* end_range, + PedSector min_size, + PedSector max_size) +{ + PED_ASSERT (constraint != NULL, return 0); + PED_ASSERT (start_range != NULL, return 0); + PED_ASSERT (end_range != NULL, return 0); + PED_ASSERT (min_size > 0, return 0); + PED_ASSERT (max_size > 0, return 0); + + constraint->start_align = ped_alignment_duplicate (start_align); + constraint->end_align = ped_alignment_duplicate (end_align); + constraint->start_range = ped_geometry_duplicate (start_range); + constraint->end_range = ped_geometry_duplicate (end_range); + constraint->min_size = min_size; + constraint->max_size = max_size; + + return 1; +} + +/** + * Convenience wrapper for ped_constraint_init(). + * + * Allocates a new piece of memory and initializes the constraint. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new ( + const PedAlignment* start_align, + const PedAlignment* end_align, + const PedGeometry* start_range, + const PedGeometry* end_range, + PedSector min_size, + PedSector max_size) +{ + PedConstraint* constraint; + + constraint = (PedConstraint*) ped_malloc (sizeof (PedConstraint)); + if (!constraint) + goto error; + if (!ped_constraint_init (constraint, start_align, end_align, + start_range, end_range, min_size, max_size)) + goto error_free_constraint; + return constraint; + +error_free_constraint: + ped_free (constraint); +error: + return NULL; +} + +/** + * Return a constraint that requires a region to be entirely contained inside + * \p max, and to entirely contain \p min. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_min_max ( + const PedGeometry* min, + const PedGeometry* max) +{ + PedGeometry start_range; + PedGeometry end_range; + + PED_ASSERT (min != NULL, return NULL); + PED_ASSERT (max != NULL, return NULL); + PED_ASSERT (ped_geometry_test_inside (max, min), return NULL); + + ped_geometry_init (&start_range, min->dev, max->start, + min->start - max->start + 1); + ped_geometry_init (&end_range, min->dev, min->end, + max->end - min->end + 1); + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &start_range, &end_range, + min->length, max->length); +} + +/** + * Return a constraint that requires a region to entirely contain \p min. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_min (const PedGeometry* min) +{ + PedGeometry full_dev; + + PED_ASSERT (min != NULL, return NULL); + + ped_geometry_init (&full_dev, min->dev, 0, min->dev->length); + return ped_constraint_new_from_min_max (min, &full_dev); +} + +/** + * Return a constraint that requires a region to be entirely contained inside + * \p max. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_max (const PedGeometry* max) +{ + PED_ASSERT (max != NULL, return NULL); + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + max, max, 1, max->length); +} + +/** + * Duplicate a constraint. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_duplicate (const PedConstraint* constraint) +{ + PED_ASSERT (constraint != NULL, return NULL); + + return ped_constraint_new ( + constraint->start_align, + constraint->end_align, + constraint->start_range, + constraint->end_range, + constraint->min_size, + constraint->max_size); +} + +/** + * Return a constraint that requires a region to satisfy both \p a and \p b. + * + * Moreover, any region satisfying \p a and \p b will also satisfy the returned + * constraint. + * + * \return \c NULL if no solution could be found (note that \c NULL is a valid + * PedConstraint). + */ +PedConstraint* +ped_constraint_intersect (const PedConstraint* a, const PedConstraint* b) +{ + PedAlignment* start_align; + PedAlignment* end_align; + PedGeometry* start_range; + PedGeometry* end_range; + PedSector min_size; + PedSector max_size; + PedConstraint* constraint; + + if (!a || !b) + return NULL; + + start_align = ped_alignment_intersect (a->start_align, b->start_align); + if (!start_align) + goto empty; + end_align = ped_alignment_intersect (a->end_align, b->end_align); + if (!end_align) + goto empty_destroy_start_align; + start_range = ped_geometry_intersect (a->start_range, b->start_range); + if (!start_range) + goto empty_destroy_end_align; + end_range = ped_geometry_intersect (a->end_range, b->end_range); + if (!end_range) + goto empty_destroy_start_range; + min_size = PED_MAX (a->min_size, b->min_size); + max_size = PED_MIN (a->max_size, b->max_size); + + constraint = ped_constraint_new ( + start_align, end_align, start_range, end_range, + min_size, max_size); + if (!constraint) + goto empty_destroy_end_range; + + ped_alignment_destroy (start_align); + ped_alignment_destroy (end_align); + ped_geometry_destroy (start_range); + ped_geometry_destroy (end_range); + return constraint; + +empty_destroy_end_range: + ped_geometry_destroy (end_range); +empty_destroy_start_range: + ped_geometry_destroy (start_range); +empty_destroy_end_align: + ped_alignment_destroy (end_align); +empty_destroy_start_align: + ped_alignment_destroy (start_align); +empty: + return NULL; +} + +/** + * Release the memory allocated for a PedConstraint constructed with + * ped_constraint_init(). + */ +void +ped_constraint_done (PedConstraint* constraint) +{ + PED_ASSERT (constraint != NULL, return); + + ped_alignment_destroy (constraint->start_align); + ped_alignment_destroy (constraint->end_align); + ped_geometry_destroy (constraint->start_range); + ped_geometry_destroy (constraint->end_range); +} + +/** + * Release the memory allocated for a PedConstraint constructed with + * ped_constraint_new(). + */ +void +ped_constraint_destroy (PedConstraint* constraint) +{ + if (constraint) { + ped_constraint_done (constraint); + ped_free (constraint); + } +} + +/* + * Return the region within which the start must lie + * in order to satisfy a constriant. It takes into account + * constraint->start_range, constraint->min_size and constraint->max_size. + * All sectors in this range that also satisfy alignment requirements have + * an end, such that the (start, end) satisfy the constraint. + */ +static PedGeometry* +_constraint_get_canonical_start_range (const PedConstraint* constraint) +{ + PedSector first_end_soln; + PedSector last_end_soln; + PedSector min_start; + PedSector max_start; + PedGeometry start_min_max_range; + PedGeometry* result; + + if (constraint->min_size > constraint->max_size) + return NULL; + + first_end_soln = ped_alignment_align_down ( + constraint->end_align, constraint->end_range, + constraint->end_range->start); + last_end_soln = ped_alignment_align_up ( + constraint->end_align, constraint->end_range, + constraint->end_range->end); + if (first_end_soln == -1 || last_end_soln == -1 + || first_end_soln > last_end_soln + || last_end_soln < constraint->min_size) + return NULL; + + min_start = first_end_soln - constraint->max_size + 1; + if (min_start < 0) + min_start = 0; + max_start = last_end_soln - constraint->min_size + 1; + if (max_start < 0) + return NULL; + + ped_geometry_init ( + &start_min_max_range, constraint->start_range->dev, + min_start, max_start - min_start + 1); + + return ped_geometry_intersect (&start_min_max_range, + constraint->start_range); +} + +/* + * Return the nearest start that will have at least one other end that + * together satisfy the constraint. + */ +static PedSector +_constraint_get_nearest_start_soln (const PedConstraint* constraint, + PedSector start) +{ + PedGeometry* start_range; + PedSector result; + + start_range = _constraint_get_canonical_start_range (constraint); + if (!start_range) + return -1; + result = ped_alignment_align_nearest ( + constraint->start_align, start_range, start); + ped_geometry_destroy (start_range); + return result; +} + +/* + * Given a constraint and a start ("half of the solution"), find the + * range of all possible ends, such that all (start, end) are solutions + * to constraint (subject to additional alignment requirements). + */ +static PedGeometry* +_constraint_get_end_range (const PedConstraint* constraint, PedSector start) +{ + PedDevice* dev = constraint->end_range->dev; + PedSector first_min_max_end; + PedSector last_min_max_end; + PedGeometry end_min_max_range; + + if (start + constraint->min_size - 1 > dev->length - 1) + return NULL; + + first_min_max_end = start + constraint->min_size - 1; + last_min_max_end = start + constraint->max_size - 1; + if (last_min_max_end > dev->length - 1) + last_min_max_end = dev->length - 1; + + ped_geometry_init (&end_min_max_range, dev, + first_min_max_end, + last_min_max_end - first_min_max_end + 1); + + return ped_geometry_intersect (&end_min_max_range, + constraint->end_range); +} + +/* + * Given "constraint" and "start", find the end that is nearest to + * "end", such that ("start", the end) together form a solution to + * "constraint". + */ +static PedSector +_constraint_get_nearest_end_soln (const PedConstraint* constraint, + PedSector start, PedSector end) +{ + PedGeometry* end_range; + PedSector result; + + end_range = _constraint_get_end_range (constraint, start); + if (!end_range) + return -1; + + result = ped_alignment_align_nearest (constraint->end_align, end_range, + end); + ped_geometry_destroy (end_range); + return result; +} + +/** + * Return the nearest region to \p geom that satisfy a \p constraint. + * + * Note that "nearest" is somewhat ambiguous. This function makes + * no guarantees about how this ambiguity is resovled. + * + * \return PedGeometry, or NULL when a \p constrain cannot be satisfied + */ +PedGeometry* +ped_constraint_solve_nearest ( + const PedConstraint* constraint, const PedGeometry* geom) +{ + PedSector start; + PedSector end; + PedGeometry* result; + + if (constraint == NULL) + return NULL; + + PED_ASSERT (geom != NULL, return NULL); + PED_ASSERT (constraint->start_range->dev == geom->dev, return NULL); + + start = _constraint_get_nearest_start_soln (constraint, geom->start); + if (start == -1) + return NULL; + end = _constraint_get_nearest_end_soln (constraint, start, geom->end); + if (end == -1) + return NULL; + + result = ped_geometry_new (geom->dev, start, end - start + 1); + if (!result) + return NULL; + PED_ASSERT (ped_constraint_is_solution (constraint, result), + return NULL); + return result; +} + +/** + * Find the largest region that satisfies a constraint. + * + * There might be more than one solution. This function makes no + * guarantees about which solution it will choose in this case. + */ +PedGeometry* +ped_constraint_solve_max (const PedConstraint* constraint) +{ + PedDevice* dev; + PedGeometry full_dev; + + if (!constraint) + return NULL; + dev = constraint->start_range->dev; + ped_geometry_init (&full_dev, dev, 0, dev->length - 1); + return ped_constraint_solve_nearest (constraint, &full_dev); +} + +/** + * Check whether \p geom satisfies the given constraint. + * + * \return \c 1 if it does. + **/ +int +ped_constraint_is_solution (const PedConstraint* constraint, + const PedGeometry* geom) +{ + PED_ASSERT (constraint != NULL, return 0); + PED_ASSERT (geom != NULL, return 0); + + if (!ped_alignment_is_aligned (constraint->start_align, NULL, + geom->start)) + return 0; + if (!ped_alignment_is_aligned (constraint->end_align, NULL, geom->end)) + return 0; + if (!ped_geometry_test_sector_inside (constraint->start_range, + geom->start)) + return 0; + if (!ped_geometry_test_sector_inside (constraint->end_range, geom->end)) + return 0; + if (geom->length < constraint->min_size) + return 0; + if (geom->length > constraint->max_size) + return 0; + return 1; +} + +/** + * Return a constraint that any region on the given device will satisfy. + */ +PedConstraint* +ped_constraint_any (const PedDevice* dev) +{ + PedGeometry full_dev; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length)) + return NULL; + + return ped_constraint_new ( + ped_alignment_any, + ped_alignment_any, + &full_dev, + &full_dev, + 1, + dev->length); +} + +/** + * Return a constraint that only the given region will satisfy. + */ +PedConstraint* +ped_constraint_exact (const PedGeometry* geom) +{ + PedAlignment start_align; + PedAlignment end_align; + PedGeometry start_sector; + PedGeometry end_sector; + + ped_alignment_init (&start_align, geom->start, 0); + ped_alignment_init (&end_align, geom->end, 0); + ped_geometry_init (&start_sector, geom->dev, geom->start, 1); + ped_geometry_init (&end_sector, geom->dev, geom->end, 1); + + return ped_constraint_new (&start_align, &end_align, + &start_sector, &end_sector, 1, + geom->dev->length); +} + +/** + * @} + */ + diff --git a/libparted/cs/geom.c b/libparted/cs/geom.c new file mode 100644 index 0000000..29c89e7 --- /dev/null +++ b/libparted/cs/geom.c @@ -0,0 +1,483 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** \file geom.c */ + + +/** + * \addtogroup PedGeometry + * + * \brief PedGeometry represents a continuous region on a device. All addressing + * through a PedGeometry object is in terms of the start of the continuous + * region. + * + * The following conditions are always true on a PedGeometry object manipulated + * with the GNU Parted API: + * + * - <tt>start + length - 1 == end</tt> + * - <tt>length > 0</tt> + * - <tt>start >= 0</tt> + * - <tt>end < dev->length</tt> + * + * @{ + */ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/** + * Initialize the previously allocated PedGeometry \p geom. + */ +int +ped_geometry_init (PedGeometry* geom, const PedDevice* dev, + PedSector start, PedSector length) +{ + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (dev != NULL, return 0); + + geom->dev = (PedDevice*) dev; + return ped_geometry_set (geom, start, length); +} + +/** + * Create a new PedGeometry object on \p disk, starting at \p start with a + * size of \p length sectors. + * + * \return NULL on failure. + */ +PedGeometry* +ped_geometry_new (const PedDevice* dev, PedSector start, PedSector length) +{ + PedGeometry* geom; + + PED_ASSERT (dev != NULL, return NULL); + + geom = (PedGeometry*) ped_malloc (sizeof (PedGeometry)); + if (!geom) + goto error; + if (!ped_geometry_init (geom, dev, start, length)) + goto error_free_geom; + return geom; + +error_free_geom: + ped_free (geom); +error: + return NULL; +} + +/** + * Duplicate a PedGeometry object. + * + * This function constructs a PedGeometry object that is an identical but + * independent copy of \p geom. Both the input, \p geom, and the output + * should be destroyed with ped_geometry_destroy() when they are no + * longer needed. + * + * \return NULL on failure. + */ +PedGeometry* +ped_geometry_duplicate (const PedGeometry* geom) +{ + PED_ASSERT (geom != NULL, return NULL); + return ped_geometry_new (geom->dev, geom->start, geom->length); +} + +/** + * Return a PedGeometry object that refers to the intersection of + * \p a and \p b. + * + * This function constructs a PedGeometry object that describes the + * region that is common to both a and b. If there is no such common + * region, it returns NULL. (This situation is not treated as an + * error by much of GNU Parted.) + */ +PedGeometry* +ped_geometry_intersect (const PedGeometry* a, const PedGeometry* b) +{ + PedSector start; + PedSector end; + + if (!a || !b || a->dev != b->dev) + return NULL; + + start = PED_MAX (a->start, b->start); + end = PED_MIN (a->end, b->end); + if (start > end) + return NULL; + + return ped_geometry_new (a->dev, start, end - start + 1); +} + +/** + * Destroy a PedGeometry object. + */ +void +ped_geometry_destroy (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL, return); + + ped_free (geom); +} + +/** + * Assign a new \p start, \p end (implicitly) and \p length to \p geom. + * + * \p geom->end is calculated from \p start and \p length. + */ +int +ped_geometry_set (PedGeometry* geom, PedSector start, PedSector length) +{ + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (geom->dev != NULL, return 0); + + if (length < 1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have the end before the start!")); + return 0; + } + if (start < 0 || start + length - 1 >= geom->dev->length) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have a partition outside the disk!")); + return 0; + } + + geom->start = start; + geom->length = length; + geom->end = start + length - 1; + + return 1; +} + +/** + * Assign a new start to \p geom without changing \p geom->end. + * + * \p geom->length is updated accordingly. + */ +int +ped_geometry_set_start (PedGeometry* geom, PedSector start) +{ + return ped_geometry_set (geom, start, geom->end - start + 1); +} + +/** + * Assign a new end to \p geom without changing \p geom->start. + * + * \p geom->length is updated accordingly. + */ +int +ped_geometry_set_end (PedGeometry* geom, PedSector end) +{ + return ped_geometry_set (geom, geom->start, end - geom->start + 1); +} +/** + * Test if \p a overlaps with \p b. + * + * That is, they lie on the same physical device, and they share + * the same physical region at least partially. + * + * \return 1 if \p a and \p b overlap. + */ +int +ped_geometry_test_overlap (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL, return 0); + PED_ASSERT (b != NULL, return 0); + + if (a->dev != b->dev) + return 0; + + if (a->start < b->start) + return a->end >= b->start; + else + return b->end >= a->start; +} + +/** + * Tests if \p b lies completely within \p a. That is, they lie on the same + * physical device, and all of the \p b's region is contained inside + * \p a's. + * + * \return 1 if the region \p b describes is contained entirely inside \p a +*/ +int +ped_geometry_test_inside (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL, return 0); + PED_ASSERT (b != NULL, return 0); + + if (a->dev != b->dev) + return 0; + + return b->start >= a->start && b->end <= a->end; +} + +/** + * Tests if \a a and \p b refer to the same physical region. + * + * \return 1 if \p a and \p b describe the same regions + * + */ +int +ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL, return 0); + PED_ASSERT (b != NULL, return 0); + + return a->dev == b->dev + && a->start == b->start + && a->end == b->end; +} + +/** + * Tests if \p sector is inside \p geom. + * + * \return 1 if sector lies within the \p region that \p geom describes + */ +int +ped_geometry_test_sector_inside (const PedGeometry* geom, PedSector sector) +{ + PED_ASSERT (geom != NULL, return 0); + + return sector >= geom->start && sector <= geom->end; +} + +/** + * Reads data from the region represented by \p geom. \p offset is the + * location from within the region, not from the start of the disk. + * \p count sectors are read into \p buffer. + * This is essentially equivalent to: + * \code + * ped_device_read (geom->disk->dev, buffer, geom->start + offset, count) + * \endcode + * + * \throws PED_EXCEPTION_ERROR when attempting to read sectors outside of + * partition + * + * \return 0 on failure + */ +int +ped_geometry_read (const PedGeometry* geom, void* buffer, PedSector start, + PedSector count) +{ + int exception_status; + PedSector real_start; + + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (buffer != NULL, return 0); + PED_ASSERT (start >= 0, return 0); + PED_ASSERT (count >= 0, return 0); + + real_start = geom->start + start; + + if (real_start + count - 1 > geom->end) { + exception_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Attempt to read sectors %ld-%ld outside of " + "partition on %s."), + (long) start, (long) (start + count - 1), + geom->dev->path); + return exception_status == PED_EXCEPTION_IGNORE; + } + + if (!ped_device_read (geom->dev, buffer, real_start, count)) + return 0; + return 1; +} + +/** + * Flushes the cache on \p geom. + * + * This function flushes all write-behind caches that might be holding + * writes made by ped_geometry_write() to \p geom. It is slow, because + * it guarantees cache coherency among all relevant caches. + * + * \return 0 on failure + */ +int +ped_geometry_sync (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL, return 0); + return ped_device_sync (geom->dev); +} + +/** + * Flushes the cache on \p geom. + * + * This function flushes all write-behind caches that might be holding writes + * made by ped_geometry_write() to \p geom. It does NOT ensure cache coherency + * with other caches that cache data in the region described by \p geom. + * If you need cache coherency, use ped_geometry_sync() instead. + * + * \return 0 on failure + */ +int +ped_geometry_sync_fast (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL, return 0); + return ped_device_sync_fast (geom->dev); +} + +/** + * Writes data into the region represented by \p geom. \p offset is the + * location from within the region, not from the start of the disk. + * \p count sectors are written. + * + * \return 0 on failure + */ +int +ped_geometry_write (PedGeometry* geom, const void* buffer, PedSector start, + PedSector count) +{ + int exception_status; + PedSector real_start; + + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (buffer != NULL, return 0); + PED_ASSERT (start >= 0, return 0); + PED_ASSERT (count >= 0, return 0); + + real_start = geom->start + start; + + if (real_start + count - 1 > geom->end) { + exception_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Attempt to write sectors %ld-%ld outside of " + "partition on %s."), + (long) start, (long) (start + count - 1), + geom->dev->path); + return exception_status == PED_EXCEPTION_IGNORE; + } + + if (!ped_device_write (geom->dev, buffer, real_start, count)) + return 0; + return 1; +} + +/** + * Checks for physical disk errors. \todo use ped_device_check() + * + * Checks a region for physical defects on \p geom. \p buffer is used + * for temporary storage for ped_geometry_check(), and has an undefined + * value. \p buffer is \p buffer_size sectors long. + * The region checked starts at \p offset sectors inside the + * region represented by \p geom, and is \p count sectors long. + * \p granularity specificies how sectors should be grouped + * together. The first bad sector to be returned will always be in + * the form: + * <tt>offset + n * granularity</tt> + * + * \return the first bad sector, or 0 if there were no physical errors + */ +PedSector +ped_geometry_check (PedGeometry* geom, void* buffer, PedSector buffer_size, + PedSector offset, PedSector granularity, PedSector count, + PedTimer* timer) +{ + PedSector group; + PedSector i; + PedSector read_len; + + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (buffer != NULL, return 0); + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("checking for bad blocks")); + +retry: + ped_exception_fetch_all(); + for (group = offset; group < offset + count; group += buffer_size) { + ped_timer_update (timer, 1.0 * (group - offset) / count); + read_len = PED_MIN (buffer_size, offset + count - group); + if (!ped_geometry_read (geom, buffer, group, read_len)) + goto found_error; + } + ped_exception_leave_all(); + ped_timer_update (timer, 1.0); + return 0; + +found_error: + ped_exception_catch(); + for (i = group; i + granularity < group + count; i += granularity) { + if (!ped_geometry_read (geom, buffer, i, granularity)) { + ped_exception_catch(); + ped_exception_leave_all(); + return i; + } + } + ped_exception_leave_all(); + goto retry; /* weird: failure on group read, but not individually */ +} + +/** + * This function takes a \p sector inside the region described by src, and + * returns that sector's address inside dst. This means that + * + * \code + * ped_geometry_read (dst, buf, ped_geometry_map(dst, src, sector), 1) + * \endcode + * + * does the same thing as + * + * \code + * ped_geometry_read (src, buf, sector, 1) + * \endcode + * + * Clearly, this will only work if \p src and \p dst overlap. + * + * \return -1 if \p sector is not within \p dst's space, + * or \p sector's address inside \p dst + * + */ +PedSector +ped_geometry_map (const PedGeometry* dst, const PedGeometry* src, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (dst != NULL, return 0); + PED_ASSERT (src != NULL, return 0); + + if (!ped_geometry_test_sector_inside (src, sector)) + return -1; + if (dst->dev != src->dev) + return -1; + + result = src->start + sector - dst->start; + if (result < 0 || result > dst->length) + return -1; + + return result; +} + +/** @} */ + diff --git a/libparted/cs/natmath.c b/libparted/cs/natmath.c new file mode 100644 index 0000000..a774d8f --- /dev/null +++ b/libparted/cs/natmath.c @@ -0,0 +1,495 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + * \file natmath.c + */ + +/** + * \addtogroup PedAlignment + * + * \brief Alignment constraint model. + * + * This part of libparted models alignment constraints. + * + * @{ + */ + +#include <stdlib.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/natmath.h> + +/* Arrrghhh! Why doesn't C have tuples? */ +typedef struct { + PedSector gcd; /* "converges" to the gcd */ + PedSector x; + PedSector y; +} EuclidTriple; + +static const PedAlignment _any = { + offset: 0, + grain_size: 1 +}; + +const PedAlignment* ped_alignment_any = &_any; +const PedAlignment* ped_alignment_none = NULL; + +/* This function returns "a mod b", the way C should have done it! + * Mathematicians prefer -3 mod 4 to be 3. Reason: division by N + * is all about adding or subtracting N, and we like our remainders + * to be between 0 and N - 1. + */ +PedSector +abs_mod (PedSector a, PedSector b) +{ + if (a < 0) + return a % b + b; + else + return a % b; +} + +/* Rounds a number down to the closest number that is a multiple of + * grain_size. + */ +PedSector +ped_round_down_to (PedSector sector, PedSector grain_size) +{ + return sector - abs_mod (sector, grain_size); +} + +inline PedSector +ped_div_round_up (PedSector numerator, PedSector divisor) +{ + return (numerator + divisor - 1) / divisor; +} + +inline PedSector +ped_div_round_to_nearest (PedSector numerator, PedSector divisor) +{ + return (numerator + divisor/2) / divisor; +} + +/* Rounds a number up to the closest number that is a multiple of + * grain_size. + */ +PedSector +ped_round_up_to (PedSector sector, PedSector grain_size) +{ + if (sector % grain_size) + return ped_round_down_to (sector, grain_size) + grain_size; + else + return sector; +} + +/* Rounds a number to the closest number that is a multiple of grain_size. */ +PedSector +ped_round_to_nearest (PedSector sector, PedSector grain_size) +{ + if (sector % grain_size > grain_size/2) + return ped_round_up_to (sector, grain_size); + else + return ped_round_down_to (sector, grain_size); +} + +/* This function returns the largest number that divides both a and b. + * It uses the ancient Euclidean algorithm. + */ +PedSector +ped_greatest_common_divisor (PedSector a, PedSector b) +{ + PED_ASSERT (a >= 0, return 0); + PED_ASSERT (b >= 0, return 0); + + /* Put the arguments in the "right" format. (Recursive calls made by + * this function are always in the right format.) + */ + if (b > a) + return ped_greatest_common_divisor (b, a); + + if (b) + return ped_greatest_common_divisor (b, a % b); + else + return a; +} + +/** + * Initialize a preallocated piece of memory for an alignment object + * (used by PedConstraint). + * + * The object will represent all sectors \e s for which the equation + * <tt>s = offset + X * grain_size</tt> holds. + */ +int +ped_alignment_init (PedAlignment* align, PedSector offset, PedSector grain_size) +{ + PED_ASSERT (align != NULL, return 0); + + if (grain_size < 0) + return 0; + + if (grain_size) + align->offset = abs_mod (offset, grain_size); + else + align->offset = offset; + align->grain_size = grain_size; + + return 1; +} + +/** + * Return an alignment object (used by PedConstraint), representing all + * PedSector's that are of the form <tt>offset + X * grain_size</tt>. + */ +PedAlignment* +ped_alignment_new (PedSector offset, PedSector grain_size) +{ + PedAlignment* align; + + align = (PedAlignment*) ped_malloc (sizeof (PedAlignment)); + if (!align) + goto error; + + if (!ped_alignment_init (align, offset, grain_size)) + goto error_free_align; + + return align; + +error_free_align: + ped_free (align); +error: + return NULL; +} + +/** + * Free up memory associated with \p align. + */ +void +ped_alignment_destroy (PedAlignment* align) +{ + if (align) + ped_free (align); +} + +/** + * Return a duplicate of \p align. + */ +PedAlignment* +ped_alignment_duplicate (const PedAlignment* align) +{ + if (!align) + return NULL; + return ped_alignment_new (align->offset, align->grain_size); +} + +/* the extended Euclid algorithm. + * + * input: + * a and b, a > b + * + * output: + * gcd, x and y, such that: + * + * gcd = greatest common divisor of a and b + * gcd = x*a + y*b + */ +EuclidTriple +extended_euclid (int a, int b) +{ + EuclidTriple result; + EuclidTriple tmp; + + if (b == 0) { + result.gcd = a; + result.x = 1; + result.y = 0; + return result; + } + + tmp = extended_euclid (b, a % b); + result.gcd = tmp.gcd; + result.x = tmp.y; + result.y = tmp.x - (a/b) * tmp.y; + return result; +} + +/** + * This function computes a PedAlignment object that describes the + * intersection of two alignments. That is, a sector satisfies the + * new alignment object if and only if it satisfies both of the original + * ones. (See ped_alignment_is_aligned() for the meaning of "satisfies") + * + * Apart from the trivial cases (where one or both of the alignment objects + * constraints have no sectors that satisfy them), this is what we're trying to + * do: + * - two input constraints: \p a and \p b. + * - the new grain_size is going to be the lowest common multiple of + * \p a->grain_size and \p b->grain_size + * - hard part - solve the simultaneous equations, for offset, where offset, + * X and Y are variables. (Note: offset can be obtained from either X or Y, + * by substituing into either equation) + * + * \code + * offset = \p a->offset + X * \p a->grain_size (1) + * offset = \p b->offset + Y * \p b->grain_size (2) + * \endcode + * + * or, abbreviated: + * + * \code + * o = Ao + X*Ag (1) + * o = Bo + Y*Bg (2) + * + * => Ao + X*Ag = Bo + Y*Bg (1) = (2) + * X*Ag - Y*Bg = Bo - Ao (3) + * \endcode + * + * As it turns out, there only exists a solution if (Bo - Ao) is a multiple + * of the GCD of Ag and Bg. Reason: all linear combinations of Ag and Bg are + * multiples of the GCD. + * + * Proof: + * + * \code + * A * Ag + B * Bg + * = A * (\p a * gcd) + B * (\p b * gcd) + * = gcd * (A * \p a + B * \p b) + * \endcode + * + * gcd is a factor of the linear combination. QED + * + * Anyway, \p a * Ag + \p b * Bg = gcd can be solved (for \p a, \p b and gcd) + * with Euclid's extended algorithm. Then, we just multiply through by + * (Bo - Ao) / gcd to get (3). + * + * i.e. + * \code + * A * Ag + B * Bg = gcd + * A*(Bo-Ao)/gcd * Ag + B(Bo-Ao)/gcd * Bg = gcd * (Bo-Ao)/gcd + * X*Ag - Y*Bg = Bo - Ao (3) + * + * X = A*(Bo-Ao)/gcd + * Y = - B*(Bo-Ao)/gcd + * \endcode + * + * then: + * \code + * o = Ao + X*Ag (1) + * = Ao + A*(Bo-Ao)/gcd*Ag + * o = Bo + Y*Bg (2) + * = Bo - B*(Bo-Ao)/gcd*Ag + * \endcode + * + * Thanks go to Nathan Hurst (njh@hawthorn.csse.monash.edu.au) for figuring + * this algorithm out :-) + * + * \note Returned \c NULL is a valid PedAlignment object, and can be used + for ped_alignment_*() function. + * + * \return a PedAlignment on success, \c NULL on failure + */ +PedAlignment* +ped_alignment_intersect (const PedAlignment* a, const PedAlignment* b) +{ + PedSector new_grain_size; + PedSector new_offset; + PedSector delta_on_gcd; + EuclidTriple gcd_factors; + + + if (!a || !b) + return NULL; + + /*PED_DEBUG (0x10, "intersecting alignments (%d,%d) and (%d,%d)", + a->offset, a->grain_size, b->offset, b->grain_size); + */ + + if (a->grain_size < b->grain_size) { + const PedAlignment* tmp; + tmp = a; a = b; b = tmp; + } + + /* weird/trivial case: where the solution space for "a" or "b" is + * either empty or contains exactly one solution + */ + if (a->grain_size == 0 && b->grain_size == 0) { + if (a->offset == b->offset) + return ped_alignment_duplicate (a); + else + return NULL; + } + + /* general case */ + gcd_factors = extended_euclid (a->grain_size, b->grain_size); + + delta_on_gcd = (b->offset - a->offset) / gcd_factors.gcd; + new_offset = a->offset + gcd_factors.x * delta_on_gcd * a->grain_size; + new_grain_size = a->grain_size * b->grain_size / gcd_factors.gcd; + + /* inconsistency => no solution */ + if (new_offset + != b->offset - gcd_factors.y * delta_on_gcd * b->grain_size) + return NULL; + + return ped_alignment_new (new_offset, new_grain_size); +} + +/* This function returns the sector closest to "sector" that lies inside + * geom and satisfies the alignment constraint. + */ +static PedSector +_closest_inside_geometry (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PED_ASSERT (align != NULL, return -1); + + if (!align->grain_size) { + if (ped_alignment_is_aligned (align, geom, sector) + && (!geom || ped_geometry_test_sector_inside (geom, + sector))) + return sector; + else + return -1; + } + + if (sector < geom->start) + sector += ped_round_up_to (geom->start - sector, + align->grain_size); + if (sector > geom->end) + sector -= ped_round_up_to (sector - geom->end, + align->grain_size); + + if (!ped_geometry_test_sector_inside (geom, sector)) + return -1; + return sector; +} + +/** + * This function returns the closest sector to \p sector that lies inside + * \p geom that satisfies the given alignment constraint \p align. It prefers + * sectors that are beyond \p sector (are not smaller than \p sector), + * but does not guarantee that this. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (align != NULL, return -1); + + if (!align->grain_size) + result = align->offset; + else + result = ped_round_up_to (sector - align->offset, + align->grain_size) + + align->offset; + + if (geom) + result = _closest_inside_geometry (align, geom, result); + return result; +} + +/** + * This function returns the closest sector to \p sector that lies inside + * \p geom that satisfies the given alignment constraint \p align. It prefers + * sectors that are before \p sector (are not larger than \p sector), + * but does not guarantee that this. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (align != NULL, return -1); + + if (!align->grain_size) + result = align->offset; + else + result = ped_round_down_to (sector - align->offset, + align->grain_size) + + align->offset; + + if (geom) + result = _closest_inside_geometry (align, geom, result); + return result; +} + +/* Returns either a or b, depending on which is closest to "sector". */ +static PedSector +closest (PedSector sector, PedSector a, PedSector b) +{ + if (a == -1) + return b; + if (b == -1) + return a; + + if (abs (sector - a) < abs (sector - b)) + return a; + else + return b; +} + +/** + * This function returns the sector that is closest to \p sector, + * satisfies the \p align constraint and lies inside \p geom. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PED_ASSERT (align != NULL, return -1); + + return closest (sector, ped_alignment_align_up (align, geom, sector), + ped_alignment_align_down (align, geom, sector)); +} + +/** + * This function returns 1 if \p sector satisfies the alignment + * constraint \p align and lies inside \p geom. + * + * \return \c 1 on success, \c 0 on failure + */ +int +ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + if (!align) + return 0; + + if (geom && !ped_geometry_test_sector_inside (geom, sector)) + return 0; + + if (align->grain_size) + return (sector - align->offset) % align->grain_size == 0; + else + return sector == align->offset; +} + +/** + * @} + */ + diff --git a/libparted/debug.c b/libparted/debug.c new file mode 100644 index 0000000..9bf86a7 --- /dev/null +++ b/libparted/debug.c @@ -0,0 +1,100 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#ifdef DEBUG + +static void default_handler ( const int level, const char* file, int line, + const char* function, const char* msg ); +static PedDebugHandler* debug_handler = &default_handler; + + +/** + * Default debug handler. + * Will print all information to stderr. + */ +static void default_handler ( const int level, const char* file, int line, + const char* function, const char* msg ) +{ + fprintf ( stderr, "[%d] %s:%d (%s): %s\n", + level, file, line, function, msg ); +} + +/** + * Send a debug message. + * Do not call this directly -- use PED_DEBUG() instead. + * + * level log level, 0 ~= "print definitely" + */ +void ped_debug ( const int level, const char* file, int line, + const char* function, const char* msg, ... ) +{ + va_list arg_list; + char* msg_concat = ped_malloc(8192); + + va_start ( arg_list, msg ); + vsnprintf ( msg_concat, 8192, msg, arg_list ); + va_end ( arg_list ); + + debug_handler ( level, file, line, function, msg_concat ); + + ped_free ( msg_concat ); +} + +/* + * handler debug handler; NULL for default handler + */ +void ped_debug_set_handler ( PedDebugHandler* handler ) +{ + debug_handler = ( handler ? handler : default_handler ); +} + +/* + * Check an assertion. + * Do not call this directly -- use PED_ASSERT() instead. + */ +int ped_assert ( int cond, const char* cond_text, + const char* file, int line, const char* function ) +{ + PedExceptionOption opt; + + if ( cond ) + return 1; + + opt = ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_IGNORE_CANCEL, + _("Assertion (%s) at %s:%d in function %s() failed."), + cond_text, file, line, function ); + + return ( opt == PED_EXCEPTION_IGNORE ); +} + +#endif /* DEBUG */ + diff --git a/libparted/device.c b/libparted/device.c new file mode 100644 index 0000000..36729e0 --- /dev/null +++ b/libparted/device.c @@ -0,0 +1,441 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999 - 2001, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** \file device.c */ + +/** + * \addtogroup PedDevice + * + * \brief Device access. + * + * When ped_device_probe_all() is called, libparted attempts to detect all + * devices. It constructs a list which can be accessed with + * ped_device_get_next(). + * + * If you want to use a device that isn't on the list, use + * ped_device_get(). Also, there may be OS-specific constructors, for creating + * devices from file descriptors, stores, etc. For example, + * ped_device_new_from_store(). + * + * @{ + */ + +#define _GNU_SOURCE 1 + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +static PedDevice* devices; /* legal advice says: initialized to NULL, + under section 6.7.8 part 10 + of ISO/EIC 9899:1999 */ + +#ifndef HAVE_CANONICALIZE_FILE_NAME +char * +canonicalize_file_name (const char *name) +{ + char * buf; + int size; + char * result; + +#ifdef PATH_MAX + size = PATH_MAX; +#else + /* Bigger is better; realpath has no way todo bounds checking. */ + size = 4096; +#endif + + /* Just in case realpath does not NULL terminate the string + * or it just fits in SIZE without a NULL terminator. */ + buf = calloc (size + 1, 1); + if (! buf) { + errno = ENOMEM; + return NULL; + } + + result = realpath (name, buf); + if (! result) + free (buf); + + return result; +} +#endif /* !HAVE_CANONICALIZE_FILE_NAME */ + +static void +_device_register (PedDevice* dev) +{ + PedDevice* walk; + for (walk = devices; walk && walk->next; walk = walk->next); + if (walk) + walk->next = dev; + else + devices = dev; + dev->next = NULL; +} + +static void +_device_unregister (PedDevice* dev) +{ + PedDevice* walk; + PedDevice* last = NULL; + + for (walk = devices; walk != NULL; last = walk, walk = walk->next) { + if (walk == dev) break; + } + + if (last) + last->next = dev->next; + else + devices = dev->next; +} + +/** + * Returns the next device that was detected by ped_device_probe_all(), or + * calls to ped_device_get_next(). + * If dev is NULL, returns the first device. + * + * \return NULL if dev is the last device. + */ +PedDevice* +ped_device_get_next (const PedDevice* dev) +{ + if (dev) + return dev->next; + else + return devices; +} + +void +_ped_device_probe (const char* path) +{ + PedDevice* dev; + + PED_ASSERT (path != NULL, return); + + ped_exception_fetch_all (); + dev = ped_device_get (path); + if (!dev) + ped_exception_catch (); + ped_exception_leave_all (); +} + +/** + * Attempts to detect all devices. + */ +void +ped_device_probe_all () +{ + ped_architecture->dev_ops->probe_all (); +} + +/** + * Close/free all devices. + * Called by ped_done(), so you do not need to worry about it. + */ +void +ped_device_free_all () +{ + while (devices) + ped_device_destroy (devices); +} + +/** + * Gets the device "name", where name is usually the block device, e.g. + * /dev/sdb. If the device wasn't detected with ped_device_probe_all(), + * an attempt will be made to detect it again. If it is found, it will + * be added to the list. + */ +PedDevice* +ped_device_get (const char* path) +{ + PedDevice* walk; + char* normal_path; + + PED_ASSERT (path != NULL, return NULL); + normal_path = canonicalize_file_name (path); + if (!normal_path) + /* Well, maybe it is just that the file does not exist. + * Try it anyway. */ + normal_path = strdup (path); + if (!normal_path) + return NULL; + + for (walk = devices; walk != NULL; walk = walk->next) { + if (!strcmp (walk->path, normal_path)) { + ped_free (normal_path); + return walk; + } + } + + walk = ped_architecture->dev_ops->_new (normal_path); + ped_free (normal_path); + if (!walk) + return NULL; + _device_register (walk); + return walk; +} + +/** + * Destroys a device and removes it from the device list, and frees + * all resources associated with the device (all resources allocated + * when the device was created). + */ +void +ped_device_destroy (PedDevice* dev) +{ + _device_unregister (dev); + + while (dev->open_count) { + if (!ped_device_close (dev)) + break; + } + + ped_architecture->dev_ops->destroy (dev); +} + +int +ped_device_is_busy (PedDevice* dev) +{ + return ped_architecture->dev_ops->is_busy (dev); +} + +/** + * Attempt to open a device to allow use of read, write and sync functions. + * + * The meaning of "open" is architecture-dependent. Apart from requesting + * access to the device from the operating system, it does things like flushing + * caches. + * \note May allocate resources. Any resources allocated here will + * be freed by a final ped_device_close(). (ped_device_open() may be + * called multiple times -- it's a ref-count-like mechanism) + * + * \return zero on failure + */ +int +ped_device_open (PedDevice* dev) +{ + int status; + + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + + if (dev->open_count) + status = ped_architecture->dev_ops->refresh_open (dev); + else + status = ped_architecture->dev_ops->open (dev); + if (status) + dev->open_count++; + return status; +} + +/** + * Close dev. + * If this is the final close, then resources allocated by + * ped_device_open() are freed. + * + * \return zero on failure + */ +int +ped_device_close (PedDevice* dev) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + PED_ASSERT (dev->open_count > 0, return 0); + + if (--dev->open_count) + return ped_architecture->dev_ops->refresh_close (dev); + else + return ped_architecture->dev_ops->close (dev); +} + +/** + * Begins external access mode. External access mode allows you to + * safely do IO on the device. If a PedDevice is open, then you should + * not do any IO on that device, e.g. by calling an external program + * like e2fsck, unless you put it in external access mode. You should + * not use any libparted commands that do IO to a device, e.g. + * ped_file_system_{open|resize|copy}, ped_disk_{read|write}), while + * a device is in external access mode. + * Also, you should not ped_device_close() a device, while it is + * in external access mode. + * Note: ped_device_begin_external_access_mode() does things like + * tell the kernel to flush its caches. + * + * Close a device while pretending it is still open. + * This is useful for temporarily suspending libparted access to the device + * in order for an external program to access it. + * (Running external programs while the device is open can cause cache + * coherency problems.) + * + * In particular, this function keeps track of dev->open_count, so that + * reference counting isn't screwed up. + * + * \return zero on failure. + */ +int +ped_device_begin_external_access (PedDevice* dev) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + + dev->external_mode = 1; + if (dev->open_count) + return ped_architecture->dev_ops->close (dev); + else + return 1; +} + +/** + * \brief Complementary function to ped_device_begin_external_access. + * + * \note does things like tell the kernel to flush the device's cache. + * + * \return zero on failure. + */ +int +ped_device_end_external_access (PedDevice* dev) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (dev->external_mode, return 0); + + dev->external_mode = 0; + if (dev->open_count) + return ped_architecture->dev_ops->open (dev); + else + return 1; +} + +/** + * \internal Read count sectors from dev into buffer, beginning with sector + * start. + * + * \return zero on failure. + */ +int +ped_device_read (const PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (buffer != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + PED_ASSERT (dev->open_count > 0, return 0); + + return (ped_architecture->dev_ops->read) (dev, buffer, start, count); +} + +/** + * \internal Write count sectors from buffer to dev, starting at sector + * start. + * + * \return zero on failure. + * + * \sa PedDevice::sector_size + * \sa PedDevice::phys_sector_size + */ +int +ped_device_write (PedDevice* dev, const void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (buffer != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + PED_ASSERT (dev->open_count > 0, return 0); + + return (ped_architecture->dev_ops->write) (dev, buffer, start, count); +} + +PedSector +ped_device_check (PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + PED_ASSERT (dev->open_count > 0, return 0); + + return (ped_architecture->dev_ops->check) (dev, buffer, start, count); +} + +/** + * \internal Flushes all write-behind caches that might be holding up + * writes. + * It is slow because it guarantees cache coherency among all relevant caches. + * + * \return zero on failure + */ +int +ped_device_sync (PedDevice* dev) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + PED_ASSERT (dev->open_count > 0, return 0); + + return ped_architecture->dev_ops->sync (dev); +} + +/** + * \internal Flushes all write-behind caches that might be holding writes. + * \warning Does NOT ensure cache coherency with other caches. + * If you need cache coherency, use ped_device_sync() instead. + * + * \return zero on failure + */ +int +ped_device_sync_fast (PedDevice* dev) +{ + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (!dev->external_mode, return 0); + PED_ASSERT (dev->open_count > 0, return 0); + + return ped_architecture->dev_ops->sync_fast (dev); +} + +/** + * Get a constraint that represents hardware requirements on alignment and + * geometry. + * This is, for example, important for media that have a physical sector + * size that is a multiple of the logical sector size. + * + * \warning This function is experimental for physical sector sizes not equal to + * 2^9. + */ +PedConstraint* +ped_device_get_constraint (PedDevice* dev) +{ + int multiplier = dev->phys_sector_size / dev->sector_size; + + PedAlignment* start_align = ped_alignment_new (multiplier, multiplier); + + PedConstraint* c = ped_constraint_new ( + start_align, ped_alignment_any, + ped_geometry_new (dev, 0, dev->length), + ped_geometry_new (dev, 0, dev->length), + 1, dev->length); + + return c; +} + +/** @} */ + diff --git a/libparted/disk.c b/libparted/disk.c new file mode 100644 index 0000000..262c33e --- /dev/null +++ b/libparted/disk.c @@ -0,0 +1,2250 @@ + /* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005 + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** \file disk.c */ + +/** + * \addtogroup PedDisk + * + * \brief Disk label access. + * + * Most programs will need to use ped_disk_new() or ped_disk_new_fresh() to get + * anything done. A PedDisk is always associated with a device and has a + * partition table. There are different types of partition tables (or disk + * labels). These are represented by the PedDiskType enumeration. + * + * @{ + */ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +# define N_(String) (String) +#else +# define _(String) (String) +# define N_(String) (String) +#endif /* ENABLE_NLS */ + +#include <string.h> + +/* UPDATE MODE functions */ +#ifdef DEBUG +static int _disk_check_sanity (PedDisk* disk); +#endif +static void _disk_push_update_mode (PedDisk* disk); +static void _disk_pop_update_mode (PedDisk* disk); +static int _disk_raw_insert_before (PedDisk* disk, PedPartition* loc, + PedPartition* part); +static int _disk_raw_insert_after (PedDisk* disk, PedPartition* loc, + PedPartition* part); +static int _disk_raw_remove (PedDisk* disk, PedPartition* part); +static int _disk_raw_add (PedDisk* disk, PedPartition* part); + +static PedDiskType* disk_types = NULL; + +void +ped_register_disk_type (PedDiskType* type) +{ + PED_ASSERT (type != NULL, return); + PED_ASSERT (type->ops != NULL, return); + PED_ASSERT (type->name != NULL, return); + + ((struct _PedDiskType*) type)->next = disk_types; + disk_types = (struct _PedDiskType*) type; +} + +void ped_unregister_disk_type (PedDiskType* type) +{ + PedDiskType* walk; + PedDiskType* last = NULL; + + PED_ASSERT (type != NULL, return); + + for (walk = disk_types; walk != NULL; last = walk, walk = walk->next) { + if (walk == type) break; + } + + if (last) + ((struct _PedDiskType*) last)->next = type->next; + else + disk_types = type->next; +} + +/** + * Return the next disk type registers, after "type". If "type" is + * NULL, returns the first disk type. + * + * \return Next disk; NULL if "type" is the last registered disk type. + */ +PedDiskType* +ped_disk_type_get_next (PedDiskType* type) +{ + if (type) + return type->next; + else + return disk_types; +} + +/** + * Return the disk type with a name of "name". + * + * \return Disk type; NULL if no match. + */ +PedDiskType* +ped_disk_type_get (const char* name) +{ + PedDiskType* walk = NULL; + + PED_ASSERT (name != NULL, return NULL); + + while (1) { + walk = ped_disk_type_get_next (walk); + if (!walk) break; + if (strcasecmp (walk->name, name) == 0) break; + } + return walk; +} + +/** + * Return the type of partition table detected on "dev". + * + * \return Type; NULL if none was detected. + */ +PedDiskType* +ped_disk_probe (PedDevice* dev) +{ + PedDiskType* walk = NULL; + + PED_ASSERT (dev != NULL, return NULL); + + if (!ped_device_open (dev)) + return NULL; + + ped_exception_fetch_all (); + while (1) { + walk = ped_disk_type_get_next (walk); + if (!walk) break; + if (walk->ops->probe (dev)) break; + } + if (ped_exception) + ped_exception_catch (); + ped_exception_leave_all (); + + ped_device_close (dev); + return walk; +} + +/** + * Read the partition table off a device (if one is found). + * + * \warning May modify \p dev->cylinders, \p dev->heads and \p dev->sectors + * if the partition table indicates that the existing values + * are incorrect. + * + * \return A new \link _PedDisk PedDisk \endlink object; + * NULL on failure (e.g. partition table not detected). + */ +PedDisk* +ped_disk_new (PedDevice* dev) +{ + PedDiskType* type; + PedDisk* disk; + + PED_ASSERT (dev != NULL, return NULL); + + if (!ped_device_open (dev)) + goto error; + + type = ped_disk_probe (dev); + if (!type) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to open %s - unrecognised disk label."), + dev->path); + goto error_close_dev; + } + disk = ped_disk_new_fresh (dev, type); + if (!disk) + goto error_close_dev; + if (!type->ops->read (disk)) + goto error_destroy_disk; + disk->needs_clobber = 0; + ped_device_close (dev); + return disk; + +error_destroy_disk: + ped_disk_destroy (disk); +error_close_dev: + ped_device_close (dev); +error: + return NULL; +} + +static int +_add_duplicate_part (PedDisk* disk, PedPartition* old_part) +{ + PedPartition* new_part; + PedConstraint* constraint_exact; + + new_part = disk->type->ops->partition_duplicate (old_part); + if (!new_part) + goto error; + new_part->disk = disk; + + constraint_exact = ped_constraint_exact (&new_part->geom); + if (!constraint_exact) + goto error_destroy_new_part; + if (!ped_disk_add_partition (disk, new_part, constraint_exact)) + goto error_destroy_constraint_exact; + ped_constraint_destroy (constraint_exact); + return 1; + +error_destroy_constraint_exact: + ped_constraint_destroy (constraint_exact); +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + return 0; +} + +/** + * Clone a \link _PedDisk PedDisk \endlink object. + * + * \return Deep copy of \p old_disk, NULL on failure. + */ +PedDisk* +ped_disk_duplicate (const PedDisk* old_disk) +{ + PedDisk* new_disk; + PedPartition* old_part; + + PED_ASSERT (old_disk != NULL, return NULL); + PED_ASSERT (!old_disk->update_mode, return NULL); + PED_ASSERT (old_disk->type->ops->duplicate != NULL, return NULL); + PED_ASSERT (old_disk->type->ops->partition_duplicate != NULL, + return NULL); + + new_disk = old_disk->type->ops->duplicate (old_disk); + if (!new_disk) + goto error; + + _disk_push_update_mode (new_disk); + for (old_part = ped_disk_next_partition (old_disk, NULL); old_part; + old_part = ped_disk_next_partition (old_disk, old_part)) { + if (ped_partition_is_active (old_part)) { + if (!_add_duplicate_part (new_disk, old_part)) + goto error_destroy_new_disk; + } + } + _disk_pop_update_mode (new_disk); + return new_disk; + +error_destroy_new_disk: + ped_disk_destroy (new_disk); +error: + return NULL; +} + +/** + * Remove all identifying signatures of a partition table, + * except for partition tables of a given type. + * + * \return 0 on error, 1 otherwise. + * + * \sa ped_disk_clobber() + */ +int +ped_disk_clobber_exclude (PedDevice* dev, const PedDiskType* exclude) +{ + PedDiskType* walk; + + PED_ASSERT (dev != NULL, goto error); + + if (!ped_device_open (dev)) + goto error; + + for (walk = ped_disk_type_get_next (NULL); walk; + walk = ped_disk_type_get_next (walk)) { + int probed; + + if (walk == exclude) + continue; + + ped_exception_fetch_all (); + probed = walk->ops->probe (dev); + if (!probed) + ped_exception_catch (); + ped_exception_leave_all (); + + if (probed && walk->ops->clobber) { + if (!walk->ops->clobber (dev)) + goto error_close_dev; + } + } + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +/** + * Remove all identifying signatures of a partition table, + * + * \return 0 on error, 1 otherwise. + * + * \sa ped_disk_clobber_exclude() + */ +int +ped_disk_clobber (PedDevice* dev) +{ + return ped_disk_clobber_exclude (dev, NULL); +} + +/** + * Create a new partition table on \p dev. + * + * This new partition table is only created in-memory, and nothing is written + * to disk until ped_disk_commit_to_dev() is called. + * + * \return The newly constructed \link _PedDisk PedDisk \endlink, + * NULL on failure. + */ +PedDisk* +ped_disk_new_fresh (PedDevice* dev, const PedDiskType* type) +{ + PedDisk* disk; + + PED_ASSERT (dev != NULL, return NULL); + PED_ASSERT (type != NULL, return NULL); + PED_ASSERT (type->ops->alloc != NULL, return NULL); + + disk = type->ops->alloc (dev); + if (!disk) + goto error; + _disk_pop_update_mode (disk); + PED_ASSERT (disk->update_mode == 0, goto error_destroy_disk); + + disk->needs_clobber = 1; + return disk; + +error_destroy_disk: + ped_disk_destroy (disk); +error: + return NULL; +} + +PedDisk* +_ped_disk_alloc (const PedDevice* dev, const PedDiskType* disk_type) +{ + PedDisk* disk; + + disk = (PedDisk*) ped_malloc (sizeof (PedDisk)); + if (!disk) + goto error; + + disk->dev = (PedDevice*)dev; + disk->type = disk_type; + disk->update_mode = 1; + disk->part_list = NULL; + return disk; + +error_free_disk: + ped_free (disk); +error: + return NULL; +} + +void +_ped_disk_free (PedDisk* disk) +{ + _disk_push_update_mode (disk); + ped_disk_delete_all (disk); + ped_free (disk); +} + +/** + * Close \p disk. + * + * What this function does depends on the PedDiskType of \p disk, + * but you can generally assume that outstanding writes are flushed + * (this mainly means that _ped_disk_free is called). + */ +void +ped_disk_destroy (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, return); + PED_ASSERT (!disk->update_mode, return); + + disk->type->ops->free (disk); +} + +/** + * Tell the operating system kernel about the partition table layout + * of \p disk. + * + * This is rather loosely defined: for example, on old versions of Linux, + * it simply calls the BLKRRPART ioctl, which tells the kernel to + * reread the partition table. On newer versions (2.4.x), it will + * use the new blkpg interface to tell Linux where each partition + * starts/ends, etc. In this case, Linux does not need to have support for + * a specific type of partition table. + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit_to_os (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, return 0); + + if (!ped_device_open (disk->dev)) + goto error; + if (!ped_architecture->disk_ops->disk_commit (disk)) + goto error_close_dev; + ped_device_close (disk->dev); + return 1; + +error_close_dev: + ped_device_close (disk->dev); +error: + return 0; +} + +/** + * Write the changes made to the in-memory description + * of a partition table to the device. + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit_to_dev (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, goto error); + PED_ASSERT (!disk->update_mode, goto error); + + if (!disk->type->ops->write) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("This libparted doesn't have write support for " + "%s. Perhaps it was compiled read-only."), + disk->type->name); + goto error; + } + + if (!ped_device_open (disk->dev)) + goto error; + + if (disk->needs_clobber) { + if (!ped_disk_clobber_exclude (disk->dev, disk->type)) + goto error_close_dev; + disk->needs_clobber = 0; + } + if (!disk->type->ops->write (disk)) + goto error_close_dev; + ped_device_close (disk->dev); + return 1; + +error_close_dev: + ped_device_close (disk->dev); +error: + return 0; +} + +/* + * This function writes the in-memory changes to a partition table to + * disk and informs the operating system of the changes. + * + * \note Equivalent to calling first ped_disk_commit_to_dev(), then + * ped_disk_commit_to_os(). + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit (PedDisk* disk) +{ + if (!ped_disk_commit_to_dev (disk)) + return 0; + return ped_disk_commit_to_os (disk); +} + +/** + * \addtogroup PedPartition + * + * @{ + */ + +/** + * Check whether a partition is mounted or busy in some + * other way. + * + * \note An extended partition is busy if any logical partitions are mounted. + * + * \return \c 1 if busy. + */ +int +ped_partition_is_busy (const PedPartition* part) +{ + PED_ASSERT (part != NULL, return 1); + + return ped_architecture->disk_ops->partition_is_busy (part); +} + +/** + * Return a path that can be used to address the partition in the + * operating system. + */ +char* +ped_partition_get_path (const PedPartition* part) +{ + PED_ASSERT (part != NULL, return NULL); + + return ped_architecture->disk_ops->partition_get_path (part); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +/** + * Perform a sanity check on a partition table. + * + * \note The check performed is generic (i.e. it does not depends on the label + * type of the disk. + * + * \throws PED_EXCEPTION_WARNING if a partition type ID does not match the file + * system on it. + * + * \return 0 if the check fails, 1 otherwise. + */ +int +ped_disk_check (PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL, return 0); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + const PedFileSystemType* fs_type = walk->fs_type; + PedGeometry* geom; + PedSector length_error; + PedSector max_length_error; + + if (!ped_partition_is_active (walk) || !fs_type) + continue; + + geom = ped_file_system_probe_specific (fs_type, &walk->geom); + if (!geom) + continue; + + length_error = abs (walk->geom.length - geom->length); + max_length_error = PED_MAX (4096, walk->geom.length / 100); + if (!ped_geometry_test_inside (&walk->geom, geom) + || length_error > max_length_error) { + char* part_size = ped_unit_format (disk->dev, walk->geom.length); + char* fs_size = ped_unit_format (disk->dev, geom->length); + PedExceptionOption choice; + + choice = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Partition %d is %s, but the file system is " + "%s."), + walk->num, part_size, fs_size); + + ped_free (part_size); + ped_free (fs_size); + + if (choice != PED_EXCEPTION_IGNORE) + return 0; + } + } + + return 1; +} + +/** + * This function checks if a particular type of partition table supports + * a feature. + * + * \return 1 if \p disk_type supports \p feature, 0 otherwise. + */ +int +ped_disk_type_check_feature (const PedDiskType* disk_type, + PedDiskTypeFeature feature) +{ + return (disk_type->features & feature) != 0; +} + +/** + * Get the number of primary partitions. + */ +int +ped_disk_get_primary_partition_count (PedDisk* disk) +{ + PedPartition* walk; + int count = 0; + + PED_ASSERT (disk != NULL, return 0); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_partition_is_active (walk) + && ! (walk->type & PED_PARTITION_LOGICAL)) + count++; + } + + return count; +} + +/** + * Get the highest partition number on \p disk. + */ +int +ped_disk_get_last_partition_num (PedDisk* disk) +{ + PedPartition* walk; + int highest = -1; + + PED_ASSERT (disk != NULL, return 0); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (walk->num > highest) + highest = walk->num; + } + + return highest; +} + +/** + * Get the maximum number of (primary) partitions the disk label supports. + * + * For example, MacIntosh partition maps can have different sizes, + * and accordingly support a different number of partitions. + */ +int +ped_disk_get_max_primary_partition_count (const PedDisk* disk) +{ + PED_ASSERT (disk->type != NULL, return 0); + PED_ASSERT (disk->type->ops->get_max_primary_partition_count != NULL, + return 0); + + return disk->type->ops->get_max_primary_partition_count (disk); +} + +/** + * \internal We turned a really nasty bureaucracy problem into an elegant maths + * problem :-) Basically, there are some constraints to a partition's + * geometry: + * + * (1) it must start and end on a "disk" block, determined by the disk label + * (not the hardware). (constraint represented by a PedAlignment) + * + * (2) if we're resizing a partition, we MIGHT need to keep each block aligned. + * Eg: if an ext2 file system has 4k blocks, then we can only move the start + * by a multiple of 4k. (constraint represented by a PedAlignment) + * + * (3) we need to keep the start and end within the device's physical + * boundaries. (constraint represented by a PedGeometry) + * + * Satisfying (1) and (2) simultaneously required a bit of fancy maths ;-) See + * ped_alignment_intersect() + * + * The application of these constraints is in disk_*.c's *_partition_align() + * function. + */ +static int +_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->num != -1, return 0); + PED_ASSERT (part->disk != NULL, return 0); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL, return 0); + PED_ASSERT (disk_type->ops->partition_align != NULL, return 0); + PED_ASSERT (part->disk->update_mode, return 0); + + return disk_type->ops->partition_align (part, constraint); +} + +static int +_partition_enumerate (PedPartition* part) +{ + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL, return 0); + PED_ASSERT (disk_type->ops->partition_enumerate != NULL, return 0); + + return disk_type->ops->partition_enumerate (part); +} + +/** + * Gives all the (active) partitions a number. It should preserve the numbers + * and orders as much as possible. + */ +static int +ped_disk_enumerate_partitions (PedDisk* disk) +{ + PedPartition* walk; + int i; + int end; + + PED_ASSERT (disk != NULL, return 0); + +/* first "sort" already-numbered partitions. (e.g. if a logical partition + * is removed, then all logical partitions that were number higher MUST be + * renumbered) + */ + end = ped_disk_get_last_partition_num (disk); + for (i=1; i<=end; i++) { + walk = ped_disk_get_partition (disk, i); + if (walk) { + if (!_partition_enumerate (walk)) + return 0; + } + } + +/* now, number un-numbered partitions */ + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_partition_is_active (walk) && walk->num == -1) { + if (!_partition_enumerate (walk)) + return 0; + } + } + + return 1; +} + +static int +_disk_remove_metadata (PedDisk* disk) +{ + PedPartition* walk = NULL; + PedPartition* next; + + PED_ASSERT (disk != NULL, return 0); + + next = ped_disk_next_partition (disk, walk); + + while (next) { + walk = next; + while (1) { + next = ped_disk_next_partition (disk, next); + if (!next || next->type & PED_PARTITION_METADATA) + break; + } + if (walk->type & PED_PARTITION_METADATA) + ped_disk_delete_partition (disk, walk); + } + return 1; +} + +static int +_disk_alloc_metadata (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, return 0); + + if (!disk->update_mode) + _disk_remove_metadata (disk); + + return disk->type->ops->alloc_metadata (disk); +} + +static int +_disk_remove_freespace (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + + walk = ped_disk_next_partition (disk, NULL); + for (; walk; walk = next) { + next = ped_disk_next_partition (disk, walk); + + if (walk->type & PED_PARTITION_FREESPACE) { + _disk_raw_remove (disk, walk); + ped_partition_destroy (walk); + } + } + + return 1; +} + +static int +_alloc_extended_freespace (PedDisk* disk) +{ + PedSector last_end; + PedPartition* walk; + PedPartition* last; + PedPartition* free_space; + PedPartition* extended_part; + + extended_part = ped_disk_extended_partition (disk); + if (!extended_part) + return 1; + + last_end = extended_part->geom.start; + last = NULL; + + for (walk = extended_part->part_list; walk; walk = walk->next) { + if (walk->geom.start > last_end + 1) { + free_space = ped_partition_new ( + disk, + PED_PARTITION_FREESPACE + | PED_PARTITION_LOGICAL, + NULL, + last_end + 1, walk->geom.start - 1); + _disk_raw_insert_before (disk, walk, free_space); + } + + last = walk; + last_end = last->geom.end; + } + + if (last_end < extended_part->geom.end) { + free_space = ped_partition_new ( + disk, + PED_PARTITION_FREESPACE | PED_PARTITION_LOGICAL, + NULL, + last_end + 1, extended_part->geom.end); + + if (last) + return _disk_raw_insert_after (disk, last, free_space); + else + extended_part->part_list = free_space; + } + + return 1; +} + +static int +_disk_alloc_freespace (PedDisk* disk) +{ + PedSector last_end; + PedPartition* walk; + PedPartition* last; + PedPartition* free_space; + + if (!_disk_remove_freespace (disk)) + return 0; + if (!_alloc_extended_freespace (disk)) + return 0; + + last = NULL; + last_end = -1; + + for (walk = disk->part_list; walk; walk = walk->next) { + if (walk->geom.start > last_end + 1) { + free_space = ped_partition_new (disk, + PED_PARTITION_FREESPACE, NULL, + last_end + 1, walk->geom.start - 1); + _disk_raw_insert_before (disk, walk, free_space); + } + + last = walk; + last_end = last->geom.end; + } + + if (last_end < disk->dev->length - 1) { + free_space = ped_partition_new (disk, + PED_PARTITION_FREESPACE, NULL, + last_end + 1, disk->dev->length - 1); + if (last) + return _disk_raw_insert_after (disk, last, free_space); + else + disk->part_list = free_space; + } + + return 1; +} + +/** + * Update mode: used when updating the internal representation of the partition + * table. In update mode, the metadata and freespace placeholder/virtual + * partitions are removed, making it much easier for various manipulation + * routines... + */ +static void +_disk_push_update_mode (PedDisk* disk) +{ + if (!disk->update_mode) { +#ifdef DEBUG + _disk_check_sanity (disk); +#endif + + _disk_remove_freespace (disk); + disk->update_mode++; + _disk_remove_metadata (disk); + +#ifdef DEBUG + _disk_check_sanity (disk); +#endif + } else { + disk->update_mode++; + } +} + +static void +_disk_pop_update_mode (PedDisk* disk) +{ + PED_ASSERT (disk->update_mode, return); + + if (disk->update_mode == 1) { + /* re-allocate metadata BEFORE leaving update mode, to prevent infinite + * recursion (metadata allocation requires update mode) + */ +#ifdef DEBUG + _disk_check_sanity (disk); +#endif + + _disk_alloc_metadata (disk); + disk->update_mode--; + _disk_alloc_freespace (disk); + +#ifdef DEBUG + _disk_check_sanity (disk); +#endif + } else { + disk->update_mode--; + } +} + +/** @} */ + +/** + * \addtogroup PedPartition + * + * \brief Partition access. + * + * @{ + */ + +PedPartition* +_ped_partition_alloc (const PedDisk* disk, PedPartitionType type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + + PED_ASSERT (disk != NULL, return 0); + + part = (PedPartition*) ped_malloc (sizeof (PedPartition)); + if (!part) + goto error; + + part->prev = NULL; + part->next = NULL; + + part->disk = (PedDisk*) disk; + if (!ped_geometry_init (&part->geom, disk->dev, start, end - start + 1)) + goto error_free_part; + + part->num = -1; + part->type = type; + part->part_list = NULL; + part->fs_type = fs_type; + + return part; + +error_free_part: + ped_free (part); +error: + return NULL; +} + +void +_ped_partition_free (PedPartition* part) +{ + ped_free (part); +} + +int +_ped_partition_attempt_align (PedPartition* part, + const PedConstraint* external, + PedConstraint* internal) +{ + PedConstraint* intersection; + PedGeometry* solution; + + intersection = ped_constraint_intersect (external, internal); + ped_constraint_destroy (internal); + if (!intersection) + goto fail; + + solution = ped_constraint_solve_nearest (intersection, &part->geom); + if (!solution) + goto fail_free_intersection; + ped_geometry_set (&part->geom, solution->start, solution->length); + ped_geometry_destroy (solution); + ped_constraint_destroy (intersection); + return 1; + +fail_free_intersection: + ped_constraint_destroy (intersection); +fail: + return 0; +} + +/** + * Create a new \link _PedPartition PedPartition \endlink on \p disk. + * + * \param type One of \p PED_PARTITION_PRIMARY, \p PED_PARTITION_EXTENDED, + * \p PED_PARTITION_LOGICAL. + * + * \note The constructed partition is not added to <tt>disk</tt>'s + * partition table. Use ped_disk_add_partition() to do this. + * + * \return A new \link _PedPartition PedPartition \endlink object, + * NULL on failure. + * + * \throws PED_EXCEPTION_ERROR if \p type is \p EXTENDED or \p LOGICAL but the + * label does not support this concept. + */ +PedPartition* +ped_partition_new (const PedDisk* disk, PedPartitionType type, + const PedFileSystemType* fs_type, PedSector start, + PedSector end) +{ + int supports_extended; + PedPartition* part; + + PED_ASSERT (disk != NULL, return NULL); + PED_ASSERT (disk->type->ops->partition_new != NULL, return NULL); + + supports_extended = ped_disk_type_check_feature (disk->type, + PED_DISK_TYPE_EXTENDED); + + if (!supports_extended + && (type == PED_PARTITION_EXTENDED + || type == PED_PARTITION_LOGICAL)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s disk labels do not support extended " + "partitions."), + disk->type->name); + goto error; + } + + part = disk->type->ops->partition_new (disk, type, fs_type, start, end); + if (!part) + goto error; + + if (fs_type || part->type == PED_PARTITION_EXTENDED) { + if (!ped_partition_set_system (part, fs_type)) + goto error_destroy_part; + } + return part; + +error_destroy_part: + ped_partition_destroy (part); +error: + return NULL; +} + +/** + * Destroy a \link _PedPartition PedPartition \endlink object. + * + * \note Should not be called on a partition that is in a partition table. + * Use ped_disk_delete_partition() instead. + */ +void +ped_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + PED_ASSERT (part->disk != NULL, return); + PED_ASSERT (part->disk->type->ops->partition_new != NULL, return); + + part->disk->type->ops->partition_destroy (part); +} + + +/** + * Return whether or not the partition is "active". + * + * A partition is active if \p part->type is neither \p PED_PARTITION_METADATA + * nor \p PED_PARTITION_FREE. + */ +int +ped_partition_is_active (const PedPartition* part) +{ + PED_ASSERT (part != NULL, return 0); + + return !(part->type & PED_PARTITION_FREESPACE + || part->type & PED_PARTITION_METADATA); +} + +/** + * Set the state (\c 1 or \c 0) of a flag on a partition. + * + * Flags are disk label specific, although they have a global + * "namespace": the flag PED_PARTITION_BOOT, for example, roughly means + * "this" partition is bootable". But this means different things on different + * disk labels (and may not be defined on some disk labels). For example, + * on MS-DOS disk labels, there can only be one boot partition, and this + * refers to the partition that will be booted from on startup. On PC98 + * disk labels, the user can choose from any bootable partition on startup. + * + * \note It is an error to call this on an unavailable flag -- use + * ped_partition_is_flag_available() to determine which flags are available + * for a given disk label. + * + * \throws PED_EXCEPTION_ERROR if the requested flag is not available for this + * label. + */ +int +ped_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + PedDiskOps* ops; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + PED_ASSERT (ped_partition_is_active (part), return 0); + + ops = part->disk->type->ops; + PED_ASSERT (ops->partition_set_flag != NULL, return 0); + PED_ASSERT (ops->partition_is_flag_available != NULL, return 0); + + if (!ops->partition_is_flag_available (part, flag)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "The flag '%s' is not available for %s disk labels.", + ped_partition_flag_get_name (flag), + part->disk->type->name); + return 0; + } + + return ops->partition_set_flag (part, flag, state); +} + +/** + * Get the state (\c 1 or \c 0) of a flag on a partition. + * + * See ped_partition_set_flag() for conditions that must hold. + * + * \todo Where's the check for flag availability? + */ +int +ped_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + PED_ASSERT (part->disk->type->ops->partition_get_flag != NULL, + return 0); + PED_ASSERT (ped_partition_is_active (part), return 0); + + return part->disk->type->ops->partition_get_flag (part, flag); +} + +/** + * Check whether a given flag is available on a partition. + * + * \return \c 1 if the flag is available. + */ +int +ped_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + PED_ASSERT (part->disk->type->ops->partition_is_flag_available != NULL, + return 0); + PED_ASSERT (ped_partition_is_active (part), return 0); + + return part->disk->type->ops->partition_is_flag_available (part, flag); +} + +/** + * Sets the system type on the partition to \p fs_type. + * + * \note The file system may be opened, to get more information about the + * file system, e.g. to determine if it's FAT16 or FAT32. + * + * \return \c 0 on failure. + */ +int +ped_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + PedFileSystem* fs; + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (ped_partition_is_active (part), return 0); + PED_ASSERT (part->disk != NULL, return 0); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL, return 0); + PED_ASSERT (disk_type->ops != NULL, return 0); + PED_ASSERT (disk_type->ops->partition_set_system != NULL, return 0); + + return disk_type->ops->partition_set_system (part, fs_type); +} + +static int +_assert_partition_name_feature (const PedDiskType* disk_type) +{ + if (!ped_disk_type_check_feature ( + disk_type, PED_DISK_TYPE_PARTITION_NAME)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "%s disk labels do not support partition names.", + disk_type->name); + return 0; + } + return 1; +} + +/** + * Sets the name of a partition. + * + * \note This will only work if the disk label supports it. + * You can use + * \code + * ped_disk_type_check_feature (part->disk->type, PED_DISK_TYPE_PARTITION_NAME); + * \endcode + * to check whether this feature is enabled for a label. + * + * \note \p name will not be modified by libparted. It can be freed + * by the caller immediately after ped_partition_set_name() is called. + * + * \return \c 1 on success, \c 0 otherwise. + */ +int +ped_partition_set_name (PedPartition* part, const char* name) +{ + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + PED_ASSERT (ped_partition_is_active (part), return 0); + PED_ASSERT (name != NULL, return 0); + + if (!_assert_partition_name_feature (part->disk->type)) + return 0; + + PED_ASSERT (part->disk->type->ops->partition_set_name != NULL, + return 0); + part->disk->type->ops->partition_set_name (part, name); + return 1; +} + +/** + * Returns the name of a partition \p part. This will only work if the disk + * label supports it. + * + * \note The returned string should not be modified. It should + * not be referenced after the partition is destroyed. + */ +const char* +ped_partition_get_name (const PedPartition* part) +{ + PED_ASSERT (part != NULL, return NULL); + PED_ASSERT (part->disk != NULL, return 0); + PED_ASSERT (ped_partition_is_active (part), return 0); + + if (!_assert_partition_name_feature (part->disk->type)) + return NULL; + + PED_ASSERT (part->disk->type->ops->partition_get_name != NULL, + return NULL); + return part->disk->type->ops->partition_get_name (part); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +PedPartition* +ped_disk_extended_partition (const PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL, return 0); + + for (walk = disk->part_list; walk; walk = walk->next) { + if (walk->type == PED_PARTITION_EXTENDED) + break; + } + return walk; +} + +/** + * Return the next partition after \p part on \p disk. If \p part is \c NULL, + * return the first partition. If \p part is the last partition, returns + * \c NULL. If \p part is an extended partition, returns the first logical + * partition. If this is called repeatedly passing the return value as \p part, + * a depth-first traversal is executed. + * + * \return The next partition, \c NULL if no more partitions left. + */ +PedPartition* +ped_disk_next_partition (const PedDisk* disk, const PedPartition* part) +{ + PED_ASSERT (disk != NULL, return 0); + + if (!part) + return disk->part_list; + if (part->type == PED_PARTITION_EXTENDED) + return part->part_list ? part->part_list : part->next; + if (part->next) + return part->next; + if (part->type & PED_PARTITION_LOGICAL) + return ped_disk_extended_partition (disk)->next; + return NULL; +} + +/** @} */ + +#ifdef DEBUG +static int +_disk_check_sanity (PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL, return 0); + + for (walk = disk->part_list; walk; walk = walk->next) { + PED_ASSERT (!(walk->type & PED_PARTITION_LOGICAL), return 0); + PED_ASSERT (!walk->prev || walk->prev->next == walk, return 0); + } + + if (!ped_disk_extended_partition (disk)) + return 1; + + for (walk = ped_disk_extended_partition (disk)->part_list; walk; + walk = walk->next) { + PED_ASSERT (walk->type & PED_PARTITION_LOGICAL, return 0); + if (walk->prev) + PED_ASSERT (walk->prev->next == walk, return 0); + } + return 1; +} +#endif + +/** + * Returns the partition numbered \p num. + * + * \return \c NULL if the specified partition does not exist. + */ +PedPartition* +ped_disk_get_partition (const PedDisk* disk, int num) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL, return 0); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (walk->num == num && !(walk->type & PED_PARTITION_FREESPACE)) + return walk; + } + + return NULL; +} + +/** + * Returns the partition that contains sect. If sect lies within a logical + * partition, then the logical partition is returned (not the extended + * partition). + */ +PedPartition* +ped_disk_get_partition_by_sector (const PedDisk* disk, PedSector sect) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL, return 0); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_geometry_test_sector_inside (&walk->geom, sect) + && walk->type != PED_PARTITION_EXTENDED) + return walk; + } + + /* should never get here, unless sect is outside of disk's useable + * part, or we're in "update mode", and the free space place-holders + * have been removed with _disk_remove_freespace() + */ + return NULL; +} + +/* I'm beginning to agree with Sedgewick :-/ */ +static int +_disk_raw_insert_before (PedDisk* disk, PedPartition* loc, PedPartition* part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (loc != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + part->prev = loc->prev; + part->next = loc; + if (part->prev) { + part->prev->next = part; + } else { + if (loc->type & PED_PARTITION_LOGICAL) + ped_disk_extended_partition (disk)->part_list = part; + else + disk->part_list = part; + } + loc->prev = part; + + return 1; +} + +static int +_disk_raw_insert_after (PedDisk* disk, PedPartition* loc, PedPartition* part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (loc != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + part->prev = loc; + part->next = loc->next; + if (loc->next) + loc->next->prev = part; + loc->next = part; + + return 1; +} + +static int +_disk_raw_remove (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + if (part->prev) { + part->prev->next = part->next; + if (part->next) + part->next->prev = part->prev; + } else { + if (part->type & PED_PARTITION_LOGICAL) { + ped_disk_extended_partition (disk)->part_list + = part->next; + } else { + disk->part_list = part->next; + } + if (part->next) + part->next->prev = NULL; + } + + return 1; +} + +/* + *UPDATE MODE ONLY + */ +static int +_disk_raw_add (PedDisk* disk, PedPartition* part) +{ + PedPartition* walk; + PedPartition* last; + PedPartition* ext_part; + + PED_ASSERT (disk->update_mode, return 0); + + ext_part = ped_disk_extended_partition (disk); + + last = NULL; + walk = (part->type & PED_PARTITION_LOGICAL) ? + ext_part->part_list : disk->part_list; + + for (; walk; last = walk, walk = walk->next) { + if (walk->geom.start > part->geom.end) + break; + } + + if (walk) { + return _disk_raw_insert_before (disk, walk, part); + } else { + if (last) { + return _disk_raw_insert_after (disk, last, part); + } else { + if (part->type & PED_PARTITION_LOGICAL) + ext_part->part_list = part; + else + disk->part_list = part; + } + } + + return 1; +} + +static PedConstraint* +_partition_get_overlap_constraint (PedPartition* part, PedGeometry* geom) +{ + PedSector min_start; + PedSector max_end; + PedPartition* walk; + PedGeometry free_space; + + PED_ASSERT (part->disk->update_mode, return NULL); + PED_ASSERT (part->geom.dev == geom->dev, return NULL); + + if (part->type & PED_PARTITION_LOGICAL) { + PedPartition* ext_part; + + ext_part = ped_disk_extended_partition (part->disk); + PED_ASSERT (ext_part != NULL, return NULL); + + min_start = ext_part->geom.start; + max_end = ext_part->geom.end; + walk = ext_part->part_list; + } else { + min_start = 0; + max_end = part->disk->dev->length - 1; + walk = part->disk->part_list; + } + + while (walk != NULL + && (walk->geom.start < geom->start + || min_start >= walk->geom.start)) { + if (walk != part) + min_start = walk->geom.end + 1; + walk = walk->next; + } + + if (walk == part) + walk = walk->next; + + if (walk) + max_end = walk->geom.start - 1; + + if (min_start >= max_end) + return NULL; + + ped_geometry_init (&free_space, part->disk->dev, + min_start, max_end - min_start + 1); + return ped_constraint_new_from_max (&free_space); +} + +/* + * Returns \c 0 if the partition, \p part overlaps with any partitions on the + * \p disk. The geometry of \p part is taken to be \p geom, NOT \p part->geom + * (the idea here is to check if \p geom is valid, before changing \p part). + * + * This is useful for seeing if a resized partitions new geometry is going to + * fit, without the existing geomtry getting in the way. + * + * Note: overlap with an extended partition is also allowed, provided that + * \p geom lies completely inside the extended partition. + */ +static int +_disk_check_part_overlaps (PedDisk* disk, PedPartition* part) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + for (walk = ped_disk_next_partition (disk, NULL); walk; + walk = ped_disk_next_partition (disk, walk)) { + if (walk->type & PED_PARTITION_FREESPACE) + continue; + if (walk == part) + continue; + if (part->type & PED_PARTITION_EXTENDED + && walk->type & PED_PARTITION_LOGICAL) + continue; + + if (ped_geometry_test_overlap (&walk->geom, &part->geom)) { + if (walk->type & PED_PARTITION_EXTENDED + && part->type & PED_PARTITION_LOGICAL + && ped_geometry_test_inside (&walk->geom, + &part->geom)) + continue; + return 0; + } + } + + return 1; +} + +static int +_partition_check_basic_sanity (PedDisk* disk, PedPartition* part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + + PED_ASSERT (part->disk == disk, return 0); + + PED_ASSERT (part->geom.start >= 0, return 0); + PED_ASSERT (part->geom.end < disk->dev->length, return 0); + PED_ASSERT (part->geom.start <= part->geom.end, return 0); + + if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED) + && (part->type == PED_PARTITION_EXTENDED + || part->type == PED_PARTITION_LOGICAL)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s disk labels don't support logical or extended " + "partitions."), + disk->type->name); + return 0; + } + + if (ped_partition_is_active (part) + && ! (part->type & PED_PARTITION_LOGICAL)) { + if (ped_disk_get_primary_partition_count (disk) + 1 + > ped_disk_get_max_primary_partition_count (disk)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Too many primary partitions.")); + return 0; + } + } + + if ((part->type & PED_PARTITION_LOGICAL) && !ext_part) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't add a logical partition to %s, because " + "there is no extended partition."), + disk->dev->path); + return 0; + } + + return 1; +} + +static int +_check_extended_partition (PedDisk* disk, PedPartition* part) +{ + PedPartition* walk; + PedPartition* ext_part; + + PED_ASSERT (disk != NULL, return 0); + ext_part = ped_disk_extended_partition (disk); + if (!ext_part) ext_part = part; + PED_ASSERT (ext_part != NULL, return 0); + + if (part != ext_part) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have more than one extended partition on %s."), + disk->dev->path); + return 0; + } + + for (walk = ext_part->part_list; walk; walk = walk->next) { + if (!ped_geometry_test_inside (&ext_part->geom, &walk->geom)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have logical partitions outside of " + "the extended partition.")); + return 0; + } + } + return 1; +} + +static int +_check_partition (PedDisk* disk, PedPartition* part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + + PED_ASSERT (part->geom.start <= part->geom.end, return 0); + + if (part->type == PED_PARTITION_EXTENDED) { + if (!_check_extended_partition (disk, part)) + return 0; + } + + if (part->type & PED_PARTITION_LOGICAL + && !ped_geometry_test_inside (&ext_part->geom, &part->geom)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have a logical partition outside of the " + "extended partition on %s."), + disk->dev->path); + return 0; + } + + if (!_disk_check_part_overlaps (disk, part)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have overlapping partitions.")); + return 0; + } + + if (! (part->type & PED_PARTITION_LOGICAL) + && ext_part && ext_part != part + && ped_geometry_test_inside (&ext_part->geom, &part->geom)) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Can't have a primary partition inside an extended " + "partition.")); + return 0; + } + + return 1; +} + +/** + * Adds PedPartition \p part to PedPartition \p disk. + * + * \warning The partition's geometry may be changed, subject to \p constraint. + * You could set \p constraint to <tt>ped_constraint_exact(&part->geom)</tt>, + * but many partition table schemes have special requirements on the start + * and end of partitions. Therefore, having an overly strict constraint + * will probably mean that this function will fail (in which + * case \p part will be left unmodified) + * \p part is assigned a number (\p part->num) in this process. + * + * \return \c 0 on failure. + */ +int +ped_disk_add_partition (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedConstraint* overlap_constraint = NULL; + PedConstraint* constraints = NULL; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + if (!_partition_check_basic_sanity (disk, part)) + return 0; + + _disk_push_update_mode (disk); + + if (ped_partition_is_active (part)) { + overlap_constraint + = _partition_get_overlap_constraint (part, &part->geom); + constraints = ped_constraint_intersect (overlap_constraint, + constraint); + + if (!constraints && constraint) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have overlapping partitions.")); + goto error; + } + + if (!_partition_enumerate (part)) + goto error; + if (!_partition_align (part, constraints)) + goto error; + } + if (!_check_partition (disk, part)) + goto error; + if (!_disk_raw_add (disk, part)) + goto error; + + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + _disk_pop_update_mode (disk); +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + return 1; + +error: + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + _disk_pop_update_mode (disk); + return 0; +} + +/** + * Removes PedPartition \p part from PedDisk \p disk. + * + * If \p part is an extended partition, it must not contain any logical + * partitions. \p part is *NOT* destroyed. The caller must call + * ped_partition_destroy(), or use ped_disk_delete_partition() instead. + * + * \return \c 0 on error. + */ +int +ped_disk_remove_partition (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + _disk_push_update_mode (disk); + PED_ASSERT (part->part_list == NULL, goto error); + _disk_raw_remove (disk, part); + _disk_pop_update_mode (disk); + ped_disk_enumerate_partitions (disk); + return 1; + +error: + _disk_pop_update_mode (disk); + return 0; +} + +static int +ped_disk_delete_all_logical (PedDisk* disk); + +/** + * Removes \p part from \p disk, and destroys \p part. + * + * \return \c 0 on failure. + */ +int +ped_disk_delete_partition (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + _disk_push_update_mode (disk); + if (part->type == PED_PARTITION_EXTENDED) + ped_disk_delete_all_logical (disk); + ped_disk_remove_partition (disk, part); + ped_partition_destroy (part); + _disk_pop_update_mode (disk); + + return 1; +} + +static int +ped_disk_delete_all_logical (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + PedPartition* ext_part; + + PED_ASSERT (disk != NULL, return 0); + ext_part = ped_disk_extended_partition (disk); + PED_ASSERT (ext_part != NULL, return 0); + + for (walk = ext_part->part_list; walk; walk = next) { + next = walk->next; + + if (!ped_disk_delete_partition (disk, walk)) + return 0; + } + return 1; +} + +/** + * Removes and destroys all partitions on \p disk. + * + * \return \c 0 on failure. + */ +int +ped_disk_delete_all (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + + PED_ASSERT (disk != NULL, return 0); + + _disk_push_update_mode (disk); + + for (walk = disk->part_list; walk; walk = next) { + next = walk->next; + + if (!ped_disk_delete_partition (disk, walk)) + return 0; + } + + _disk_pop_update_mode (disk); + + return 1; +} + +/** + * Sets the geometry of \p part (i.e. change a partitions location). This can + * fail for many reasons, e.g. can't overlap with other partitions. If it + * does fail, \p part will remain unchanged. Returns \c 0 on failure. \p part's + * geometry may be set to something different from \p start and \p end subject + * to \p constraint. + * + * \warning The constraint warning from ped_disk_add_partition() applies. + * + * \note this function does not modify the contents of the partition. You need + * to call ped_file_system_resize() separately. + */ +int +ped_disk_set_partition_geom (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint, + PedSector start, PedSector end) +{ + PedConstraint* overlap_constraint = NULL; + PedConstraint* constraints = NULL; + PedGeometry old_geom; + PedGeometry new_geom; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk == disk, return 0); + + old_geom = part->geom; + ped_geometry_init (&new_geom, part->geom.dev, start, end - start + 1); + + _disk_push_update_mode (disk); + + overlap_constraint + = _partition_get_overlap_constraint (part, &new_geom); + constraints = ped_constraint_intersect (overlap_constraint, constraint); + if (!constraints && constraint) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have overlapping partitions.")); + goto error_pop_update_mode; + } + + part->geom = new_geom; + if (!_partition_align (part, constraints)) + goto error_pop_update_mode; + if (!_check_partition (disk, part)) + goto error_pop_update_mode; + + /* remove and add, to ensure the ordering gets updated if necessary */ + _disk_raw_remove (disk, part); + _disk_raw_add (disk, part); + + _disk_pop_update_mode (disk); + + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + return 1; + +error_pop_update_mode: + _disk_pop_update_mode (disk); +error: + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + part->geom = old_geom; + return 0; +} + +/** + * Grow PedPartition \p part geometry to the maximum possible subject to + * \p constraint. The new geometry will be a superset of the old geometry. + * + * \return 0 on failure + */ +int +ped_disk_maximize_partition (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedGeometry old_geom; + PedSector global_min_start; + PedSector global_max_end; + PedSector new_start; + PedSector new_end; + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + if (part->type & PED_PARTITION_LOGICAL) { + PED_ASSERT (ext_part != NULL, return 0); + global_min_start = ext_part->geom.start; + global_max_end = ext_part->geom.end; + } else { + global_min_start = 0; + global_max_end = disk->dev->length - 1; + } + + old_geom = part->geom; + + _disk_push_update_mode (disk); + + if (part->prev) + new_start = part->prev->geom.end + 1; + else + new_start = global_min_start; + + if (part->next) + new_end = part->next->geom.start - 1; + else + new_end = global_max_end; + + if (!ped_disk_set_partition_geom (disk, part, constraint, new_start, + new_end)) + goto error; + + _disk_pop_update_mode (disk); + return 1; + +error: + constraint_any = ped_constraint_any (disk->dev); + ped_disk_set_partition_geom (disk, part, constraint_any, + old_geom.start, old_geom.end); + ped_constraint_destroy (constraint_any); + _disk_pop_update_mode (disk); + return 0; +} + +/** + * Get the maximum geometry \p part can be grown to, subject to + * \p constraint. + * + * \return \c NULL on failure. + */ +PedGeometry* +ped_disk_get_max_partition_geometry (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedGeometry old_geom; + PedGeometry* max_geom; + PedConstraint* constraint_exact; + + PED_ASSERT(disk != NULL, return NULL); + PED_ASSERT(part != NULL, return NULL); + PED_ASSERT(ped_partition_is_active (part), return NULL); + + old_geom = part->geom; + if (!ped_disk_maximize_partition (disk, part, constraint)) + return NULL; + max_geom = ped_geometry_duplicate (&part->geom); + + constraint_exact = ped_constraint_exact (&old_geom); + ped_disk_set_partition_geom (disk, part, constraint_exact, + old_geom.start, old_geom.end); + ped_constraint_destroy (constraint_exact); + + /* this assertion should never fail, because the old + * geometry was valid + */ + PED_ASSERT (ped_geometry_test_equal (&part->geom, &old_geom), + return NULL); + + return max_geom; +} + +/** + * Reduce the size of the extended partition to a minimum while still wrapping + * its logical partitions. If there are no logical partitions, remove the + * extended partition. + * + * \return 0 on failure. + */ +int +ped_disk_minimize_extended_partition (PedDisk* disk) +{ + PedPartition* first_logical; + PedPartition* last_logical; + PedPartition* walk; + PedPartition* ext_part; + PedConstraint* constraint; + int status; + + PED_ASSERT (disk != NULL, return 0); + + ext_part = ped_disk_extended_partition (disk); + if (!ext_part) + return 1; + + _disk_push_update_mode (disk); + + first_logical = ext_part->part_list; + if (!first_logical) { + _disk_pop_update_mode (disk); + return ped_disk_delete_partition (disk, ext_part); + } + + for (walk = first_logical; walk->next; walk = walk->next); + last_logical = walk; + + constraint = ped_constraint_any (disk->dev); + status = ped_disk_set_partition_geom (disk, ext_part, constraint, + first_logical->geom.start, + last_logical->geom.end); + ped_constraint_destroy (constraint); + + _disk_pop_update_mode (disk); + return status; +} + +/** + * @} + */ + +/** + * \addtogroup PedPartition + * + * @{ + */ + +/** + * Returns a name that seems mildly appropriate for a partition type \p type. + * + * Eg, if you pass (PED_PARTITION_LOGICAL & PED_PARTITION_FREESPACE), it + * will return "free". This isn't to be taken too seriously - it's just + * useful for user interfaces, so you can show the user something ;-) + * + * \note The returned string will be in English. However, + * translations are provided, so the caller can call + * dgettext("parted", RESULT) on the result. + * + */ +const char* +ped_partition_type_get_name (PedPartitionType type) +{ + if (type & PED_PARTITION_METADATA) + return N_("metadata"); + else if (type & PED_PARTITION_FREESPACE) + return N_("free"); + else if (type & PED_PARTITION_EXTENDED) + return N_("extended"); + else if (type & PED_PARTITION_LOGICAL) + return N_("logical"); + else + return N_("primary"); +} + + +/** + * Returns a name for a \p flag, e.g. PED_PARTITION_BOOT will return "boot". + * + * \note The returned string will be in English. However, + * translations are provided, so the caller can call + * dgettext("parted", RESULT) on the result. + */ +const char* +ped_partition_flag_get_name (PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + return N_("boot"); + case PED_PARTITION_ROOT: + return N_("root"); + case PED_PARTITION_SWAP: + return N_("swap"); + case PED_PARTITION_HIDDEN: + return N_("hidden"); + case PED_PARTITION_RAID: + return N_("raid"); + case PED_PARTITION_LVM: + return N_("lvm"); + case PED_PARTITION_LBA: + return N_("lba"); + case PED_PARTITION_HPSERVICE: + return N_("hp-service"); + case PED_PARTITION_PALO: + return N_("palo"); + case PED_PARTITION_PREP: + return N_("prep"); + case PED_PARTITION_MSFT_RESERVED: + return N_("msftres"); + + default: + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unknown partition flag, %d."), + flag); + return NULL; + } +} + +/** + * Iterates through all flags. + * + * ped_partition_flag_next(0) returns the first flag + * + * \return the next flag, or 0 if there are no more flags + */ +PedPartitionFlag +ped_partition_flag_next (PedPartitionFlag flag) +{ + return (flag + 1) % (PED_PARTITION_LAST_FLAG + 1); +} + +/** + * Returns the flag associated with \p name. + * + * \p name can be the English + * string, or the translation for the native language. + */ +PedPartitionFlag +ped_partition_flag_get_by_name (const char* name) +{ + PedPartitionFlag flag; + const char* flag_name; + + for (flag = ped_partition_flag_next (0); flag; + flag = ped_partition_flag_next (flag)) { + flag_name = ped_partition_flag_get_name (flag); + if (strcasecmp (name, flag_name) == 0 + || strcasecmp (name, _(flag_name)) == 0) + return flag; + } + + return 0; +} + +void +ped_partition_print (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + + printf (" %-10s %02d (%d->%d)\n", + ped_partition_type_get_name (part->type), + part->num, + (int) part->geom.start, (int) part->geom.end); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +/** + * Prints a summary of disk's partitions. Useful for debugging. + */ +void +ped_disk_print (PedDisk* disk) +{ + PedPartition* part; + + PED_ASSERT (disk != NULL, return); + + for (part = disk->part_list; part; + part = ped_disk_next_partition (disk, part)) + ped_partition_print (part); +} + +/** @} */ + diff --git a/libparted/exception.c b/libparted/exception.c new file mode 100644 index 0000000..eb9086e --- /dev/null +++ b/libparted/exception.c @@ -0,0 +1,304 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** \file exception.c */ + +/** + * \addtogroup PedException + * + * \brief Exception handling. + * + * There are a few types of exceptions: PED_EXCEPTION_INFORMATION, + * PED_EXCEPTION_WARNING, PED_EXCEPTION_ERROR, PED_EXCEPTION_FATAL, + * PED_EXCEPTION_BUG. + * + * They are "thrown" when one of the above events occur while executing + * a libparted function. For example, if ped_device_open() fails + * because the device doesn't exist, an exception will be thrown. + * Exceptions contain text describing what the event was. It will give + * at least one option for resolving the exception: PED_EXCEPTION_FIX, + * PED_EXCEPTION_YES, PED_EXCEPTION_NO, PED_EXCEPTION_OK, PED_EXCEPTION_RETRY, + * PED_EXCEPTION_IGNORE, PED_EXCEPTION_CANCEL. After an exception is thrown, + * there are two things that can happen: + * + * -# an exception handler is called, which selects how the exception should be + * resolved (usually by asking the user). Also note: an exception handler may + * choose to return PED_EXCEPTION_UNHANDLED. In this case, a default action + * will be taken by libparted (respectively the code that threw the + * exception). In general, a default action will be "safe". + * -# the exception is not handled, because the caller of the function wants to + * handle everything itself. In this case, PED_EXCEPTION_UNHANDLED is + * returned. + * + * @{ + */ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +#define N_(String) String +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +int ped_exception = 0; + +static PedExceptionOption default_handler (PedException* ex); + +static PedExceptionHandler* ex_handler = default_handler; +static PedException* ex = NULL; +static int ex_fetch_count = 0; + +static char* type_strings [] = { + N_("Information"), + N_("Warning"), + N_("Error"), + N_("Fatal"), + N_("Bug"), + N_("No Implementation") +}; + +static char* option_strings [] = { + N_("Fix"), + N_("Yes"), + N_("No"), + N_("OK"), + N_("Retry"), + N_("Ignore"), + N_("Cancel") +}; + +/** + * Return a string describing an exception type. + */ +char* +ped_exception_get_type_string (PedExceptionType ex_type) +{ + return type_strings [ex_type - 1]; +} + +/* FIXME: move this out to the prospective math.c */ +/* FIXME: this can probably be done more efficiently */ +static int +ped_log2 (int n) +{ + int x; + + PED_ASSERT (n > 0, return -1); + + for (x=0; 1 << x <= n; x++); + + return x - 1; +} + +/** + * Return a string describing an exception option. + */ +char* +ped_exception_get_option_string (PedExceptionOption ex_opt) +{ + return option_strings [ped_log2 (ex_opt)]; +} + +static PedExceptionOption +default_handler (PedException* ex) +{ + if (ex->type == PED_EXCEPTION_BUG) + fprintf (stderr, + _("A bug has been detected in GNU Parted. " + "Refer to the web site of parted " + "http://www.gnu.org/software/parted/parted.html " + "for more informations of what could be useful " + "for bug submitting! " + "Please email a bug report to " + "bug-parted@gnu.org containing at least the " + "version (%s) and the following message: "), + VERSION); + else + fprintf (stderr, "%s: ", + ped_exception_get_type_string (ex->type)); + fprintf (stderr, "%s\n", ex->message); + + switch (ex->options) { + case PED_EXCEPTION_OK: + case PED_EXCEPTION_CANCEL: + case PED_EXCEPTION_IGNORE: + return ex->options; + + default: + return PED_EXCEPTION_UNHANDLED; + } +} + +/** + * Set the exception handler. + * + * The exception handler should return ONE of the options set in ex->options, + * indicating the way the event should be resolved. + */ +void +ped_exception_set_handler (PedExceptionHandler* handler) +{ + if (handler) + ex_handler = handler; + else + ex_handler = default_handler; +} + +/** + * Get the current exception handler. + */ +PedExceptionHandler * +ped_exception_get_handler (void) +{ + if (ex_handler) + return ex_handler; + return default_handler; +} + +/** + * Assert that the current exception has been resolved. + */ +void +ped_exception_catch () +{ + if (ped_exception) { + ped_exception = 0; + + ped_free (ex->message); + ped_free (ex); + ex = NULL; + } +} + +static PedExceptionOption +do_throw () +{ + PedExceptionOption ex_opt; + + ped_exception = 1; + + if (ex_fetch_count) { + return PED_EXCEPTION_UNHANDLED; + } else { + ex_opt = ex_handler (ex); + ped_exception_catch (); + return ex_opt; + } +} + +/** + * Throw an exception. + * + * You can also use this in a program using libparted. + * "message" is a printf-like format string, so you can do + * + * \code + * ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_CANCEL, + * "Can't open %s", file_name); + * \endcode + * + * Returns the option selected to resolve the exception. If the exception was + * unhandled, PED_EXCEPTION_UNHANDLED is returned. + */ +PedExceptionOption +ped_exception_throw (PedExceptionType ex_type, + PedExceptionOption ex_opts, const char* message, ...) +{ + va_list arg_list; + + if (ex) + ped_exception_catch (); + + ex = (PedException*) malloc (sizeof (PedException)); + if (!ex) + goto no_memory; + + ex->message = (char*) malloc (8192); + if (!ex->message) + goto no_memory; + + ex->type = ex_type; + ex->options = ex_opts; + + va_start (arg_list, message); + vsnprintf (ex->message, 8192, message, arg_list); + va_end (arg_list); + + return do_throw (); + +no_memory: + fprintf (stderr, "Out of memory in exception handler!\n"); + + va_start (arg_list, message); + vfprintf (stderr, message, arg_list); + va_end (arg_list); + + return PED_EXCEPTION_UNHANDLED; +} + +/** + * Rethrow an unhandled exception. + * This means repeating the last ped_exception_throw() statement. + */ +PedExceptionOption +ped_exception_rethrow () +{ + return do_throw (); +} + +/** + * Indicates that exceptions should not go to the exception handler, but + * passed up to the calling function(s). All calls to + * ped_exception_throw() will return PED_EXCEPTION_UNHANDLED. + */ +void +ped_exception_fetch_all () +{ + ex_fetch_count++; +} + +/** + * Indicates that the calling function does not want to accept any + * responsibility for exceptions any more. + * + * \note a caller of that function may still want responsibility, so + * ped_exception_throw() may not invoke the exception handler. + * + * \warning every call to this function must have a preceding + * ped_exception_fetch_all(). + */ +void +ped_exception_leave_all () +{ + PED_ASSERT (ex_fetch_count > 0, return); + ex_fetch_count--; +} + +/** @} */ + diff --git a/libparted/filesys.c b/libparted/filesys.c new file mode 100644 index 0000000..cf33f96 --- /dev/null +++ b/libparted/filesys.c @@ -0,0 +1,786 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** \file filesys.c */ + +/** + * \addtogroup PedFileSystem + * + * \note File systems exist on a PedGeometry - NOT a PedPartition. + * + * @{ + */ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <string.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define BUFFER_SIZE 4096 /* in sectors */ + +static PedFileSystemType* fs_types = NULL; + +void +ped_file_system_type_register (PedFileSystemType* fs_type) +{ + PED_ASSERT (fs_type != NULL, return); + PED_ASSERT (fs_type->ops != NULL, return); + PED_ASSERT (fs_type->name != NULL, return); + + /* pretend that "next" isn't part of the struct :-) */ + ((struct _PedFileSystemType*) fs_type)->next = fs_types; + fs_types = (struct _PedFileSystemType*) fs_type; +} + +void +ped_file_system_type_unregister (PedFileSystemType* fs_type) +{ + PedFileSystemType* walk; + PedFileSystemType* last = NULL; + + PED_ASSERT (fs_type != NULL, return); + + for (walk = fs_types; walk != fs_type; walk = walk->next) { + if (!walk) return; + last = walk; + } + + if (last) { + ((struct _PedFileSystemType*) last)->next = fs_type->next; + } else { + fs_types = fs_types->next; + } +} + +/** + * Get a PedFileSystemType by its @p name. + * + * @return @c NULL if none found. + */ +PedFileSystemType* +ped_file_system_type_get (const char* name) +{ + PedFileSystemType* walk; + + PED_ASSERT (name != NULL, return NULL); + + for (walk = fs_types; walk != NULL; walk = walk->next) { + if (!strcasecmp (walk->name, name)) + break; + } + return walk; +} + +/** + * Get the next PedFileSystemType after @p fs_type. + * + * @return @c NULL if @p fs_type is the last item in the list. + */ +PedFileSystemType* +ped_file_system_type_get_next (const PedFileSystemType* fs_type) +{ + if (fs_type) + return fs_type->next; + else + return fs_types; +} + +/** + * Attempt to find a file system and return the region it occupies. + * + * @param fs_type The file system type to probe for. + * @param geom The region to be searched. + * + * @return @p NULL if @p fs_type file system wasn't detected + */ +PedGeometry* +ped_file_system_probe_specific ( + const PedFileSystemType* fs_type, PedGeometry* geom) +{ + PedGeometry* result; + + PED_ASSERT (fs_type != NULL, return NULL); + PED_ASSERT (fs_type->ops->probe != NULL, return NULL); + PED_ASSERT (geom != NULL, return NULL); + + if (!ped_device_open (geom->dev)) + return 0; + result = fs_type->ops->probe (geom); + ped_device_close (geom->dev); + return result; +} + +static int +_test_open (PedFileSystemType* fs_type, PedGeometry* geom) +{ + PedFileSystem* fs; + + ped_exception_fetch_all (); + fs = fs_type->ops->open (geom); + if (fs) + fs_type->ops->close (fs); + else + ped_exception_catch (); + ped_exception_leave_all (); + return fs != NULL; +} + +static PedFileSystemType* +_probe_with_open (PedGeometry* geom, int detected_count, + PedFileSystemType* detected[]) +{ + int i; + PedFileSystemType* open_detected = NULL; + + ped_device_open (geom->dev); + + /* If one and only one file system that Parted is able to open + * can be successfully opened on this geometry, return it. + * If more than one can be, return NULL. + */ + for (i=0; i<detected_count; i++) { + if (!detected[i]->ops->open || !_test_open (detected [i], geom)) + continue; + + if (open_detected) { + ped_device_close (geom->dev); + return NULL; + } else { + open_detected = detected [i]; + } + } + + /* If no file system has been successfully opened, and + * if Parted has detected at most one unopenable file system, + * return it. + */ + if (!open_detected) + for (i=0; i<detected_count; i++) { + if (detected[i]->ops->open) + continue; + if (open_detected) { + ped_device_close (geom->dev); + return NULL; + } else { + open_detected = detected [i]; + } + } + + ped_device_close (geom->dev); + return open_detected; +} + +static int +_geometry_error (const PedGeometry* a, const PedGeometry* b) +{ + PedSector start_delta = a->start - b->start; + PedSector end_delta = a->end - b->end; + + return abs (start_delta) + abs (end_delta); +} + +static PedFileSystemType* +_best_match (const PedGeometry* geom, PedFileSystemType* detected [], + const int detected_error [], int detected_count) +{ + int best_match = 0; + int i; + PedSector min_error; + + min_error = PED_MAX (4096, geom->length / 100); + + for (i = 1; i < detected_count; i++) { + if (detected_error [i] < detected_error [best_match]) + best_match = i; + } + + /* make sure the best match is significantly better than all the + * other matches + */ + for (i = 0; i < detected_count; i++) { + if (i == best_match) + continue; + + if (abs (detected_error [best_match] - detected_error [i]) + < min_error) + return NULL; + } + + return detected [best_match]; +} + + +/** + * Attempt to detect a file system in region \p geom. + * This function tries to be clever at dealing with ambiguous + * situations, such as when one file system was not completely erased before a + * new file system was created on top of it. + * + * \return a new PedFileSystem on success, \c NULL on failure + */ +PedFileSystemType* +ped_file_system_probe (PedGeometry* geom) +{ + PedFileSystemType* detected[32]; + int detected_error[32]; + int detected_count = 0; + PedFileSystemType* walk = NULL; + + PED_ASSERT (geom != NULL, return NULL); + + if (!ped_device_open (geom->dev)) + return NULL; + + ped_exception_fetch_all (); + while ( (walk = ped_file_system_type_get_next (walk)) ) { + PedGeometry* probed; + + probed = ped_file_system_probe_specific (walk, geom); + if (probed) { + detected [detected_count] = walk; + detected_error [detected_count] + = _geometry_error (geom, probed); + detected_count++; + ped_geometry_destroy (probed); + } else { + ped_exception_catch (); + } + } + ped_exception_leave_all (); + + ped_device_close (geom->dev); + + if (!detected_count) + return NULL; + walk = _best_match (geom, detected, detected_error, detected_count); + if (walk) + return walk; + return _probe_with_open (geom, detected_count, detected); +} + +/** + * This function erases all file system signatures that indicate that a + * file system occupies a given region described by \p geom. + * After this operation ped_file_system_probe() won't detect any file system. + * + * \note ped_file_system_create() calls this before creating a new file system. + * + * \return \c 1 on success, \c 0 on failure + */ +int +ped_file_system_clobber (PedGeometry* geom) +{ + PedFileSystemType* fs_type = NULL; + + PED_ASSERT (geom != NULL, return 0); + + if (!ped_device_open (geom->dev)) + goto error; + + ped_exception_fetch_all (); + while ((fs_type = ped_file_system_type_get_next (fs_type))) { + PedGeometry* probed; + + if (!fs_type->ops->clobber) + continue; + + probed = ped_file_system_probe_specific (fs_type, geom); + if (!probed) { + ped_exception_catch (); + continue; + } + ped_geometry_destroy (probed); + + if (fs_type->ops->clobber && !fs_type->ops->clobber (geom)) { + ped_exception_leave_all (); + goto error_close_dev; + } + } + ped_device_close (geom->dev); + ped_exception_leave_all (); + return 1; + +error_close_dev: + ped_device_close (geom->dev); +error: + return 0; +} + +/* This function erases all signatures that indicate the presence of + * a file system in a particular region, without erasing any data + * contained inside the "exclude" region. + */ +static int +ped_file_system_clobber_exclude (PedGeometry* geom, + const PedGeometry* exclude) +{ + PedGeometry* clobber_geom; + int status; + + if (ped_geometry_test_sector_inside (exclude, geom->start)) + return 1; + + clobber_geom = ped_geometry_duplicate (geom); + if (ped_geometry_test_overlap (clobber_geom, exclude)) + ped_geometry_set_end (clobber_geom, exclude->start - 1); + + status = ped_file_system_clobber (clobber_geom); + ped_geometry_destroy (clobber_geom); + return status; +} + +/** + * This function opens the file system stored on \p geom, if it + * can find one. + * It is often called in the following manner: + * \code + * fs = ped_file_system_open (&part.geom) + * \endcode + * + * \throws PED_EXCEPTION_ERROR if file system could not be detected + * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume + * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on + * \p geom is not implemented + * + * \return a PedFileSystem on success, \c NULL on failure. + */ +PedFileSystem* +ped_file_system_open (PedGeometry* geom) +{ + PedFileSystemType* type; + PedFileSystem* fs; + PedGeometry* probed_geom; + + PED_ASSERT (geom != NULL, return NULL); + + if (!ped_device_open (geom->dev)) + goto error; + + type = ped_file_system_probe (geom); + if (!type) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Could not detect file system.")); + goto error_close_dev; + } + + probed_geom = ped_file_system_probe_specific (type, geom); + if (!probed_geom) + goto error_close_dev; + if (!ped_geometry_test_inside (geom, probed_geom)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The file system is bigger than its volume!")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_probed_geom; + } + + if (!type->ops->open) { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for opening %s file systems " + "is not implemented yet."), + type->name); + goto error_destroy_probed_geom; + } + + fs = type->ops->open (probed_geom); + if (!fs) + goto error_destroy_probed_geom; + ped_geometry_destroy (probed_geom); + return fs; + +error_destroy_probed_geom: + ped_geometry_destroy (probed_geom); +error_close_dev: + ped_device_close (geom->dev); +error: + return 0; +} + +/** + * This function initializes a new file system of type \p type on + * a region described by \p geom, writing out appropriate metadata and + * signatures. If \p timer is non-NULL, it is used as the progress meter. + * + * \throws PED_EXCEPTION_NO_FEATURE if creating file system type \p type + * is not implemented yet + * + * \return a PedFileSystem on success, \c NULL on failure + */ +PedFileSystem* +ped_file_system_create (PedGeometry* geom, const PedFileSystemType* type, + PedTimer* timer) +{ + PedFileSystem* fs; + + PED_ASSERT (geom != NULL, return NULL); + PED_ASSERT (type != NULL, return NULL); + + if (!type->ops->create) { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for creating %s file systems " + "is not implemented yet."), + type->name); + goto error; + } + + if (!ped_device_open (geom->dev)) + goto error; + + if (!ped_file_system_clobber (geom)) + goto error_close_dev; + fs = type->ops->create (geom, timer); + if (!fs) + goto error_close_dev; + return fs; + +error_close_dev: + ped_device_close (geom->dev); +error: + return 0; +} + +/** + * Close file system \p fs. + * + * \return \c 1 on success, \c 0 on failure + */ +int +ped_file_system_close (PedFileSystem* fs) +{ + PedDevice* dev = fs->geom->dev; + + PED_ASSERT (fs != NULL, goto error_close_dev); + + if (!fs->type->ops->close (fs)) + goto error_close_dev; + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +/** + * Check \p fs file system for errors. + * + * \throws PED_EXCEPTION_NO_FEATURE if checking file system \p fs is + * not implemented yet + * + * \return \c 0 on failure (i.e. unfixed errors) + */ +int +ped_file_system_check (PedFileSystem* fs, PedTimer* timer) +{ + PED_ASSERT (fs != NULL, return 0); + + if (!fs->type->ops->check) { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for checking %s file systems " + "is not implemented yet."), + fs->type->name); + return 0; + } + return fs->type->ops->check (fs, timer); +} + +static int +_raw_copy (const PedGeometry* src, PedGeometry* dest, PedTimer* timer) +{ + char* buf; + PedSector pos; + + PED_ASSERT (src != NULL, goto error); + PED_ASSERT (dest != NULL, goto error); + PED_ASSERT (src->length <= dest->length, goto error); + + buf = ped_malloc (BUFFER_SIZE * 512); /* FIXME */ + if (!buf) + goto error; + + if (!ped_device_open (src->dev)) + goto error_free_buf; + if (!ped_device_open (dest->dev)) + goto error_close_src; + + for (pos = 0; pos + BUFFER_SIZE < src->length; pos += BUFFER_SIZE) { + ped_timer_update (timer, 1.0 * pos / src->length); + if (!ped_geometry_read (src, buf, pos, BUFFER_SIZE)) + goto error_close_dest; + if (!ped_geometry_write (dest, buf, pos, BUFFER_SIZE)) + goto error_close_dest; + } + if (pos < src->length) { + ped_timer_update (timer, 1.0 * pos / src->length); + if (!ped_geometry_read (src, buf, pos, src->length - pos)) + goto error_close_dest; + if (!ped_geometry_write (dest, buf, pos, src->length - pos)) + goto error_close_dest; + } + ped_timer_update (timer, 1.0); + + ped_device_close (src->dev); + ped_device_close (dest->dev); + ped_free (buf); + return 1; + +error_close_dest: + ped_device_close (dest->dev); +error_close_src: + ped_device_close (src->dev); +error_free_buf: + ped_free (buf); +error: + return 0; +} + +static PedFileSystem* +_raw_copy_and_resize (const PedFileSystem* fs, PedGeometry* geom, + PedTimer* timer) +{ + PedFileSystem* new_fs; + PedTimer* sub_timer = NULL; + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("raw block copying")); + + sub_timer = ped_timer_new_nested (timer, 0.95); + if (!_raw_copy (fs->geom, geom, sub_timer)) + goto error; + ped_timer_destroy_nested (sub_timer); + + new_fs = ped_file_system_open (geom); + if (!new_fs) + goto error; + + ped_timer_set_state_name (timer, _("growing file system")); + + sub_timer = ped_timer_new_nested (timer, 0.05); + if (!ped_file_system_resize (new_fs, geom, sub_timer)) + goto error_close_new_fs; + ped_timer_destroy_nested (sub_timer); + return new_fs; + +error_close_new_fs: + ped_file_system_close (new_fs); +error: + ped_timer_destroy_nested (sub_timer); + return NULL; +} + +/** + * Create a new file system (of the same type) on \p geom, and + * copy the contents of \p fs into the new filesystem. + * If \p timer is non-NULL, it is used as the progress meter. + * + * \throws PED_EXCEPTION_ERROR when trying to copy onto an overlapping partition + * \throws PED_EXCEPTION_NO_FEATURE if copying of file system \p fs + * is not implemented yet + * + * \return a new PedFileSystem on success, \c NULL on failure + */ +PedFileSystem* +ped_file_system_copy (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + PedFileSystem* new_fs; + + PED_ASSERT (fs != NULL, return 0); + PED_ASSERT (geom != NULL, return 0); + + if (!ped_device_open (geom->dev)) + goto error; + + if (ped_geometry_test_overlap (fs->geom, geom)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Can't copy onto an overlapping partition.")); + goto error_close_dev; + } + + if (!fs->checked && fs->type->ops->check) { + if (!ped_file_system_check (fs, timer)) + goto error_close_dev; + } + + if (!ped_file_system_clobber_exclude (geom, fs->geom)) + goto error_close_dev; + + if (!fs->type->ops->copy) { + if (fs->type->ops->resize) { + if (fs->geom->length <= geom->length) + return _raw_copy_and_resize ( + fs, (PedGeometry*) geom, + timer); + + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Direct support for copying file systems is " + "not yet implemented for %s. However, " + "support for resizing is implemented. " + "Therefore, the file system can be copied if " + "the new partition is at least as big as the " + "old one. So, either shrink the partition " + "you are trying to copy, or copy to a bigger " + "partition."), + fs->type->name); + goto error_close_dev; + } else { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for copying %s file systems is not " + "implemented yet."), + fs->type->name); + goto error_close_dev; + } + } + new_fs = fs->type->ops->copy (fs, geom, timer); + if (!new_fs) + goto error_close_dev; + return new_fs; + +error_close_dev: + ped_device_close (geom->dev); +error: + return NULL;; +} + +/** + * Resize \p fs to new geometry \p geom. + * + * \p geom should satisfy the ped_file_system_get_resize_constraint(). + * (This isn't asserted, so it's not a bug not to... just it's likely + * to fail ;) If \p timer is non-NULL, it is used as the progress meter. + * + * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs + * is not implemented yet + * + * \return \c 0 on failure + */ +int +ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + PED_ASSERT (fs != NULL, return 0); + PED_ASSERT (geom != NULL, return 0); + + if (!fs->type->ops->resize) { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for resizing %s file systems " + "is not implemented yet."), + fs->type->name); + return 0; + } + if (!fs->checked && fs->type->ops->check) { + if (!ped_file_system_check (fs, timer)) + return 0; + } + if (!ped_file_system_clobber_exclude (geom, fs->geom)) + return 0; + + return fs->type->ops->resize (fs, geom, timer); +} + +/** + * This function returns a constraint on the region that all file systems + * of a particular type \p fs_type created on device \p dev with + * ped_file_system_create() must satisfy. For example, FAT16 file systems must + * be at least 32 megabytes. + * + * \return \c NULL on failure + */ +PedConstraint* +ped_file_system_get_create_constraint (const PedFileSystemType* fs_type, + const PedDevice* dev) +{ + PED_ASSERT (fs_type != NULL, return NULL); + PED_ASSERT (dev != NULL, return NULL); + + if (!fs_type->ops->get_create_constraint) + return NULL; + return fs_type->ops->get_create_constraint (dev); +} +/** + * Return a constraint, that represents all of the possible ways the + * file system \p fs can be resized with ped_file_system_resize(). + * This takes into account the amount of used space on + * the filesystem \p fs and the capabilities of the resize algorithm. + * Hints: + * -# if constraint->start_align->grain_size == 0, or + * constraint->start_geom->length == 1, then the start can not be moved + * -# constraint->min_size is the minimum size you can resize the partition + * to. You might want to tell the user this ;-). + * + * \return a PedConstraint on success, \c NULL on failure + */ +PedConstraint* +ped_file_system_get_resize_constraint (const PedFileSystem* fs) +{ + PED_ASSERT (fs != NULL, return 0); + + if (!fs->type->ops->get_resize_constraint) + return NULL; + return fs->type->ops->get_resize_constraint (fs); +} + +/** + * Get the constraint on copying \p fs with ped_file_system_copy() + * to somewhere on \p dev. + * + * \return a PedConstraint on success, \c NULL on failure + */ +PedConstraint* +ped_file_system_get_copy_constraint (const PedFileSystem* fs, + const PedDevice* dev) +{ + PedGeometry full_dev; + + PED_ASSERT (fs != NULL, return NULL); + PED_ASSERT (dev != NULL, return NULL); + + if (fs->type->ops->get_copy_constraint) + return fs->type->ops->get_copy_constraint (fs, dev); + + if (fs->type->ops->resize) { + 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, + fs->geom->length, dev->length); + } + + return NULL; +} + +/** @} */ diff --git a/libparted/fs/Makefile.am b/libparted/fs/Makefile.am new file mode 100644 index 0000000..8f5d69d --- /dev/null +++ b/libparted/fs/Makefile.am @@ -0,0 +1,33 @@ +# This file is part of GNU Parted +# Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +# +# This file may be modified and/or distributed without restriction. + +SUBDIRS = amiga ext2 ufs fat ntfs hfs linux_swap xfs jfs reiserfs # bfs + +LIBS = @INTLLIBS@ @LIBS@ + +partedincludedir = -I$(top_srcdir)/include +noinst_LTLIBRARIES = libfs.la +libfs_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -release $(LT_RELEASE) + +libfs_la_LIBADD = @UUID_LIBS@ \ + @OS_LIBS@ \ + @REISER_LIBS@ \ + amiga/libamigafs.la \ + ext2/libext2.la \ + ufs/libufs.la \ + fat/libfat.la \ + ntfs/libntfs.la \ + hfs/libhfs.la \ + linux_swap/liblinuxswap.la \ + xfs/libxfs.la \ + jfs/libjfs.la \ + reiserfs/libreiserfs.la +# bfs/libbfs.la + +libfs_la_SOURCES = + +INCLUDES = $(partedincludedir) @INTLINCS@ + diff --git a/libparted/fs/amiga/Makefile.am b/libparted/fs/amiga/Makefile.am new file mode 100644 index 0000000..9d69be1 --- /dev/null +++ b/libparted/fs/amiga/Makefile.am @@ -0,0 +1,14 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libamigafs.la +libamigafs_la_SOURCES = amiga.h \ + amiga.c \ + affs.h \ + affs.c \ + asfs.h \ + asfs.c \ + apfs.h \ + apfs.c \ + interface.c + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/amiga/affs.c b/libparted/fs/amiga/affs.c new file mode 100644 index 0000000..77629e2 --- /dev/null +++ b/libparted/fs/amiga/affs.c @@ -0,0 +1,466 @@ +/* + affs.c -- parted support for affs file systems + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "amiga.h" +#include "affs.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static int +_affs_probe_root (uint32_t *block, int blocksize) { + int i; + uint32_t sum; + + if (PED_BE32_TO_CPU (block[0]) != 2) return 0; + if (PED_BE32_TO_CPU (block[128*blocksize-1]) != 1) return 0; + for (i = 0, sum = 0; i < 128*blocksize; i++) + sum += PED_BE32_TO_CPU (block[i]); + if (sum) return 0; + return 1; +} + +static PedGeometry* +_generic_affs_probe (PedGeometry* geom, uint32_t kind) +{ + uint32_t *block; + int32_t i; + PedSector root, len, pos; + struct PartitionBlock * part; + int blocksize = 1, reserved = 2, prealloc = 0; + + PED_ASSERT (geom != NULL, return NULL); + PED_ASSERT (geom->dev != NULL, return NULL); + + /* Finds the blocksize, prealloc and reserved values of the partition block */ + if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate partition block\n"), __func__); + goto error_part; + } + if (amiga_find_part(geom, part) != NULL) { + prealloc = PED_BE32_TO_CPU (part->de_PreAlloc); + reserved = PED_BE32_TO_CPU (part->de_Reserved); + reserved = reserved == 0 ? 1 : reserved; + blocksize = PED_BE32_TO_CPU (part->de_SizeBlock) + * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128; + } + ped_free (part); + + /* Test boot block */ + if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate block\n"), __func__); + goto error_block; + } + if (!ped_device_read (geom->dev, block, geom->start, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read boot block %llu\n"), __func__, geom->start); + goto error; + } + if (PED_BE32_TO_CPU (block[0]) != kind) { + goto error; + } + + /* Find and test the root block */ + len = geom->length / blocksize - reserved; + pos = (len - 1) / 2; + root = geom->start + (pos + reserved) * blocksize; + printf ("Pralloc = %d, Reserved = %d, blocksize = %d, root block at %llu\n", + prealloc, reserved, blocksize, root); + + if (!ped_device_read (geom->dev, block, root, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read root block %llu\n"), __func__, root); + goto error; + } + if (_affs_probe_root(block, blocksize) == 1) { + ped_free (block); + return ped_geometry_duplicate (geom); + } + +error: + ped_free (block); +error_block: +error_part: + return NULL; +} +static PedGeometry* +_affs0_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5300); +} +static PedGeometry* +_affs1_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5301); +} +static PedGeometry* +_affs2_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5302); +} +static PedGeometry* +_affs3_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5303); +} +static PedGeometry* +_affs4_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5304); +} +static PedGeometry* +_affs5_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5305); +} +static PedGeometry* +_affs6_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5306); +} +static PedGeometry* +_affs7_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5307); +} +static PedGeometry* +_amufs_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754653); +} +static PedGeometry* +_amufs0_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754600); +} +static PedGeometry* +_amufs1_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754601); +} +static PedGeometry* +_amufs2_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754602); +} +static PedGeometry* +_amufs3_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754603); +} +static PedGeometry* +_amufs4_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754604); +} +static PedGeometry* +_amufs5_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754605); +} + +static PedFileSystemOps _affs0_ops = { + probe: _affs0_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _affs1_ops = { + probe: _affs1_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _affs2_ops = { + probe: _affs2_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _affs3_ops = { + probe: _affs3_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _affs4_ops = { + probe: _affs4_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _affs5_ops = { + probe: _affs5_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _affs6_ops = { + probe: _affs6_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _affs7_ops = { + probe: _affs7_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _amufs_ops = { + probe: _amufs_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _amufs0_ops = { + probe: _amufs0_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _amufs1_ops = { + probe: _amufs1_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _amufs2_ops = { + probe: _amufs2_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _amufs3_ops = { + probe: _amufs3_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _amufs4_ops = { + probe: _amufs4_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _amufs5_ops = { + probe: _amufs5_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; + +#define AFFS_BLOCK_SIZES ((int[5]){512, 1024, 2048, 4096, 0}) +#define AMUFS_BLOCK_SIZES ((int[2]){512, 0}) + + +PedFileSystemType _affs0_type = { + next: NULL, + ops: &_affs0_ops, + name: "affs0", + block_sizes: AFFS_BLOCK_SIZES +}; +PedFileSystemType _affs1_type = { + next: NULL, + ops: &_affs1_ops, + name: "affs1", + block_sizes: AFFS_BLOCK_SIZES +}; +PedFileSystemType _affs2_type = { + next: NULL, + ops: &_affs2_ops, + name: "affs2", + block_sizes: AFFS_BLOCK_SIZES +}; +PedFileSystemType _affs3_type = { + next: NULL, + ops: &_affs3_ops, + name: "affs3", + block_sizes: AFFS_BLOCK_SIZES +}; +PedFileSystemType _affs4_type = { + next: NULL, + ops: &_affs4_ops, + name: "affs4", + block_sizes: AFFS_BLOCK_SIZES +}; +PedFileSystemType _affs5_type = { + next: NULL, + ops: &_affs5_ops, + name: "affs5", + block_sizes: AFFS_BLOCK_SIZES +}; +PedFileSystemType _affs6_type = { + next: NULL, + ops: &_affs6_ops, + name: "affs6", + block_sizes: AFFS_BLOCK_SIZES +}; +PedFileSystemType _affs7_type = { + next: NULL, + ops: &_affs7_ops, + name: "affs7", + block_sizes: AFFS_BLOCK_SIZES +}; +PedFileSystemType _amufs_type = { + next: NULL, + ops: &_amufs_ops, + name: "amufs", + block_sizes: AMUFS_BLOCK_SIZES +}; +PedFileSystemType _amufs0_type = { + next: NULL, + ops: &_amufs0_ops, + name: "amufs0", + block_sizes: AMUFS_BLOCK_SIZES +}; +PedFileSystemType _amufs1_type = { + next: NULL, + ops: &_amufs1_ops, + name: "amufs1", + block_sizes: AMUFS_BLOCK_SIZES +}; +PedFileSystemType _amufs2_type = { + next: NULL, + ops: &_amufs2_ops, + name: "amufs2", + block_sizes: AMUFS_BLOCK_SIZES +}; +PedFileSystemType _amufs3_type = { + next: NULL, + ops: &_amufs3_ops, + name: "amufs3", + block_sizes: AMUFS_BLOCK_SIZES +}; +PedFileSystemType _amufs4_type = { + next: NULL, + ops: &_amufs4_ops, + name: "amufs4", + block_sizes: AMUFS_BLOCK_SIZES +}; +PedFileSystemType _amufs5_type = { + next: NULL, + ops: &_amufs5_ops, + name: "amufs5", + block_sizes: AMUFS_BLOCK_SIZES +}; diff --git a/libparted/fs/amiga/affs.h b/libparted/fs/amiga/affs.h new file mode 100644 index 0000000..ac12de6 --- /dev/null +++ b/libparted/fs/amiga/affs.h @@ -0,0 +1,20 @@ + +/* + affs.h -- parted suppoer for affs filesystems header files + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + diff --git a/libparted/fs/amiga/amiga.c b/libparted/fs/amiga/amiga.c new file mode 100644 index 0000000..bf6ff6b --- /dev/null +++ b/libparted/fs/amiga/amiga.c @@ -0,0 +1,382 @@ +/* + libparted/fs_amiga - amiga file system support. + Copyright (C) 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + Contributor: Sven Luther <luther@debian.org> +*/ + +#include <string.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "amiga.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* String manipulation */ +static void _amiga_set_bstr (const char *cstr, char *bstr, int maxsize) { + int size = strlen (cstr); + int i; + + if (size >= maxsize) return; + bstr[0] = size; + for (i = 0; i<size; i++) bstr[i+1] = cstr[i]; +} +static const char * _amiga_get_bstr (char * bstr) { + char * cstr = bstr + 1; + int size = bstr[0]; + + cstr[size] = '\0'; + return cstr; +} + +#define IDNAME_RIGIDDISK (uint32_t)0x5244534B /* 'RDSK' */ +#define IDNAME_BADBLOCK (uint32_t)0x42414442 /* 'BADB' */ +#define IDNAME_PARTITION (uint32_t)0x50415254 /* 'PART' */ +#define IDNAME_FILESYSHEADER (uint32_t)0x46534844 /* 'FSHD' */ +#define IDNAME_LOADSEG (uint32_t)0x4C534547 /* 'LSEG' */ +#define IDNAME_BOOT (uint32_t)0x424f4f54 /* 'BOOT' */ +#define IDNAME_FREE (uint32_t)0xffffffff + +static const char * +_amiga_block_id (uint32_t id) { + switch (id) { + case IDNAME_RIGIDDISK : + return "RDSK"; + case IDNAME_BADBLOCK : + return "BADB"; + case IDNAME_PARTITION : + return "PART"; + case IDNAME_FILESYSHEADER : + return "FSHD"; + case IDNAME_LOADSEG : + return "LSEG"; + case IDNAME_BOOT : + return "BOOT"; + case IDNAME_FREE : + return "<free>"; + default : + return "<unknown>"; + } +} +static int +_amiga_valid_block_id (uint32_t id) { + switch (id) { + case IDNAME_RIGIDDISK : + case IDNAME_BADBLOCK : + case IDNAME_PARTITION : + case IDNAME_FILESYSHEADER : + case IDNAME_LOADSEG : + case IDNAME_BOOT : + return 1; + case IDNAME_FREE : + default : + return 0; + } +} + +struct AmigaIds * +_amiga_add_id (uint32_t id, struct AmigaIds *ids) { + struct AmigaIds *newid; + + if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate id list element\n"), __func__); + return 0; + } + newid->ID = id; + newid->next = ids; + return newid; +} + +void +_amiga_free_ids (struct AmigaIds *ids) { + struct AmigaIds *current, *next; + + for (current = ids; current != NULL; current = next) { + next = current->next; + ped_free (current); + } +} +int +_amiga_id_in_list (uint32_t id, struct AmigaIds *ids) { + struct AmigaIds *current; + + for (current = ids; current != NULL; current = current->next) { + if (id == current->ID) + return 1; + } + return 0; +} + +#define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff) + +struct AmigaBlock { + uint32_t amiga_ID; /* Identifier 32 bit word */ + uint32_t amiga_SummedLongss; /* Size of the structure for checksums */ + int32_t amiga_ChkSum; /* Checksum of the structure */ +}; +#define AMIGA(pos) ((struct AmigaBlock *)(pos)) + +struct RigidDiskBlock { + uint32_t rdb_ID; /* Identifier 32 bit word : 'RDSK' */ + uint32_t rdb_SummedLongs; /* Size of the structure for checksums */ + int32_t rdb_ChkSum; /* Checksum of the structure */ + uint32_t rdb_HostID; /* SCSI Target ID of host, not really used */ + uint32_t rdb_BlockBytes; /* Size of disk blocks */ + uint32_t rdb_Flags; /* RDB Flags */ + /* block list heads */ + uint32_t rdb_BadBlockList; /* Bad block list */ + uint32_t rdb_PartitionList; /* Partition list */ + uint32_t rdb_FileSysHeaderList; /* File system header list */ + uint32_t rdb_DriveInit; /* Drive specific init code */ + uint32_t rdb_BootBlockList; /* Amiga OS 4 Boot Blocks */ + uint32_t rdb_Reserved1[5]; /* Unused word, need to be set to $ffffffff */ + /* physical drive characteristics */ + uint32_t rdb_Cylinders; /* Number of the cylinders of the drive */ + uint32_t rdb_Sectors; /* Number of sectors of the drive */ + uint32_t rdb_Heads; /* Number of heads of the drive */ + uint32_t rdb_Interleave; /* Interleave */ + uint32_t rdb_Park; /* Head parking cylinder */ + uint32_t rdb_Reserved2[3]; /* Unused word, need to be set to $ffffffff */ + uint32_t rdb_WritePreComp; /* Starting cylinder of write precompensation */ + uint32_t rdb_ReducedWrite; /* Starting cylinder of reduced write current */ + uint32_t rdb_StepRate; /* Step rate of the drive */ + uint32_t rdb_Reserved3[5]; /* Unused word, need to be set to $ffffffff */ + /* logical drive characteristics */ + uint32_t rdb_RDBBlocksLo; /* low block of range reserved for hardblocks */ + uint32_t rdb_RDBBlocksHi; /* high block of range for these hardblocks */ + uint32_t rdb_LoCylinder; /* low cylinder of partitionable disk area */ + uint32_t rdb_HiCylinder; /* high cylinder of partitionable data area */ + uint32_t rdb_CylBlocks; /* number of blocks available per cylinder */ + uint32_t rdb_AutoParkSeconds; /* zero for no auto park */ + uint32_t rdb_HighRDSKBlock; /* highest block used by RDSK */ + /* (not including replacement bad blocks) */ + uint32_t rdb_Reserved4; + /* drive identification */ + char rdb_DiskVendor[8]; + char rdb_DiskProduct[16]; + char rdb_DiskRevision[4]; + char rdb_ControllerVendor[8]; + char rdb_ControllerProduct[16]; + char rdb_ControllerRevision[4]; + uint32_t rdb_Reserved5[10]; +}; + +#define AMIGA_MAX_PARTITIONS 128 +#define RDB_LOCATION_LIMIT 16 +#define RDSK(pos) ((struct RigidDiskBlock *)(pos)) + +static int +_amiga_checksum (struct AmigaBlock *blk) { + uint32_t *rdb = (uint32_t *) blk; + uint32_t sum; + int i, end; + + sum = PED_BE32_TO_CPU (rdb[0]); + end = PED_BE32_TO_CPU (rdb[1]); + + if (end > PED_SECTOR_SIZE_DEFAULT) end = PED_SECTOR_SIZE_DEFAULT; + + for (i = 1; i < end; i++) sum += PED_BE32_TO_CPU (rdb[i]); + + return sum; +} + +static void +_amiga_calculate_checksum (struct AmigaBlock *blk) { + + blk->amiga_ChkSum = PED_CPU_TO_BE32( + PED_BE32_TO_CPU(blk->amiga_ChkSum) - + _amiga_checksum((struct AmigaBlock *) blk)); + return; +} + + +static struct AmigaBlock * +_amiga_read_block (PedDevice *dev, struct AmigaBlock *blk, PedSector block, struct AmigaIds *ids) { + if (!ped_device_read (dev, blk, block, 1)) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Couldn't read block %llu\n"), __func__, block)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + return NULL; + } + } + if (ids && !_amiga_id_in_list(PED_BE32_TO_CPU(blk->amiga_ID), ids)) + return NULL; + if (_amiga_checksum (blk) != 0) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL, + _("%s : Bad checksum on block %llu of type %s\n"), + __func__, block, _amiga_block_id(PED_BE32_TO_CPU(blk->amiga_ID)))) + { + case PED_EXCEPTION_CANCEL : + return NULL; + case PED_EXCEPTION_FIX : + _amiga_calculate_checksum(AMIGA(blk)); + if (!ped_device_write (dev, blk, block, 1)) { + switch (ped_exception_throw(PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("%s : Couldn't write block %d\n"), __func__, block)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + return NULL; + } + } + case PED_EXCEPTION_IGNORE : + case PED_EXCEPTION_UNHANDLED : + default : + return blk; + } + } + return blk; +} + +static uint32_t +_amiga_find_rdb (PedDevice *dev, struct RigidDiskBlock *rdb) { + int i; + struct AmigaIds *ids; + + ids = _amiga_add_id (IDNAME_RIGIDDISK, NULL); + + for (i = 0; i<RDB_LOCATION_LIMIT; i++) { + if (!_amiga_read_block (dev, AMIGA(rdb), i, ids)) { + continue; + } + if (PED_BE32_TO_CPU (rdb->rdb_ID) == IDNAME_RIGIDDISK) { + _amiga_free_ids (ids); + return i; + } + } + _amiga_free_ids (ids); + return AMIGA_RDB_NOT_FOUND; +} + +static int +_amiga_loop_check (uint32_t block, uint32_t * blocklist, uint32_t max) +{ + uint32_t i; + + for (i = 0; i < max; i++) + if (block == blocklist[i]) { + /* We are looping, let's stop. */ + return 1; + } + blocklist[max] = block; + return 0; +} + +/* We have already allocated a rdb, we are now reading it from the disk */ +struct PartitionBlock * +amiga_find_part (PedGeometry *geom, struct PartitionBlock *part) +{ + struct RigidDiskBlock *rdb; + uint32_t partblock; + uint32_t partlist[AMIGA_MAX_PARTITIONS]; + int i; + + PED_ASSERT(geom!= NULL, return NULL); + PED_ASSERT(geom->dev!= NULL, return NULL); + + if (!(rdb = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate disk_specific rdb block\n"), __func__)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + return NULL; + } + } + if (_amiga_find_rdb (geom->dev, rdb) == AMIGA_RDB_NOT_FOUND) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Didn't find rdb block, should never happen\n"), __func__)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + ped_free(rdb); + return NULL; + } + } + + /* We initialize the hardblock free list to detect loops */ + for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = IDNAME_FREE; + + for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList); + i < AMIGA_MAX_PARTITIONS && partblock != IDNAME_FREE; + i++, partblock = PED_BE32_TO_CPU(part->pb_Next)) + { + PedSector start, end; + PedSector cylblocks; + + /* Let's look for loops in the partition table */ + if (_amiga_loop_check(partblock, partlist, i)) { + ped_free (rdb); + return NULL; + } + /* Let's read a partition block to get its geometry*/ + if (!ped_device_read (geom->dev, part, (PedSector)partblock, 1)) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to read partition block %llu\n"), + __func__, (PedSector)partblock)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + ped_free(rdb); + return NULL; + } + } + + /* Current block is not a Partition Block */ + if (part->pb_ID != IDNAME_PARTITION) { + ped_free (rdb); + return NULL; + } + + /* Calculate the geometry of the partition */ + cylblocks = ((PedSector) PED_BE32_TO_CPU (part->de_Surfaces)) * + ((PedSector) PED_BE32_TO_CPU (part->de_BlocksPerTrack)); + start = ((PedSector) PED_BE32_TO_CPU (part->de_LowCyl)) * cylblocks; + end = ((((PedSector) PED_BE32_TO_CPU (part->de_HighCyl))+1) * (cylblocks))-1; + + /* And check if it is the one we are searching for */ + if (start == geom->start && end == geom->end) { + ped_free (rdb); + return part; + } + } + + ped_free (rdb); + return NULL; +} diff --git a/libparted/fs/amiga/amiga.h b/libparted/fs/amiga/amiga.h new file mode 100644 index 0000000..a7bc1f8 --- /dev/null +++ b/libparted/fs/amiga/amiga.h @@ -0,0 +1,71 @@ +/* + util.h -- amiga partition table headers. + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +struct PartitionBlock { + uint32_t pb_ID; /* Identifier 32 bit word : 'PART' */ + uint32_t pb_SummedLongs; /* Size of the structure for checksums */ + int32_t pb_ChkSum; /* Checksum of the structure */ + uint32_t pb_HostID; /* SCSI Target ID of host, not really used */ + uint32_t pb_Next; /* Block number of the next PartitionBlock */ + uint32_t pb_Flags; /* Part Flags (NOMOUNT and BOOTABLE) */ + uint32_t pb_Reserved1[2]; + uint32_t pb_DevFlags; /* Preferred flags for OpenDevice */ + uint8_t pb_DriveName[32]; /* Preferred DOS device name: BSTR form */ + uint32_t pb_Reserved2[15]; + uint32_t de_TableSize; /* Size of Environment vector */ + uint32_t de_SizeBlock; /* Size of the blocks in 32 bit words, usually 128 */ + uint32_t de_SecOrg; /* Not used; must be 0 */ + uint32_t de_Surfaces; /* Number of heads (surfaces) */ + uint32_t de_SectorPerBlock; /* Disk sectors per block, used with SizeBlock, usually 1 */ + uint32_t de_BlocksPerTrack; /* Blocks per track. drive specific */ + uint32_t de_Reserved; /* DOS reserved blocks at start of partition. */ + uint32_t de_PreAlloc; /* DOS reserved blocks at end of partition */ + uint32_t de_Interleave; /* Not used, usually 0 */ + uint32_t de_LowCyl; /* First cylinder of the partition */ + uint32_t de_HighCyl; /* Last cylinder of the partition */ + uint32_t de_NumBuffers; /* Initial # DOS of buffers. */ + uint32_t de_BufMemType; /* Type of mem to allocate for buffers */ + uint32_t de_MaxTransfer; /* Max number of bytes to transfer at a time */ + uint32_t de_Mask; /* Address Mask to block out certain memory */ + int32_t de_BootPri; /* Boot priority for autoboot */ + uint32_t de_DosType; /* Dostype of the file system */ + uint32_t de_Baud; /* Baud rate for serial handler */ + uint32_t de_Control; /* Control word for handler/filesystem */ + uint32_t de_BootBlocks; /* Number of blocks containing boot code */ + uint32_t pb_EReserved[12]; +}; + +#define PART(pos) ((struct PartitionBlock *)(pos)) + +#define PBFB_BOOTABLE 0 /* this partition is intended to be bootable */ +#define PBFF_BOOTABLE 1L /* (expected directories and files exist) */ +#define PBFB_NOMOUNT 1 /* do not mount this partition (e.g. manually */ +#define PBFF_NOMOUNT 2L /* mounted, but space reserved here) */ + +struct PartitionBlock * amiga_find_part (PedGeometry *geom, struct PartitionBlock *part); + +struct AmigaIds { + uint32_t ID; + struct AmigaIds *next; +}; + +struct AmigaIds * _amiga_add_id (uint32_t id, struct AmigaIds *ids); +void _amiga_free_ids (struct AmigaIds *ids); +int _amiga_id_in_list (uint32_t id, struct AmigaIds *ids); + diff --git a/libparted/fs/amiga/apfs.c b/libparted/fs/amiga/apfs.c new file mode 100644 index 0000000..f92c416 --- /dev/null +++ b/libparted/fs/amiga/apfs.c @@ -0,0 +1,154 @@ +/* + apfs.c -- parted support for apfs file systems + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "amiga.h" +#include "apfs.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static int +_apfs_probe_root (uint32_t *block, uint32_t blocksize, uint32_t kind) { + if (PED_BE32_TO_CPU (block[0]) != kind) return 0; + return 1; +} + +static PedGeometry* +_generic_apfs_probe (PedGeometry* geom, uint32_t kind) +{ + uint32_t *block; + int32_t i, sum; + PedSector root; + struct PartitionBlock * part; + uint32_t blocksize = 1, reserved = 2, prealloc = 0; + + PED_ASSERT (geom != NULL, return NULL); + PED_ASSERT (geom->dev != NULL, return NULL); + + /* Finds the blocksize, prealloc and reserved values of the partition block */ + if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate partition block\n"), __func__); + goto error_part; + } + if (amiga_find_part(geom, part) != NULL) { + prealloc = PED_BE32_TO_CPU (part->de_PreAlloc); + reserved = PED_BE32_TO_CPU (part->de_Reserved); + blocksize = PED_BE32_TO_CPU (part->de_SizeBlock) + * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128; + } + ped_free (part); + + /* Test boot block */ + if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate block\n"), __func__); + goto error_block; + } + if (!ped_device_read (geom->dev, block, geom->start, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read boot block %llu\n"), __func__, geom->start); + goto error; + } + if (PED_BE32_TO_CPU (block[0]) != kind) { + goto error; + } + + /* Find and test the root block */ + root = geom->start+reserved*blocksize; + if (!ped_device_read (geom->dev, block, root, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read root block %llu\n"), __func__, root); + goto error; + } + if (_apfs_probe_root(block, blocksize, kind) == 1) { + ped_free(block); + return ped_geometry_duplicate (geom); + } + +error: + ped_free (block); +error_block: +error_part: + return NULL; +} + +static PedGeometry* +_apfs1_probe (PedGeometry* geom) { + return _generic_apfs_probe (geom, 0x50463101); +} + +static PedGeometry* +_apfs2_probe (PedGeometry* geom) { + return _generic_apfs_probe (geom, 0x50463102); +} + +static PedFileSystemOps _apfs1_ops = { + probe: _apfs1_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; +static PedFileSystemOps _apfs2_ops = { + probe: _apfs2_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; + +#define APFS_BLOCK_SIZES ((int[2]){512, 0}) + +PedFileSystemType _apfs1_type = { + next: NULL, + ops: &_apfs1_ops, + name: "apfs1", + block_sizes: APFS_BLOCK_SIZES +}; +PedFileSystemType _apfs2_type = { + next: NULL, + ops: &_apfs2_ops, + name: "apfs2", + block_sizes: APFS_BLOCK_SIZES +}; diff --git a/libparted/fs/amiga/apfs.h b/libparted/fs/amiga/apfs.h new file mode 100644 index 0000000..543a509 --- /dev/null +++ b/libparted/fs/amiga/apfs.h @@ -0,0 +1,19 @@ + +/* + apfs.h -- parted support for apfs file systems header files + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ diff --git a/libparted/fs/amiga/asfs.c b/libparted/fs/amiga/asfs.c new file mode 100644 index 0000000..eb87559 --- /dev/null +++ b/libparted/fs/amiga/asfs.c @@ -0,0 +1,145 @@ +/* + asfs.c -- parted asfs filesystem support + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "amiga.h" +#include "asfs.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static int +_asfs_probe_root (PedGeometry *geom, uint32_t *block, int blocksize, PedSector root) { + int i, sum; + PedSector start, end; + + if (PED_BE32_TO_CPU (block[0]) != 0x53465300) return 0; + for (i = 0, sum = 1; i < 128*blocksize; i++) sum += PED_BE32_TO_CPU (block[i]); + if (sum != 0) return 0; + if (PED_BE32_TO_CPU (block[2]) * blocksize + geom->start != root) { + return 0; + } + start = ((((PedSector) PED_BE32_TO_CPU (block[8])) << 32) + + (PedSector) PED_BE32_TO_CPU (block[9])) / 512; + end = (((((PedSector) PED_BE32_TO_CPU (block[10])) << 32) + + (PedSector) PED_BE32_TO_CPU (block[11])) / 512) - 1; + if (start != geom->start || end != geom->end) return 0; + return 1; +} + +static PedGeometry* +_asfs_probe (PedGeometry* geom) +{ + uint32_t *block; + struct PartitionBlock * part; + int blocksize = 1, reserved = 1, prealloc = 1; + PedSector root, root2; + int found = 0; + + PED_ASSERT (geom != NULL, return NULL); + PED_ASSERT (geom->dev != NULL, return NULL); + + /* Finds the blocksize, prealloc and reserved values of the partition block */ + if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate partition block\n"), __func__); + goto error_part; + } + if (amiga_find_part(geom, part) != NULL) { + prealloc = PED_BE32_TO_CPU (part->de_PreAlloc) == 0 ? + 1 : PED_BE32_TO_CPU (part->de_PreAlloc); + reserved = PED_BE32_TO_CPU (part->de_Reserved) == 0 ? + 1 : PED_BE32_TO_CPU (part->de_Reserved); + blocksize = PED_BE32_TO_CPU (part->de_SizeBlock) + * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128; + } + ped_free (part); + + /* Test boot block */ + if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate block\n"), __func__); + goto error_block; + } + root = geom->start; + if (!ped_device_read (geom->dev, block, root, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read root block %llu\n"), __func__, root); + goto error; + } + if (PED_BE32_TO_CPU (block[0]) != 0x53465300) { + goto error; + } + + /* Find and test the root blocks */ + if (_asfs_probe_root(geom, block, blocksize, root)) { + found++; + } + root = geom->end - blocksize - (geom->length % blocksize) + 1; + if (!ped_device_read (geom->dev, block, root, 1)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read root block %llu\n"), __func__, root); + goto error; + } + if (_asfs_probe_root(geom, block, blocksize, root)) { + found++; + } + if (found != 0) { + ped_free (block); + return ped_geometry_duplicate (geom); + } + +error: + ped_free (block); +error_block: +error_part: + return NULL; +} + +static PedFileSystemOps _asfs_ops = { + probe: _asfs_probe, + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +}; + +PedFileSystemType _asfs_type = { + next: NULL, + ops: &_asfs_ops, + name: "asfs", + block_sizes: ((int[2]){512, 0}) +}; diff --git a/libparted/fs/amiga/asfs.h b/libparted/fs/amiga/asfs.h new file mode 100644 index 0000000..959fc16 --- /dev/null +++ b/libparted/fs/amiga/asfs.h @@ -0,0 +1,19 @@ + +/* + asfs.h -- parted asfs filesystem support header files + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ diff --git a/libparted/fs/amiga/interface.c b/libparted/fs/amiga/interface.c new file mode 100644 index 0000000..a3c57f8 --- /dev/null +++ b/libparted/fs/amiga/interface.c @@ -0,0 +1,90 @@ +/* + interface.c -- parted support amiga file systems + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +extern PedFileSystemType _affs0_type; +extern PedFileSystemType _affs1_type; +extern PedFileSystemType _affs2_type; +extern PedFileSystemType _affs3_type; +extern PedFileSystemType _affs4_type; +extern PedFileSystemType _affs5_type; +extern PedFileSystemType _affs6_type; +extern PedFileSystemType _affs7_type; +extern PedFileSystemType _amufs_type; +extern PedFileSystemType _amufs0_type; +extern PedFileSystemType _amufs1_type; +extern PedFileSystemType _amufs2_type; +extern PedFileSystemType _amufs3_type; +extern PedFileSystemType _amufs4_type; +extern PedFileSystemType _amufs5_type; +extern PedFileSystemType _asfs_type; +extern PedFileSystemType _apfs1_type; +extern PedFileSystemType _apfs2_type; + +void ped_file_system_amiga_init () +{ + ped_file_system_type_register (&_affs0_type); + ped_file_system_type_register (&_affs1_type); + ped_file_system_type_register (&_affs2_type); + ped_file_system_type_register (&_affs3_type); + ped_file_system_type_register (&_affs4_type); + ped_file_system_type_register (&_affs5_type); + ped_file_system_type_register (&_affs6_type); + ped_file_system_type_register (&_affs7_type); + ped_file_system_type_register (&_amufs_type); + ped_file_system_type_register (&_amufs0_type); + ped_file_system_type_register (&_amufs1_type); + ped_file_system_type_register (&_amufs2_type); + ped_file_system_type_register (&_amufs3_type); + ped_file_system_type_register (&_amufs4_type); + ped_file_system_type_register (&_amufs5_type); + ped_file_system_type_register (&_asfs_type); + ped_file_system_type_register (&_apfs1_type); + ped_file_system_type_register (&_apfs2_type); +} + +void ped_file_system_amiga_done () +{ + ped_file_system_type_unregister (&_affs0_type); + ped_file_system_type_unregister (&_affs1_type); + ped_file_system_type_unregister (&_affs2_type); + ped_file_system_type_unregister (&_affs3_type); + ped_file_system_type_unregister (&_affs4_type); + ped_file_system_type_unregister (&_affs5_type); + ped_file_system_type_unregister (&_affs6_type); + ped_file_system_type_unregister (&_affs7_type); + ped_file_system_type_unregister (&_amufs_type); + ped_file_system_type_unregister (&_amufs0_type); + ped_file_system_type_unregister (&_amufs1_type); + ped_file_system_type_unregister (&_amufs2_type); + ped_file_system_type_unregister (&_amufs3_type); + ped_file_system_type_unregister (&_amufs4_type); + ped_file_system_type_unregister (&_amufs5_type); + ped_file_system_type_unregister (&_asfs_type); + ped_file_system_type_unregister (&_apfs1_type); + ped_file_system_type_unregister (&_apfs2_type); +} diff --git a/libparted/fs/bfs/Makefile.am b/libparted/fs/bfs/Makefile.am new file mode 100644 index 0000000..8bd73f1 --- /dev/null +++ b/libparted/fs/bfs/Makefile.am @@ -0,0 +1,6 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libbfs.la +libbfs_la_SOURCES = bfs.c + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/bfs/bfs.c b/libparted/fs/bfs/bfs.c new file mode 100644 index 0000000..4a5c5ba --- /dev/null +++ b/libparted/fs/bfs/bfs.c @@ -0,0 +1,276 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#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 "bfs.h" + + +#define BFS_SPECIFIC(fs) ((struct BfsSpecific*) (fs->type_specific)) +#define BFS_SB(fs) (BFS_SPECIFIC(fs)->sb) + + +const char BFS_MAGIC[4] = { 0x1B, 0xAD, 0xFA, 0xCE }; +const long long BFS_SECTOR_SIZE = 512; +const uint32_t BFS_PED_SANITY = 0xffffffff; +const long long BFS_PED_MIN_INODES = 16; + +static PedGeometry* +bfs_probe (PedGeometry* geom) +{ + uint8_t* buf; + + PED_ASSERT (geom != NULL, return NULL); + PED_ASSERT (geom->dev != NULL, return NULL); + + buf = ped_malloc (geom->dev->sector_size); + + if (!ped_geometry_read (geom, buf, 0, 1)) + return 0; + + //if ( PED_CPU_TO_LE32((uint32_t)buf) == BFS_MAGIC ) + return ped_geometry_new (geom->dev, geom->start, + ped_div_round_up ( + PED_CPU_TO_LE32((uint32_t)(buf+8)), + geom->dev->sector_size)); + else + return NULL; +} + +#ifndef DISCOVER_ONLY +static int +bfs_clobber (PedGeometry* geom) +{ + uint8_t* buf; + + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (geom->dev != NULL, return 0); + + buf = ped_malloc (geom->dev->sector_size); + + if (!ped_geometry_read (geom, buf, 0, 1)) + return 0; + memset (buf, 0, 512); + return ped_geometry_write (geom, buf, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + + +static PedFileSystem* +bfs_alloc (const PedGeometry* geom) +{ + PedFileSystem* fs; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + goto error; + + fs->type_specific = (struct BfsSpecific*) ped_malloc ( + sizeof (struct BfsSpecific)); + if (!fs->type_specific) + goto error_free_fs; + + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) + goto error_free_type_specific; + + fs->checked = 0; + return fs; + +error_free_type_specific: + ped_free (fs->type_specific); +error_free_fs: + ped_free (fs); +error: + return NULL; +} + + +void +bfs_free (PedFileSystem* fs) +{ + ped_geometry_destroy (fs->geom); + ped_free (fs->type_specific); + ped_free (fs); +} + + +static PedFileSystem* +bfs_open (PedGeometry *geom) +{ + PedFileSystem* fs = bfs_alloc (geom); + + struct bfs_sb* sb = (struct bfs_sb*) ped_malloc(sizeof(struct bfs_sb)); + struct BfsSpecific* bfs; + uint8_t* buf; + + PED_ASSERT (geom != NULL, return NULL); + PED_ASSERT (geom->dev != NULL, return NULL); + + buf = ped_malloc (geom->dev->sector_size); + + if (!fs) + return NULL; + + bfs = fs->type_specific; + + if (!ped_geometry_read (geom, buf, 0, 1)) + return NULL; + + memcpy (sb, buf, BFS_SECTOR_SIZE); + + bfs->sb = sb; + + return fs; +} + + +#ifndef DISCOVER_ONLY +static struct bfs_inode* create_root_inode() +{ + struct bfs_inode* root = ped_malloc (sizeof(struct bfs_inode)); + + root->i = 2UL; + /*root->start = FIX; + root->end = ; + root->eof_off = ;*/ + root->attr = 2UL; + root->mode = 512UL; /* rwxrwxrwx */ + root->uid = root->gid = 0UL; + root->nlinks = 0UL; + root->atime = root->ctime = 0UL; + memset ((void*)root->reserved, 0, 32*4); + + return root; +} + + +static uint8_t* _block_alloc (int n) +{ + return ped_calloc (n * BFS_SECTOR_SIZE); +} + + +static void _write_inodes (PedFileSystem* fs) +{ +} + + +/* write a BFS block - always 512 bytes */ +static int _write_block (PedFileSystem* fs, uint8_t* buf, int n) +{ + /* FIXME: support for bs != 2^9 */ + return ped_geometry_write ( fs->geom, buf, n, 1 ); +} + + +static int _write_sb (PedFileSystem* fs) +{ + uint8_t* sb = _block_alloc (1); + + BFS_SB(fs)->magic = BFS_MAGIC; + BFS_SB(fs)->sanity = BFS_PED_SANITY; + BFS_SB(fs)->start = BFS_SPECIFIC(fs)->data_start; + BFS_SB(fs)->size = BFS_SPECIFIC(fs)->size; + + memcpy (sb, BFS_SB(fs), sizeof(struct bfs_sb)); + + return _write_block (fs, sb, 1); +} + + +static PedFileSystem* +bfs_create (PedGeometry *geom, PedTimer *timer) +{ + PedFileSystem* fs = bfs_alloc (geom); + int n_inodes = PED_MAX (BFS_PED_MIN_INODES, 16/*some sane value here*/); + + /* TODO: check whether geometry is big enough */ + + fs->data_start = 1 + ped_round_up_to (n_inodes * 64, 512); + fs->size = geom->dev->sector_size * length; + + ped_timer_set_state_name (timer, "Writing inodes"); + + + + ped_timer_set_state_name (timer, "Writing super block"); + _write_sb (fs); + + return 0; +} +#endif /* !DISCOVER_ONLY */ + + +static PedFileSystemOps bfs_ops = { + probe: bfs_probe, +#ifndef DISCOVER_ONLY + clobber: bfs_clobber, +#else + clobber: NULL, +#endif + open: bfs_open, +#ifndef DISCOVER_ONLY + create: bfs_create, +#else + create: NULL +#endif + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL +}; + +static PedFileSystemType bfs_type = { + next: NULL, + ops: &bfs_ops, + name: "bfs", + block_sizes: ((int[2]){512, 0}) +}; + +void +ped_file_system_bfs_init () +{ + ped_file_system_type_register (&bfs_type); +} + +void +ped_file_system_bfs_done () +{ + ped_file_system_type_unregister (&bfs_type); +} + + diff --git a/libparted/fs/bfs/bfs.h b/libparted/fs/bfs/bfs.h new file mode 100644 index 0000000..07d8a75 --- /dev/null +++ b/libparted/fs/bfs/bfs.h @@ -0,0 +1,47 @@ +#ifndef BFS_H +#define BFS_H + +#ifndef blk_t + typedef long long blk_t; +#endif + +struct bfs_sb +{ + char magic[4]; + uint32_t start; + uint32_t size; + uint32_t sanity[4]; +}; + +struct bfs_inode +{ + uint32_t i; + uint32_t start; + uint32_t end; + uint32_t eof_off; + uint32_t attr; + uint32_t mode; + uint32_t uid; + uint32_t gid; + uint32_t nlinks; + uint32_t atime; + uint32_t ctime; + uint32_t reserved[4]; +}; + +struct bfs_dirent +{ + uint16_t i; + uint8_t name[14]; +}; + +struct BfsSpecific +{ + struct bfs_sb *sb; + int n_inodes; + blk_t data_start; + long long size; +}; + +#endif + diff --git a/libparted/fs/ext2/Makefile.am b/libparted/fs/ext2/Makefile.am new file mode 100644 index 0000000..f4f202c --- /dev/null +++ b/libparted/fs/ext2/Makefile.am @@ -0,0 +1,19 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libext2.la +libext2_la_SOURCES = ext2.c \ + ext2.h \ + ext2_fs.h \ + ext2_block_relocator.c \ + ext2_buffer.c \ + ext2_inode_relocator.c \ + ext2_meta.c \ + ext2_mkfs.c \ + ext2_resize.c \ + interface.c \ + parted_io.c \ + parted_io.h \ + tune.c \ + tune.h + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/ext2/ext2.c b/libparted/fs/ext2/ext2.c new file mode 100644 index 0000000..c7e6592 --- /dev/null +++ b/libparted/fs/ext2/ext2.c @@ -0,0 +1,794 @@ +/* + ext2.c -- generic ext2 stuff + Copyright (C) 1998, 1999, 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <uuid/uuid.h> +#include "ext2.h" + +/* ext2 stuff ****************************************************************/ + +unsigned char _bitmap[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + +int ext2_copy_block(struct ext2_fs *fs, blk_t from, blk_t to) +{ + unsigned char* buf = ped_malloc (fs->blocksize); + + if (!ext2_bcache_flush(fs, from)) return 0; + if (!ext2_bcache_flush(fs, to)) return 0; + + if (!ext2_read_blocks(fs, buf, from, 1)) return 0; + if (!ext2_write_blocks(fs, buf, to, 1)) return 0; + + return 1; +} + +int ext2_get_block_state(struct ext2_fs *fs, blk_t block) +{ + struct ext2_buffer_head *bh; + int group; + int offset; + int state; + + block -= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + group = block / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + offset = block % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group])); + state = bh->data[offset>>3] & _bitmap[offset&7]; + ext2_brelse(bh, 0); + + return state; +} + +blk_t ext2_find_free_block(struct ext2_fs *fs) +{ + int i; + + for (i=0;i<fs->numgroups;i++) + if (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[i])) + { + blk_t j; + blk_t offset; + + offset = i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb) + + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + for (j=fs->adminblocks; + j<EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + j++) + if (ext2_is_data_block(fs, offset + j) && + !ext2_get_block_state(fs, offset + j)) + return offset + j; + + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Inconsistent group descriptors!")); + } + + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system full!")); + return 0; +} + +ino_t ext2_find_free_inode(struct ext2_fs *fs) +{ + int i; + + for (i=0;i<fs->numgroups;i++) + if (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[i])) + { + ino_t j; + ino_t offset; + + offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1; + for (j=0;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++) + if (!ext2_get_inode_state(fs, offset + j)) + return offset + j; + + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Inconsistent group descriptors!")); + } + + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system full!")); + return 0; +} + +int ext2_move_blocks(struct ext2_fs *fs, blk_t src, blk_t num, blk_t dest) +{ + unsigned char *buf; + blk_t i; + + ped_exception_fetch_all(); + if ((buf = ped_malloc(num << fs->logsize)) != NULL) + { + ped_exception_leave_all(); + + if (!ext2_bcache_flush_range(fs, src, num)) return 0; + if (!ext2_bcache_flush_range(fs, dest, num)) return 0; + + if (!ext2_read_blocks(fs, buf, src, num)) return 0; + if (!ext2_write_blocks(fs, buf, dest, num)) return 0; + + ped_free(buf); + return 1; + } + ped_exception_catch(); + ped_exception_leave_all(); + + if (src > dest) + { + for (i=0;i<num;i++) + if (!ext2_copy_block(fs, src+i, dest+i)) + return 0; + } + else + { + for (i=num;i>0;i--) + if (!ext2_copy_block(fs, src+i, dest+i)) + return 0; + } + return 1; +} + +int ext2_read_blocks(struct ext2_fs *fs, void *ptr, blk_t block, blk_t num) +{ + return fs->devhandle->ops->read(fs->devhandle->cookie, ptr, block, num); +} + +int ext2_set_block_state(struct ext2_fs *fs, blk_t block, int state, int updatemetadata) +{ + struct ext2_buffer_head *bh; + int group; + int offset; + + block -= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + group = block / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + offset = block % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group])); + bh->dirty = 1; + if (state) + bh->data[offset>>3] |= _bitmap[offset&7]; + else + bh->data[offset>>3] &= ~_bitmap[offset&7]; + ext2_brelse(bh, 0); + + if (updatemetadata) + { + int diff; + + diff = state ? -1 : 1; + + fs->gd[group].bg_free_blocks_count = PED_CPU_TO_LE16 + (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[group]) + diff); + fs->sb.s_free_blocks_count = PED_CPU_TO_LE32 + (EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) + diff); + fs->metadirty |= EXT2_META_SB | EXT2_META_GD; + } + return 1; +} + +int ext2_write_blocks(struct ext2_fs *fs, void *ptr, blk_t block, blk_t num) +{ + return fs->devhandle->ops->write(fs->devhandle->cookie, ptr, block, num); +} + +int ext2_zero_blocks(struct ext2_fs *fs, blk_t block, blk_t num) +{ + unsigned char *buf; + blk_t i; + + ped_exception_fetch_all(); + buf = ped_malloc (num << fs->logsize); + if (buf) + { + ped_exception_leave_all(); + + memset(buf, 0, num << fs->logsize); + if (!ext2_bcache_flush_range(fs, block, num)) + goto error_free_buf; + if (!ext2_write_blocks(fs, buf, block, num)) + goto error_free_buf; + ped_free(buf); + return 1; + } + ped_exception_catch(); + + buf = ped_malloc (fs->blocksize); + if (buf) + { + ped_exception_leave_all(); + + memset(buf, 0, fs->blocksize); + + for (i=0;i<num;i++) + { + if (!ext2_bcache_flush(fs, block+i)) + goto error_free_buf; + if (!ext2_write_blocks(fs, buf, block+i, 1)) + goto error_free_buf; + } + + ped_free(buf); + return 1; + } + ped_exception_catch(); + ped_exception_leave_all(); + + for (i=0;i<num;i++) + { + struct ext2_buffer_head *bh; + + bh = ext2_bcreate(fs, block+i); + if (!bh) + goto error; + bh->dirty = 1; + if (!ext2_brelse(bh, 1)) + goto error; + } + return 1; + +error_free_buf: + ped_free(buf); +error: + return 0; +} + +off_t ext2_get_inode_offset(struct ext2_fs *fs, ino_t inode, blk_t *block) +{ + int group; + int offset; + + inode--; + + group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb); + offset = (inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb)) + * sizeof(struct ext2_inode); + + *block = EXT2_GROUP_INODE_TABLE(fs->gd[group]) + + (offset >> fs->logsize); + + return offset & (fs->blocksize - 1); +} + +int ext2_get_inode_state(struct ext2_fs *fs, ino_t inode) +{ + struct ext2_buffer_head *bh; + int group; + int offset; + int ret; + + inode--; + group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb); + offset = inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb); + + bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group])); + ret = bh->data[offset>>3] & _bitmap[offset&7]; + ext2_brelse(bh, 0); + + return ret; +} + +int ext2_read_inode(struct ext2_fs *fs, ino_t inode, struct ext2_inode *data) +{ + struct ext2_buffer_head *bh; + blk_t blk; + off_t off; + + off = ext2_get_inode_offset(fs, inode, &blk); + + bh = ext2_bread(fs, blk); + if (!bh) + return 0; + + memcpy(data, bh->data + off, sizeof(struct ext2_inode)); + ext2_brelse(bh, 0); + return 1; +} + +int ext2_set_inode_state(struct ext2_fs *fs, ino_t inode, int state, int updatemetadata) +{ + struct ext2_buffer_head *bh; + int group; + int offset; + + inode--; + group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb); + offset = inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb); + + bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group])); + if (!bh) + return 0; + bh->dirty = 1; + if (state) + bh->data[offset>>3] |= _bitmap[offset&7]; + else + bh->data[offset>>3] &= ~_bitmap[offset&7]; + ext2_brelse(bh, 0); + + if (updatemetadata) + { + int diff; + + diff = state ? -1 : 1; + + fs->gd[group].bg_free_inodes_count = PED_CPU_TO_LE16 + (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[group]) + diff); + fs->sb.s_free_inodes_count = PED_CPU_TO_LE32 + (EXT2_SUPER_FREE_INODES_COUNT(fs->sb) + diff); + fs->metadirty = EXT2_META_SB | EXT2_META_GD; + } + return 1; +} + +static void +_inode_update_size(struct ext2_fs *fs, struct ext2_inode *inode, int delta) +{ + int i512perblock = 1 << (fs->logsize - 9); + uint64_t size; + + /* i_blocks is in 512 byte blocks */ + inode->i_blocks = PED_CPU_TO_LE32(EXT2_INODE_BLOCKS(*inode) + + delta * i512perblock); + size = EXT2_INODE_SIZE(*inode) + delta * fs->blocksize; + inode->i_size = PED_CPU_TO_LE32(size % (1LL << 32)); + inode->i_size_high = PED_CPU_TO_LE32(size / (1LL << 32)); + inode->i_mtime = PED_CPU_TO_LE32(time(NULL)); +} + +int ext2_do_inode(struct ext2_fs *fs, struct ext2_inode *inode, blk_t block, + int action) +{ + struct ext2_buffer_head *bh; + uint32_t *udata; + blk_t count = 0; + int i; + int u32perblock = fs->blocksize >> 2; + int i512perblock = 1 << (fs->logsize - 9); + + if (block == 0 || EXT2_INODE_MODE(*inode) == 0) + return -1; + + if (fs->opt_debug) + switch (action) + { + case EXT2_ACTION_ADD: + fprintf(stderr,"adding 0x%04x to inode\n", + block); + break; + case EXT2_ACTION_DELETE: + fprintf(stderr,"deleting 0x%04x from inode\n", + block); + break; + case EXT2_ACTION_FIND: + fprintf(stderr,"finding 0x%04x in inode\n", + block); + break; + } + + /* Direct blocks for first 12 blocks */ + for (i = 0; i < EXT2_NDIR_BLOCKS; i++) + { + if (action == EXT2_ACTION_ADD && !EXT2_INODE_BLOCK(*inode, i)) + { + inode->i_block[i] = PED_CPU_TO_LE32(block); + _inode_update_size (fs, inode, 1); + ext2_set_block_state(fs, block, 1, 1); + return i; + } + if (EXT2_INODE_BLOCK(*inode, i) == block) + { + if (action == EXT2_ACTION_DELETE) + { + inode->i_block[i] = 0; + _inode_update_size (fs, inode, -1); + ext2_set_block_state(fs, block, 0, 1); + } + return i; + } + if (EXT2_INODE_BLOCK(*inode, i)) + count += i512perblock; + } + + count += EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK) ? i512perblock : 0; + count += EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK) ? i512perblock : 0; + count += EXT2_INODE_BLOCK(*inode, EXT2_TIND_BLOCK) ? i512perblock : 0; + + if (!EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK) || + (count >= EXT2_INODE_BLOCKS(*inode) && action != EXT2_ACTION_ADD)) + return -1; + + bh = ext2_bread(fs, EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK)); + udata = (uint32_t *)bh->data; + + /* Indirect blocks for next 256/512/1024 blocks (for 1k/2k/4k blocks) */ + for (i = 0; i < u32perblock; i++) { + if (action == EXT2_ACTION_ADD && !udata[i]) { + bh->dirty = 1; + udata[i] = PED_CPU_TO_LE32(block); + _inode_update_size (fs, inode, 1); + ext2_set_block_state(fs, block, 1, 1); + ext2_brelse(bh, 0); + return EXT2_NDIR_BLOCKS + i; + } + if (PED_LE32_TO_CPU(udata[i]) == block) { + if (action == EXT2_ACTION_DELETE) { + bh->dirty = 1; + udata[i] = 0; + _inode_update_size (fs, inode, -1); + ext2_set_block_state(fs, block, 0, 1); + } + ext2_brelse(bh, 0); + return EXT2_NDIR_BLOCKS + i; + } + if (udata[i]) + { + count += i512perblock; + if (count >= EXT2_INODE_BLOCKS(*inode) && + action != EXT2_ACTION_ADD) + return -1; + } + } + + ext2_brelse(bh, 0); + + if (!EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK) || + (count >= EXT2_INODE_BLOCKS(*inode) && action != EXT2_ACTION_ADD)) + return -1; + bh = ext2_bread(fs, EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK)); + udata = (uint32_t *)bh->data; + + /* Double indirect blocks for next 2^16/2^18/2^20 1k/2k/4k blocks */ + for (i = 0; i < u32perblock; i++) { + struct ext2_buffer_head *bh2; + uint32_t *udata2; + int j; + + if (!udata[i]) { + ext2_brelse(bh, 0); + return -1; + } + bh2 = ext2_bread(fs, PED_LE32_TO_CPU(udata[i])); + udata2 = (uint32_t *)bh2->data; + count += i512perblock; + + for (j = 0; j < u32perblock; j++) { + if (action == EXT2_ACTION_ADD && !udata2[j]) { + bh2->dirty = 1; + udata2[j] = PED_CPU_TO_LE32(block); + _inode_update_size (fs, inode, 1); + ext2_set_block_state(fs, block, 1, 1); + ext2_brelse(bh, 0); + ext2_brelse(bh2, 0); + return EXT2_NDIR_BLOCKS + i * u32perblock + j; + } + if (PED_LE32_TO_CPU(udata2[j]) == block) { + if (action == EXT2_ACTION_DELETE) { + bh2->dirty = 1; + udata2[j] = 0; + _inode_update_size (fs, inode, -1); + ext2_set_block_state(fs, block, 0, 1); + } + ext2_brelse(bh, 0); + ext2_brelse(bh2, 0); + return EXT2_NDIR_BLOCKS + i * u32perblock + j; + } + if (udata2[j]) + { + count += i512perblock; + if (count >= EXT2_INODE_BLOCKS(*inode) && + action != EXT2_ACTION_ADD) + return -1; + } + } + ext2_brelse(bh2, 0); + } + ext2_brelse(bh, 0); + + /* FIXME: we should check for triple-indirect blocks here, but it + * would be nice to have a better routine to traverse blocks, and + * file systems that need triple-indirect blocks for the resize + * inode are too big to worry about yet. + */ + + return -1; +} + +int ext2_write_inode(struct ext2_fs *fs, ino_t inode, const struct ext2_inode *data) +{ + struct ext2_buffer_head *bh; + blk_t blk; + off_t off; + + off = ext2_get_inode_offset(fs, inode, &blk); + + bh = ext2_bread(fs, blk); + if (!bh) + return 0; + bh->dirty = 1; + memcpy(bh->data + off, data, sizeof(struct ext2_inode)); + ext2_brelse(bh, 0); + + return 1; +} + +int ext2_zero_inode(struct ext2_fs *fs, ino_t inode) +{ + struct ext2_inode buf; + + memset(&buf, 0, sizeof(struct ext2_inode)); + return ext2_write_inode(fs, inode, &buf); +} + + + + + +/* check whether y is root of x + * (formula grabbed from linux ext2 kernel source) */ +static int is_root(int x, int y) +{ + if (!x) + return 1; + + while (1) + { + if (x == 1) + return 1; + + if (x % y) + return 0; + + x /= y; + } +} + +/* check whether group contains a superblock copy on file systems + * where not all groups have one (sparse superblock feature) */ +int ext2_is_group_sparse(struct ext2_fs *fs, int group) +{ + if (!fs->sparse) + return 1; + + if (is_root(group, 3) || is_root(group, 5) || is_root(group, 7)) + return 1; + + return 0; +} + +void ext2_close(struct ext2_fs *fs) +{ + ext2_commit_metadata(fs, EXT2_META_PRIMARY | EXT2_META_BACKUP); + ext2_sync(fs); + + ext2_bcache_deinit(fs); + + fs->devhandle->ops->close(fs->devhandle->cookie); + + ped_free(fs->gd); + ped_free(fs); +} + +int ext2_commit_metadata(struct ext2_fs *fs, int copies) +{ + int i; + int num; + int wmeta = fs->metadirty & copies; + unsigned char* sb = ped_malloc(fs->blocksize); + struct ext2_super_block *sb_for_io; + int sb_block; + + /* See if there is even anything to write... */ + if (wmeta == EXT2_META_CLEAN) + return 1; + + fs->sb.s_r_blocks_count = PED_CPU_TO_LE32 ( + fs->r_frac * (loff_t)EXT2_SUPER_BLOCKS_COUNT(fs->sb) + / 100); + + if (!ext2_read_blocks (fs, sb, 0, 1)) + return 0; + + if (EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)) { + memcpy(sb, &fs->sb, 1024); + sb_for_io = (struct ext2_super_block *) sb; + } else { + memcpy(sb+1024, &fs->sb, 1024); + sb_for_io = (struct ext2_super_block *) (sb + 1024); + } + + num = copies & EXT2_META_BACKUP ? fs->numgroups : 1; + + for (i = 0, sb_block = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); i < num; + i++, sb_block += EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)) + { + + if (!ext2_is_group_sparse(fs, i)) + continue; + + if (fs->dynamic_version) + sb_for_io->s_block_group_nr = PED_CPU_TO_LE16 (i); + + if ((i == 0 && wmeta & EXT2_META_PRIMARY_SB) || + (i != 0 && wmeta & EXT2_META_SB)) + { + if (!ext2_bcache_flush_range(fs, sb_block, 1)) + return 0; + if (!ext2_write_blocks(fs, sb, sb_block, 1)) + return 0; + } + if ((i == 0 && wmeta & EXT2_META_PRIMARY_GD) || + (i != 0 && wmeta & EXT2_META_GD)) + { + if (!ext2_bcache_flush_range(fs, sb_block + 1, + fs->gdblocks)) + return 0; + if (!ext2_write_blocks(fs, fs->gd, sb_block + 1, + fs->gdblocks)) + return 0; + } + } + + sb_for_io->s_block_group_nr = 0; + + /* Clear the flags of the components we just finished writing. */ + fs->metadirty &= ~copies; + + return 1; +} + +int ext2_sync(struct ext2_fs *fs) +{ + if (!ext2_commit_metadata(fs, EXT2_META_PRIMARY)) return 0; + if (!ext2_bcache_sync(fs)) return 0; + if (!fs->devhandle->ops->sync(fs->devhandle->cookie)) return 0; + return 1; +} + +struct ext2_fs *ext2_open(struct ext2_dev_handle *handle, int state) +{ + struct ext2_fs *fs; + + if ((fs = (struct ext2_fs *) ped_malloc(sizeof(struct ext2_fs))) + == NULL) + goto error; + + handle->ops->set_blocksize(handle->cookie, 10); + + if (!handle->ops->read(handle->cookie, &fs->sb, 1, 1) + || EXT2_SUPER_MAGIC(fs->sb) != EXT2_SUPER_MAGIC_CONST) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Invalid superblock. Are you sure this is an ext2 " + "file system?")); + goto error_free_fs; + } + + + fs->opt_debug = 1; + fs->opt_safe = 1; + fs->opt_verbose = 0; + + if (EXT2_SUPER_STATE(fs->sb) & EXT2_ERROR_FS & ~(state & EXT2_ERROR_FS)) + { + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE_CANCEL, + _("File system has errors! You should run e2fsck.")) + == PED_EXCEPTION_CANCEL) + goto error_free_fs; + } + + if (!((EXT2_SUPER_STATE(fs->sb) | state) & EXT2_VALID_FS) + || (EXT2_SUPER_FEATURE_INCOMPAT(fs->sb) + & EXT3_FEATURE_INCOMPAT_RECOVER)) + { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("File system was not cleanly unmounted! " + "You should run e2fsck. Modifying an unclean " + "file system could cause severe corruption.")) + != PED_EXCEPTION_IGNORE) + goto error_free_fs; + } + + fs->dynamic_version = EXT2_SUPER_REV_LEVEL (fs->sb) > 0; + + if ((EXT2_SUPER_FEATURE_COMPAT(fs->sb) + & ~(EXT3_FEATURE_COMPAT_HAS_JOURNAL | + EXT2_FEATURE_COMPAT_HAS_DIR_INDEX)) || + (EXT2_SUPER_FEATURE_INCOMPAT(fs->sb) + & ~(EXT2_FEATURE_INCOMPAT_FILETYPE | + EXT3_FEATURE_INCOMPAT_RECOVER)) || + (EXT2_SUPER_FEATURE_RO_COMPAT(fs->sb) + & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | + EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an incompatible feature enabled.")); + goto error_free_fs; + } + + fs->devhandle = handle; + fs->logsize = EXT2_SUPER_LOG_BLOCK_SIZE(fs->sb) + 10; + handle->ops->set_blocksize(handle->cookie, fs->logsize); + + if (!ext2_bcache_init(fs)) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Error allocating buffer cache.")); + goto error_free_fs; + } + + fs->blocksize = 1 << fs->logsize; + + fs->numgroups = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(fs->sb) + - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb), + EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)); + fs->gdblocks = ped_div_round_up (fs->numgroups + * sizeof(struct ext2_group_desc), + fs->blocksize); + fs->inodeblocks = ped_div_round_up (EXT2_SUPER_INODES_PER_GROUP(fs->sb) + * sizeof(struct ext2_inode), + fs->blocksize); + fs->r_frac = ped_div_round_up (100 * (loff_t)EXT2_SUPER_R_BLOCKS_COUNT(fs->sb), + EXT2_SUPER_BLOCKS_COUNT(fs->sb)); + fs->adminblocks = 3 + fs->gdblocks + fs->inodeblocks; + + fs->sparse = 0; + if (EXT2_SUPER_FEATURE_RO_COMPAT(fs->sb) + & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) + fs->sparse = 1; + + fs->has_journal = 0 < (EXT2_SUPER_FEATURE_COMPAT(fs->sb) + & EXT3_FEATURE_COMPAT_HAS_JOURNAL); + fs->has_internal_journal + = fs->has_journal + && uuid_is_null(EXT2_SUPER_JOURNAL_UUID(fs->sb)) + && EXT2_SUPER_JOURNAL_INUM(fs->sb); + + fs->gd = ped_malloc (fs->numgroups * sizeof (struct ext2_group_desc) + + fs->blocksize); + if (!fs->gd) + goto error_deinit_bcache; + + ext2_read_blocks(fs, fs->gd, EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + 1, + fs->gdblocks); + + fs->metadirty = 0; + return fs; + +error_free_gd: + ped_free(fs->gd); +error_deinit_bcache: + ext2_bcache_deinit(fs); +error_free_fs: + ped_free(fs); +error: + return NULL; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/ext2/ext2.h b/libparted/fs/ext2/ext2.h new file mode 100644 index 0000000..5ff152f --- /dev/null +++ b/libparted/fs/ext2/ext2.h @@ -0,0 +1,247 @@ +/* + ext2.h -- ext2 header + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef _EXT2_H +#define _EXT2_H + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <sys/types.h> +#include "tune.h" + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + + +/* Ehrm.... sorry, pedanticists! :-) */ +#ifndef offsetof +# define offsetof(type, field) ((size_t)(&(((type *)0)->field))) +#endif + +#ifdef __BEOS__ + typedef off_t loff_t; +#endif + +typedef u_int32_t blk_t; + +#ifdef HAVE_LINUX_EXT2_FS_H +#define _LINUX_TYPES_H +#define i_version i_generation +#include <linux/ext2_fs.h> +#else +#include "ext2_fs.h" +#endif + +extern unsigned char _bitmap[8]; + +struct ext2_buffer_cache +{ + struct ext2_buffer_head *cache; + struct ext2_buffer_head *heads; + struct ext2_buffer_head **hash; + struct ext2_fs *fs; + + int size; + int numalloc; + unsigned char *buffermem; +}; + +struct ext2_buffer_head +{ + struct ext2_buffer_head *next; + struct ext2_buffer_head *prev; + unsigned char *data; + blk_t block; + + int usecount; + int dirty; + + struct ext2_buffer_cache *bc; + int alloc; +}; + +struct ext2_dev_ops +{ + int (*close)(void *cookie); + blk_t (*get_size)(void *cookie); + int (*read)(void *cookie, void *ptr, blk_t block, blk_t num); + int (*set_blocksize)(void *cookie, int logsize); + int (*sync)(void *cookie); + int (*write)(void *cookie, void *ptr, blk_t block, blk_t num); +}; + +struct ext2_dev_handle +{ + struct ext2_dev_ops *ops; + void *cookie; +}; + +struct ext2_fs +{ + struct ext2_dev_handle *devhandle; + + struct ext2_super_block sb; + struct ext2_group_desc *gd; + struct ext2_buffer_cache *bc; + int metadirty; /* 0:all sb&gd copies clean + 1:all sb&gd copies dirty + 2:only first sb&gd copy clean */ + + int dynamic_version; + int sparse; /* sparse superblocks */ + int has_journal; /* journal */ + int has_internal_journal; + + int blocksize; + int logsize; + blk_t adminblocks; + blk_t gdblocks; + blk_t itoffset; + blk_t inodeblocks; + int numgroups; + int r_frac; /* reserved % of blocks */ + + unsigned char *relocator_pool; + unsigned char *relocator_pool_end; + + int opt_debug; + int opt_safe; + int opt_verbose; + + void *journal; +}; + + +#define EXT2_ACTION_ADD 1 +#define EXT2_ACTION_DELETE 2 +#define EXT2_ACTION_FIND 3 + +#define EXT2_META_CLEAN 0 +#define EXT2_META_PRIMARY_SB 1 +#define EXT2_META_BACKUP_SB 2 +#define EXT2_META_PRIMARY_GD 4 +#define EXT2_META_BACKUP_GD 8 + +#define EXT2_META_PRIMARY (EXT2_META_PRIMARY_SB | EXT2_META_PRIMARY_GD) +#define EXT2_META_BACKUP (EXT2_META_BACKUP_SB | EXT2_META_BACKUP_GD) +#define EXT2_META_SB (EXT2_META_PRIMARY_SB | EXT2_META_BACKUP_SB) +#define EXT2_META_GD (EXT2_META_PRIMARY_GD | EXT2_META_BACKUP_GD) + +/* generic stuff */ +int ext2_copy_block (struct ext2_fs *fs, blk_t from, blk_t to); +void ext2_close (struct ext2_fs *fs); +int ext2_commit_metadata (struct ext2_fs *fs, int copies); +off_t ext2_get_inode_offset (struct ext2_fs *fs, ino_t inode, blk_t *block); +blk_t ext2_find_free_block (struct ext2_fs *fs); +ino_t ext2_find_free_inode (struct ext2_fs *fs); +int ext2_get_inode_state (struct ext2_fs *fs, ino_t inode); +int ext2_is_group_sparse (struct ext2_fs *fs, int group); +int ext2_move_blocks (struct ext2_fs *fs, blk_t src, blk_t num, blk_t dest); +struct ext2_fs *ext2_open (struct ext2_dev_handle *handle, int state); +int ext2_read_blocks (struct ext2_fs *fs, void *ptr, blk_t block, blk_t numblocks); +int ext2_read_inode (struct ext2_fs *fs, ino_t inode, struct ext2_inode *inodep); +int ext2_set_inode_state (struct ext2_fs *fs, ino_t inode, int state, int updatemetadata); +int ext2_do_inode (struct ext2_fs *fs, struct ext2_inode *inode, blk_t block, int action); +int ext2_sync (struct ext2_fs *fs); +int ext2_write_blocks (struct ext2_fs *fs, void *ptr, blk_t block, blk_t numblocks); +int ext2_write_inode (struct ext2_fs *fs, ino_t inode, const struct ext2_inode *inodep); +int ext2_zero_blocks (struct ext2_fs *fs, blk_t block, blk_t num); +int ext2_zero_inode (struct ext2_fs *fs, ino_t inode); + +/* block related */ +void ext2_bgbitmap_cache_deinit (struct ext2_fs *fs); +int ext2_bgbitmap_cache_flush (struct ext2_fs *fs); +int ext2_bgbitmap_cache_init (struct ext2_fs *fs); +int ext2_get_block_state (struct ext2_fs *, blk_t block); +int ext2_set_block_state (struct ext2_fs *, blk_t block, int state, int updatemetadata); + +/* block relocator */ +int ext2_block_relocate (struct ext2_fs *fs, blk_t newsize); + +/* buffer */ +void ext2_bcache_deinit (struct ext2_fs *fs); +void ext2_bcache_dump (struct ext2_fs *fs); +int ext2_bcache_flush (struct ext2_fs *fs, blk_t block); +int ext2_bcache_flush_range (struct ext2_fs *fs, blk_t first, blk_t last); +int ext2_bcache_init (struct ext2_fs *fs); +int ext2_bcache_sync (struct ext2_fs *fs); +struct ext2_buffer_head *ext2_bcreate (struct ext2_fs *fs, blk_t block); +struct ext2_buffer_head *ext2_bread (struct ext2_fs *fs, blk_t block); +int ext2_brelse (struct ext2_buffer_head *bh, int forget); + +/* inode relocator */ +int ext2_inode_relocate (struct ext2_fs *fs, int newgroups); + +/* journalling */ +void ext2_journal_deinit (struct ext2_fs *fs); +int ext2_journal_init (struct ext2_fs *fs); + +/* metadata mover */ +int ext2_metadata_push (struct ext2_fs *fs, blk_t newsize); + +/* fs creation */ +struct ext2_fs *ext2_mkfs (struct ext2_dev_handle *handle, blk_t numblocks, int log_block_size, blk_t blocks_per_group, int inodes_per_group, int sparse_sb, int reserved_block_percentage, PedTimer* timer); + +/* resize */ +int ext2_resize_fs (struct ext2_fs *fs, blk_t newsize, PedTimer* timer); + +/* unix I/O */ +struct ext2_dev_handle *ext2_make_dev_handle_from_file(char *dev); + + + + +static __inline__ int ext2_is_data_block(struct ext2_fs *fs, blk_t block) +{ + blk_t blk; + int group; + + PED_ASSERT (block >= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb), return 0); + PED_ASSERT (block < EXT2_SUPER_BLOCKS_COUNT(fs->sb), return 0); + + blk = block - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + + group = blk / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + blk %= EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + if (ext2_is_group_sparse(fs, group) && blk <= fs->gdblocks) + return 0; + + if (block == EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]) || + block == EXT2_GROUP_INODE_BITMAP(fs->gd[group])) + return 0; + + if (block >= EXT2_GROUP_INODE_TABLE(fs->gd[group]) && + block < EXT2_GROUP_INODE_TABLE(fs->gd[group]) + fs->inodeblocks) + return 0; + + return 1; +} + +#endif diff --git a/libparted/fs/ext2/ext2_block_relocator.c b/libparted/fs/ext2/ext2_block_relocator.c new file mode 100644 index 0000000..48c6e8b --- /dev/null +++ b/libparted/fs/ext2/ext2_block_relocator.c @@ -0,0 +1,925 @@ +/* + ext2_block_relocator.c -- ext2 block relocator + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +#include <stdio.h> +#include <stdlib.h> +#include "ext2.h" + + +/* This struct describes a single block that will be relocated. The + * block's original location is "num", and its new location is "dest". + * The block is presumebly referred to by some other block in the file + * system, which is recorded as "refblock". (Only one reference to + * the block is allowed by the block relocator.) "refoffset" describes + * the location within the refblock in which the block is referenced. + * "isindirect" is 0 for direct, 1 for single-indirect, 2 for + * double-indirect, etc. + * + * The algorithms in the file fill the entries of this struct in this order: + * num, refblock/refoffset/isindirectblock, dest. + */ +struct ext2_block_entry +{ + blk_t num; + blk_t dest; + blk_t refblock; + unsigned refoffset:16; + unsigned isindirectblock:16; +}; + +/* This struct contains all data structures relevant to the block relocator. + * - newallocoffset is the distance between the start of a block group, + * and the first data block in the group. This can change when a + * filesystem is resized, because the size of the group descriptors is + * proportional to the size of the filesystem. + * + * - allocentries is the size of the "block" array. It is a tuneable + * parameter that determines how many blocks can be moved in each + * pass. + * + * - usedentries says how many entries of the "block" array have been + * used. That is, how many blocks have been scheduled so far to + * be moved. + * + * - resolvedentries is the number of blocks whose referencing block + * has been found and recorded in block[.]->refblock, etc. + * + * - block is an array that records which blocks need to be moved, and + * where they will be moved to, etc. At some point in the algorithm, this + * array gets sorted (grep for qsort!) by indirectness. + * + * - start: each entry in this array corresponds to a level of + * indirectness (0-3). Each level has two items: dst and num. "num" + * is the number of blocks inside "block" of that level of indirectness. + * After doscan() is finished, and the level of indirectness of each + * block is known, "block" is sorted (see above). The "dst" pointer + * is a pointer inside "block" that indicates the start of the portion + * of the array containg blocks of that level of indirectness. + */ +struct ext2_block_relocator_state +{ + blk_t newallocoffset; + blk_t allocentries; + blk_t usedentries; + blk_t resolvedentries; + struct ext2_block_entry *block; + + struct { + struct ext2_block_entry *dst; + int num; + } start[4]; +}; + + + +static int compare_block_entries(const void *x0, const void *x1) +{ + const struct ext2_block_entry *b0; + const struct ext2_block_entry *b1; + + b0 = (const struct ext2_block_entry *)x0; + b1 = (const struct ext2_block_entry *)x1; + + if (b0->num < b1->num) + return -1; + + if (b0->num > b1->num) + return 1; + + return 0; +} + +static int compare_block_entries_ind(const void *x0, const void *x1) +{ + const struct ext2_block_entry *b0; + const struct ext2_block_entry *b1; + + b0 = (const struct ext2_block_entry *)x0; + b1 = (const struct ext2_block_entry *)x1; + + if (b0->isindirectblock > b1->isindirectblock) + return -1; + + if (b0->isindirectblock < b1->isindirectblock) + return 1; + + return 0; +} + +static int compare_block_entries_ref(const void *x0, const void *x1) +{ + const struct ext2_block_entry *b0; + const struct ext2_block_entry *b1; + + b0 = (const struct ext2_block_entry *)x0; + b1 = (const struct ext2_block_entry *)x1; + + if (b0->refblock < b1->refblock) + return -1; + + if (b0->refblock > b1->refblock) + return 1; + + return 0; +} + +struct ext2_block_entry *findit(struct ext2_block_relocator_state *state, blk_t block) +{ + int min; + int max; + struct ext2_block_entry *retv; + int t; + blk_t tval; + + max = state->usedentries - 1; + min = 0; + retv = NULL; + + repeat: + if (min > max) + goto out; + + t = (min + max) >> 1; + tval = state->block[t].num; + + if (tval > block) + max = t - 1; + + if (tval < block) + min = t + 1; + + if (tval != block) + goto repeat; + + retv = &state->block[t]; + + out: + return retv; +} + +/* This function adds records a reference to a block ("blk"), if that + * block is scheduled to be moved. + */ +static int doblock(struct ext2_fs *fs, + struct ext2_block_relocator_state *state, + blk_t blk, + blk_t refblock, + off_t refoffset, + int indirect) +{ + struct ext2_block_entry *ent; + + if ((ent = findit(state, blk)) == NULL) + return 1; + + if (ent->refblock) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Cross-linked blocks found! Better go run e2fsck " + "first!")); + return 0; + } + + ent->refblock = refblock; + ent->refoffset = refoffset; + ent->isindirectblock = indirect; + + state->resolvedentries++; + state->start[indirect].num++; + + return 1; +} + +static int doindblock(struct ext2_fs *fs, + struct ext2_block_relocator_state *state, + blk_t blk, + blk_t refblock, + off_t refoffset) +{ + struct ext2_buffer_head *bh; + int i; + uint32_t *uptr; + + if (!doblock(fs, state, blk, refblock, refoffset, 1)) + return 0; + + bh = ext2_bread(fs, blk); + if (!bh) + return 0; + uptr = (uint32_t *)bh->data; + + for (i=0;i<(fs->blocksize >> 2);i++) + if (uptr[i]) + if (!doblock(fs, state, PED_LE32_TO_CPU(uptr[i]), blk, + i<<2, 0)) + return 0; + + if (!ext2_brelse(bh, 0)) + return 0; + + return 1; +} + +static int dodindblock(struct ext2_fs *fs, + struct ext2_block_relocator_state *state, + blk_t blk, + blk_t refblock, + off_t refoffset) +{ + struct ext2_buffer_head *bh; + int i; + uint32_t *uptr; + + if (!doblock(fs, state, blk, refblock, refoffset, 2)) + return 0; + + bh = ext2_bread(fs, blk); + if (!bh) + return 0; + uptr = (uint32_t *)bh->data; + + for (i=0;i<(fs->blocksize >> 2);i++) + if (uptr[i]) + if (!doindblock(fs, state, PED_LE32_TO_CPU(uptr[i]), + blk, i<<2)) + return 0; + + if (!ext2_brelse(bh, 0)) + return 0; + + return 1; +} + +static int dotindblock(struct ext2_fs *fs, + struct ext2_block_relocator_state *state, + blk_t blk, + blk_t refblock, + off_t refoffset) +{ + struct ext2_buffer_head *bh; + int i; + uint32_t *uptr; + + if (!doblock(fs, state, blk, refblock, refoffset, 3)) + return 0; + + bh = ext2_bread(fs, blk); + if (!bh) + return 0; + uptr = (uint32_t *)bh->data; + + for (i=0;i<(fs->blocksize >> 2);i++) + if (uptr[i]) + if (!dodindblock(fs, state, PED_LE32_TO_CPU(uptr[i]), + blk, i<<2)) + return 0; + + if (!ext2_brelse(bh, 0)) + return 0; + + return 1; +} + + +/* This function records any block references from an inode to blocks that are + * scheduled to be moved. + */ +static int doinode(struct ext2_fs *fs, struct ext2_block_relocator_state *state, int inode) +{ + struct ext2_inode buf; + + if (!ext2_read_inode(fs, inode, &buf)) + return 0; + + if (EXT2_INODE_BLOCKS(buf)) + { + blk_t blk; + int i; + off_t inodeoffset; + blk_t inodeblock; + + inodeoffset = ext2_get_inode_offset(fs, inode, &inodeblock); + + /* do Hurd block, if there is one... */ + if (EXT2_SUPER_CREATOR_OS(fs->sb) == EXT2_OS_HURD + && EXT2_INODE_TRANSLATOR(buf)) { + if (!doblock(fs, + state, + EXT2_INODE_TRANSLATOR(buf), + inodeblock, + inodeoffset + offsetof(struct ext2_inode, + osd1.hurd1.h_i_translator), + 0)) + return 0; + } + + for (i=0;i<EXT2_NDIR_BLOCKS;i++) + if ((blk = EXT2_INODE_BLOCK(buf, i)) != 0) + if (!doblock(fs, + state, + blk, + inodeblock, + inodeoffset + offsetof(struct ext2_inode, i_block[i]), + 0)) + return 0; + + if ((blk = EXT2_INODE_BLOCK(buf, EXT2_IND_BLOCK)) != 0) + if (!doindblock(fs, + state, + blk, + inodeblock, + inodeoffset + offsetof(struct ext2_inode, i_block[EXT2_IND_BLOCK]))) + return 0; + + if ((blk = EXT2_INODE_BLOCK(buf, EXT2_DIND_BLOCK)) != 0) + if (!dodindblock(fs, + state, + blk, + inodeblock, + inodeoffset + offsetof(struct ext2_inode, i_block[EXT2_DIND_BLOCK]))) + return 0; + + if ((blk = EXT2_INODE_BLOCK(buf, EXT2_TIND_BLOCK)) != 0) + if (!dotindblock(fs, + state, + blk, + inodeblock, + inodeoffset + offsetof(struct ext2_inode, i_block[EXT2_TIND_BLOCK]))) + return 0; + + } + + return 1; +} + +/* This function scans the entire filesystem, to find all references to blocks + * that are scheduled to be moved. + */ +static int doscan(struct ext2_fs *fs, struct ext2_block_relocator_state *state) +{ + int i; + + state->start[0].num = 0; + state->start[1].num = 0; + state->start[2].num = 0; + state->start[3].num = 0; + + for (i=0;i<fs->numgroups;i++) + { + struct ext2_buffer_head *bh; + unsigned int j; + int offset; + + if (fs->opt_verbose) + { + fprintf(stderr, " scanning group %i.... ", i); + fflush(stderr); + } + + bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[i])); + if (!bh) + return 0; + offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1; + + for (j=0;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++) + if (bh->data[j>>3] & _bitmap[j&7]) + { + if (!doinode(fs, state, offset + j)) + { + ext2_brelse(bh, 0); + return 0; + } + + if (state->resolvedentries == state->usedentries) + break; + } + + ext2_brelse(bh, 0); + + if (fs->opt_verbose) + { + fprintf(stderr, "%i/%i blocks resolved\r", + state->resolvedentries, + state->usedentries); + fflush(stderr); + } + + if (state->resolvedentries == state->usedentries) + break; + } + + if (fs->opt_verbose) + fprintf(stderr, "\n"); + + state->start[3].dst = state->block; + state->start[2].dst = state->start[3].dst + state->start[3].num; + state->start[1].dst = state->start[2].dst + state->start[2].num; + state->start[0].dst = state->start[1].dst + state->start[1].num; + + return 1; +} + + + + + +static int ext2_block_relocator_copy(struct ext2_fs *fs, struct ext2_block_relocator_state *state) +{ + unsigned char *buf; + + ped_exception_fetch_all(); + buf = (unsigned char *) ped_malloc(MAXCONT << fs->logsize); + if (buf) + { + int num; + int numleft; + struct ext2_block_entry *ptr; + + ped_exception_leave_all(); + + numleft = state->usedentries; + ptr = state->block; + while (numleft) + { + num = PED_MIN(numleft, MAXCONT); + while (num != 1) + { + if (ptr[0].num + num-1 == ptr[num-1].num && + ptr[0].dest + num-1 == ptr[num-1].dest) + break; + + num >>= 1; + } + + if (!ext2_bcache_flush_range(fs, ptr[0].num, num)) + goto error_free_buf; + if (!ext2_bcache_flush_range(fs, ptr[0].dest, num)) + goto error_free_buf; + + if (!ext2_read_blocks(fs, buf, ptr[0].num, num)) + goto error_free_buf; + if (!ext2_write_blocks(fs, buf, ptr[0].dest, num)) + goto error_free_buf; + + ptr += num; + numleft -= num; + + if (fs->opt_verbose) + { + fprintf(stderr, "copied %i/%i blocks\r", + state->usedentries - numleft, + state->usedentries); + fflush(stderr); + } + } + + ped_free(buf); + + if (fs->opt_safe) + ext2_sync(fs); + + if (fs->opt_verbose) + fprintf(stderr, "\n"); + } + else + { + blk_t i; + + ped_exception_catch(); + ped_exception_leave_all(); + + for (i=0;i<state->usedentries;i++) + { + struct ext2_block_entry *block; + + block = &state->block[i]; + if (!ext2_copy_block(fs, block->num, block->dest)) + goto error; + } + } + + return 1; + +error_free_buf: + ped_free(buf); +error: + return 0; +} + +static int ext2_block_relocator_ref(struct ext2_fs *fs, struct ext2_block_relocator_state *state, struct ext2_block_entry *block) +{ + struct ext2_buffer_head *bh; + static int numerrors = 0; + + if (!(block->refblock || block->refoffset)) + { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("Block %i has no reference? Weird."), + block->num); + return 0; + } + + bh = ext2_bread(fs, block->refblock); + if (!bh) + return 0; + + if (fs->opt_debug) + { + if (PED_LE32_TO_CPU(*((uint32_t *)(bh->data + block->refoffset))) + != block->num) { + fprintf(stderr, + "block %i ref error! (->%i {%i, %i})\n", + block->num, + block->dest, + block->refblock, + block->refoffset); + ext2_brelse(bh, 0); + + if (numerrors++ < 4) + return 1; + + fprintf(stderr, "all is not well!\n"); + return 0; + } + } + + *((uint32_t *)(bh->data + block->refoffset)) + = PED_LE32_TO_CPU(block->dest); + bh->dirty = 1; + ext2_brelse(bh, 0); + + ext2_set_block_state(fs, block->dest, 1, 1); + ext2_set_block_state(fs, block->num, 0, 1); + + if (block->isindirectblock) + { + struct ext2_block_entry *dst; + int i; + int num; + + dst = state->start[block->isindirectblock-1].dst; + num = state->start[block->isindirectblock-1].num; + + for (i=0;i<num;i++) + if (dst[i].refblock == block->num) + dst[i].refblock = block->dest; + } + + return 1; +} + +/* This function allocates new locations for blocks that are scheduled to move + * (inside state->blocks). + * + * FIXME: doesn't seem to handle sparse block groups. That is, there might be + * some free space that could be exploited in resizing that currently isn't... + * + * FIXME: should throw an exception if it fails to allocate blocks. + */ +static int ext2_block_relocator_grab_blocks(struct ext2_fs *fs, struct ext2_block_relocator_state *state) +{ + int i; + blk_t ptr; + + ptr = 0; + + for (i=0;i<fs->numgroups;i++) + if (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[i])) + { + struct ext2_buffer_head *bh; + unsigned int j; + int offset; + + bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[i])); + offset = i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb) + + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + + for (j=state->newallocoffset; + j<EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + j++) + if (!(bh->data[j>>3] & _bitmap[j&7])) + { + state->block[ptr++].dest = offset + j; + + if (ptr == state->usedentries) + { + ext2_brelse(bh, 0); + return 1; + } + } + + ext2_brelse(bh, 0); + } + + return 0; +} + +static int ext2_block_relocator_flush(struct ext2_fs *fs, struct ext2_block_relocator_state *state) +{ + int i; + + if (!state->usedentries) + return 1; + + if (fs->opt_verbose) + fprintf(stderr, "ext2_block_relocator_flush\n"); + + if (fs->opt_debug) + { + again: + + for (i=0; (unsigned int) i < state->usedentries-1; i++) + if (state->block[i].num >= state->block[i+1].num) + { + fprintf(stderr, + "ext2_block_relocator_flush: " + "blocks not in order!\n"); + + qsort(state->block, + state->usedentries, + sizeof(struct ext2_block_entry), + compare_block_entries); + goto again; + } + } + + if (!doscan(fs, state)) + return 0; + + if (!ext2_block_relocator_grab_blocks(fs, state)) + return 0; + + if (!ext2_block_relocator_copy(fs, state)) + return 0; + + qsort(state->block, + state->usedentries, + sizeof(struct ext2_block_entry), + compare_block_entries_ind); + + for (i=3;i>=0;i--) + { + struct ext2_block_entry *dst; + int j; + int num; + + dst = state->start[i].dst; + num = state->start[i].num; + + if (!num) + continue; + + if (fs->opt_verbose) + { + /* FIXXXME gross hack */ + fprintf(stderr, "relocating %s blocks", + ((char *[4]){"direct", + "singly indirect", + "doubly indirect", + "triply indirect"})[i]); + fflush(stderr); + } + + qsort(dst, + num, + sizeof(struct ext2_block_entry), + compare_block_entries_ref); + + for (j=0;j<num;j++) + if (!ext2_block_relocator_ref(fs, state, &dst[j])) + return 0; + + if (fs->opt_safe) { + if (!ext2_sync(fs)) + return 0; + } + + if (fs->opt_verbose) + fprintf(stderr, "\n"); + } + + state->usedentries = 0; + state->resolvedentries = 0; + + return 1; +} + +static int ext2_block_relocator_mark(struct ext2_fs *fs, struct ext2_block_relocator_state *state, blk_t block) +{ + int i; + + if (fs->opt_debug) + { + if (!ext2_get_block_state(fs, block) || + !ext2_is_data_block(fs, block)) + { + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("Block %i shouldn't have been marked " + "(%d, %d)!"), block, + ext2_get_block_state(fs, block), + ext2_is_data_block(fs, block)); + } + } + + if (state->usedentries == state->allocentries - 1) + if (!ext2_block_relocator_flush(fs, state)) + return 0; + + i = state->usedentries; + state->block[i].num = block; + state->block[i].dest = 0; + state->block[i].refblock = 0; + state->block[i].refoffset = 0; + + state->usedentries++; + return 1; +} + +static int ext2_block_relocate_grow(struct ext2_fs *fs, struct ext2_block_relocator_state *state, blk_t newsize) +{ + blk_t newgdblocks; + blk_t newitoffset; + int i; + + newgdblocks = ped_div_round_up (newsize + - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb), + EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)); + newgdblocks = ped_div_round_up (newgdblocks + * sizeof(struct ext2_group_desc), + fs->blocksize); + if (newgdblocks == fs->gdblocks) + return 1; + + newitoffset = newgdblocks + 3; + state->newallocoffset = newitoffset + fs->inodeblocks; + + for (i=0;i<fs->numgroups;i++) + { + struct ext2_buffer_head *bh; + blk_t diff; + blk_t j; + blk_t start; + int sparse; + + bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[i])); + start = (i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)) + + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + sparse = ext2_is_group_sparse(fs, i); + + if (EXT2_GROUP_INODE_TABLE(fs->gd[i]) < start + newitoffset + || (sparse && ((EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) + < start + newitoffset - 2) + || (EXT2_GROUP_INODE_BITMAP(fs->gd[i]) + < start + newitoffset - 1)))) + { + diff = newitoffset - (EXT2_GROUP_INODE_TABLE(fs->gd[i]) + - start); + + for (j=0;j<diff;j++) + { + blk_t k; + + k = EXT2_GROUP_INODE_TABLE(fs->gd[i]) + + fs->inodeblocks + j; + if (bh->data[k>>3] & _bitmap[k&7]) + if (!ext2_block_relocator_mark(fs, + state, start + k)) + { + ext2_brelse(bh, 0); + return 0; + } + } + } + + ext2_brelse(bh, 0); + } + + if (!ext2_block_relocator_flush(fs, state)) + return 0; + + return 1; +} + +static int ext2_block_relocate_shrink(struct ext2_fs *fs, struct ext2_block_relocator_state *state, blk_t newsize) +{ + int diff; + int i; + + diff = ped_div_round_up (newsize - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb), + EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)); + diff = ped_div_round_up (diff * sizeof(struct ext2_group_desc), + fs->blocksize); + diff = fs->gdblocks - diff; + + state->newallocoffset = fs->itoffset + fs->inodeblocks; + + for (i=0;i<fs->numgroups;i++) + { + struct ext2_buffer_head *bh; + blk_t groupsize; + blk_t j; + blk_t offset; + int sparse; + blk_t start; + int type; + + offset = i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb) + + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + sparse = ext2_is_group_sparse(fs, i); + + if (newsize >= offset + EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)) + continue; /* group will survive */ + + bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[i])); + + if (newsize <= offset) + type = 2; /* group is fully chopped off */ + else + type = 1; /* group is partly chopped off */ + + if (!sparse && type == 2) + { + for (j=EXT2_GROUP_INODE_BITMAP(fs->gd[i])+1; + j<EXT2_GROUP_INODE_TABLE(fs->gd[i]); + j++) + { + blk_t k; + + k = j - offset; + if (bh->data[k>>3] & _bitmap[k&7]) + if (!ext2_block_relocator_mark(fs, state, j)) + { + ext2_brelse(bh, 0); + return 0; + } + } + } + + start = newsize; + if (type == 2) + start = EXT2_GROUP_INODE_TABLE(fs->gd[i]) + + fs->inodeblocks; + + start -= offset; + + groupsize = EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + if (offset + groupsize > EXT2_SUPER_BLOCKS_COUNT(fs->sb)) + groupsize = EXT2_SUPER_BLOCKS_COUNT(fs->sb) - offset; + + for (j=start;j<groupsize;j++) + if (bh->data[j>>3] & _bitmap[j&7]) + if (!ext2_block_relocator_mark(fs, state, + offset + j)) + { + ext2_brelse(bh, 0); + return 0; + } + + ext2_brelse(bh, 0); + } + + return ext2_block_relocator_flush(fs, state); +} + +int ext2_block_relocate(struct ext2_fs *fs, blk_t newsize) +{ + struct ext2_block_relocator_state state; + + if (fs->opt_verbose) + fprintf(stderr, "relocating blocks....\n"); + + state.newallocoffset = 0; + state.allocentries = (ext2_relocator_pool_size << 10) / + sizeof(struct ext2_block_entry); + state.usedentries = 0; + state.resolvedentries = 0; + state.block = (struct ext2_block_entry *)fs->relocator_pool; + + if (newsize < EXT2_SUPER_BLOCKS_COUNT(fs->sb)) + return ext2_block_relocate_shrink(fs, &state, newsize); + + return ext2_block_relocate_grow(fs, &state, newsize); +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/ext2/ext2_buffer.c b/libparted/fs/ext2/ext2_buffer.c new file mode 100644 index 0000000..c0ea14c --- /dev/null +++ b/libparted/fs/ext2/ext2_buffer.c @@ -0,0 +1,447 @@ +/* + ext2_buffer.c -- ext2 buffer cache + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "ext2.h" + +/* pseudo-header */ + +static __inline__ int ext2_block_hash(blk_t block) +{ + unsigned long x; + + x = block ^ (block >> 8) ^ (block >> 16) ^ (block >> 24); + return x & ((1 << ext2_hash_bits) - 1); +} + +static struct ext2_buffer_head *ext2_bh_alloc (struct ext2_buffer_cache *, blk_t); +static void ext2_bh_dealloc (struct ext2_buffer_head *); +static struct ext2_buffer_head *ext2_bh_find (struct ext2_buffer_cache *, blk_t); +static int ext2_bh_do_read (struct ext2_buffer_head *); +static int ext2_bh_do_write(struct ext2_buffer_head *); +static void ext2_bh_hash (struct ext2_buffer_head *); +static void ext2_bh_unhash (struct ext2_buffer_head *); + + + +static int try_to_flush(struct ext2_buffer_cache *bc) +{ + int i; + + for (i=0;i<bc->size;i++) + { + struct ext2_buffer_head *bh; + + bh = &bc->heads[i]; + + if (bh->alloc && !bh->usecount && !bh->dirty) + { + ext2_bh_dealloc(bh); + return 1; + } + } + + for (i=0;i<bc->size;i++) + { + struct ext2_buffer_head *bh; + + bh = &bc->heads[i]; + + if (bh->alloc && !bh->usecount && bh->dirty) + { + ext2_bh_do_write(bh); + ext2_bh_dealloc(bh); + return 1; + } + } + + if (ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Couldn't flush buffer cache!")) + != PED_EXCEPTION_IGNORE) + return 0; + return 1; +} + + + + + +static struct ext2_buffer_head *ext2_bh_alloc(struct ext2_buffer_cache *bc, blk_t block) +{ + struct ext2_buffer_head *bh; + int i; + + bh = NULL; + + tryagain: + for (i=0;i<bc->size;i++) + { + bh = &bc->heads[i]; + + if (!bh->alloc) + break; + } + + if (i == bc->size) + { + try_to_flush(bc); + goto tryagain; + } + + bh = &bc->heads[i]; + + bh->next = NULL; + bh->prev = NULL; + bh->block = block; + bh->usecount = 0; + bh->dirty = 0; + bh->alloc = 1; + bc->numalloc++; + + ext2_bh_hash(bh); + + return bh; +} + +static void ext2_bh_dealloc(struct ext2_buffer_head *bh) +{ + if (bh->dirty) + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_IGNORE, + "deallocing() a dirty buffer! %i\n", bh->block); + + ext2_bh_unhash(bh); + bh->alloc = 0; + bh->bc->numalloc--; +} + +static struct ext2_buffer_head *ext2_bh_find(struct ext2_buffer_cache *bc, blk_t block) +{ + struct ext2_buffer_head *a; + struct ext2_buffer_head *b; + int hash; + + hash = ext2_block_hash(block); + a = bc->hash[hash]; + + if (a != NULL) + { + b = a; + do + { + if (a->block == block) + return a; + + a = a->next; + } while (a != b); + } + + return NULL; +} + +static int ext2_bh_do_read(struct ext2_buffer_head *bh) +{ + return ext2_read_blocks(bh->bc->fs, bh->data, bh->block, 1); +} + +static int ext2_bh_do_write(struct ext2_buffer_head *bh) +{ + if (!bh->alloc) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + "Attempt to write unallocated buffer."); + return 0; + } + + ext2_write_blocks(bh->bc->fs, bh->data, bh->block, 1); + bh->dirty = 0; + return 1; +} + +static void ext2_bh_hash(struct ext2_buffer_head *bh) +{ + int hash; + + hash = ext2_block_hash(bh->block); + if (bh->bc->hash[hash] != NULL) + { + bh->next = bh->bc->hash[hash]; + bh->prev = bh->next->prev; + bh->next->prev = bh; + bh->prev->next = bh; + return; + } + + bh->bc->hash[hash] = bh; + bh->next = bh->prev = bh; +} + +static void ext2_bh_unhash(struct ext2_buffer_head *bh) +{ + int hash; + + hash = ext2_block_hash(bh->block); + + bh->prev->next = bh->next; + bh->next->prev = bh->prev; + + if (bh->bc->hash[hash] == bh) + { + if (bh->next != bh) + bh->bc->hash[hash] = bh->next; + else + bh->bc->hash[hash] = NULL; + } + + bh->next = NULL; + bh->prev = NULL; +} + + + + + + + +static int breadimmhits = 0; +static int breadindhits = 0; +static int breadmisses = 0; + +void ext2_bcache_deinit(struct ext2_fs *fs) +{ + ext2_bcache_sync(fs); + ped_free(fs->bc->buffermem); + ped_free(fs->bc->hash); + ped_free(fs->bc->heads); + ped_free(fs->bc); + + if (fs->opt_verbose) + fprintf(stderr, + "direct hits: %i, indirect hits: %i, misses: %i\n", + breadimmhits, + breadindhits, + breadmisses); +} + +void ext2_bcache_dump(struct ext2_fs *fs) +{ + int i; + + fprintf(stderr, "buffer cache dump:\n"); + + for (i=0;i<(1<<ext2_hash_bits);i++) + if (fs->bc->hash[i] != NULL) + { + struct ext2_buffer_head *a; + struct ext2_buffer_head *b; + + fprintf(stderr, "%i: ", i); + + a = b = fs->bc->hash[i]; + do + { + fprintf(stderr, "%i ", a->block); + a = a->next; + } while (a != b); + + fprintf(stderr, "\n"); + } +} + +int ext2_bcache_flush(struct ext2_fs *fs, blk_t block) +{ + struct ext2_buffer_head *bh; + + if ((bh = ext2_bh_find(fs->bc, block)) == NULL) + return 1; + + if (bh->usecount) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + "Attempt to flush a buffer that's in use! [%i,%i]", + bh->block, bh->usecount); + return 0; + } + + if (bh->dirty) { + if (!ext2_bh_do_write(bh)) + return 0; + } + + ext2_bh_dealloc(bh); + return 1; +} + +int ext2_bcache_flush_range(struct ext2_fs *fs, blk_t block, blk_t num) +{ + blk_t end = block + num; + + for (; block < end; block++) { + if (!ext2_bcache_flush(fs, block)) + return 0; + } + return 1; +} + +int ext2_bcache_init(struct ext2_fs *fs) +{ + struct ext2_buffer_cache *bc; + int i; + int size; + + size = ext2_buffer_cache_pool_size >> (fs->logsize - 10); + + if ((bc = (struct ext2_buffer_cache *) ped_malloc(sizeof(struct ext2_buffer_cache))) == NULL) + return 0; + + if ((bc->heads = (struct ext2_buffer_head *) ped_malloc(size * sizeof(struct ext2_buffer_head))) == NULL) + return 0; + + if ((bc->hash = (struct ext2_buffer_head **) ped_malloc(sizeof(struct ext2_buffer_head *) << ext2_hash_bits)) == NULL) + { + ped_free(bc->heads); + ped_free(bc); + return 0; + } + + if ((bc->buffermem = (unsigned char *) ped_malloc(ext2_buffer_cache_pool_size << 10)) == NULL) + { + ped_free(bc->hash); + ped_free(bc->heads); + ped_free(bc); + return 0; + } + + bc->cache = &bc->heads[0]; + bc->fs = fs; + bc->size = size; + bc->numalloc = 0; + + for (i=0;i<size;i++) + { + bc->heads[i].data = bc->buffermem + (i << fs->logsize); + bc->heads[i].bc = bc; + bc->heads[i].alloc = 0; + } + + for (i=0;i<(1<<ext2_hash_bits);i++) + bc->hash[i] = NULL; + + fs->bc = bc; + + return 1; +} + +int ext2_bcache_sync(struct ext2_fs *fs) +{ + int i; + + for (i=0;i<fs->bc->size;i++) + { + struct ext2_buffer_head *bh; + + bh = &fs->bc->heads[i]; + + if (bh->alloc && bh->dirty) { + if (!ext2_bh_do_write(bh)) + return 0; + } + } + return 1; +} + + + + + + + + +struct ext2_buffer_head *ext2_bcreate(struct ext2_fs *fs, blk_t block) +{ + struct ext2_buffer_head *bh; + + if ((bh = ext2_bh_find(fs->bc, block)) != NULL) + { + bh->usecount++; + } + else + { + bh = ext2_bh_alloc(fs->bc, block); + bh->usecount = 1; + } + + memset(bh->data, 0, fs->blocksize); + bh->dirty = 1; + + return bh; +} + +struct ext2_buffer_head *ext2_bread(struct ext2_fs *fs, blk_t block) +{ + struct ext2_buffer_head *bh; + + if ((bh = fs->bc->cache)->block == block) + { + breadimmhits++; + bh->usecount++; + return bh; + } + + if ((bh = ext2_bh_find(fs->bc, block)) != NULL) + { + fs->bc->cache = bh; + breadindhits++; + bh->usecount++; + return bh; + } + + breadmisses++; + + bh = ext2_bh_alloc(fs->bc, block); + fs->bc->cache = bh; + bh->usecount = 1; + if (!ext2_bh_do_read(bh)) { + ext2_bh_dealloc(bh); + return NULL; + } + + return bh; +} + +int ext2_brelse(struct ext2_buffer_head *bh, int forget) +{ + if (bh->usecount-- == 1 && forget) + { + if (bh->dirty) { + if (!ext2_bh_do_write(bh)) + return 0; + } + + ext2_bh_dealloc(bh); + } + return 1; +} + +#endif /* !DISCOVER_ONLY */ + diff --git a/libparted/fs/ext2/ext2_fs.h b/libparted/fs/ext2/ext2_fs.h new file mode 100644 index 0000000..713cc21 --- /dev/null +++ b/libparted/fs/ext2/ext2_fs.h @@ -0,0 +1,323 @@ +/* + * linux/include/linux/ext2_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * EXT2_*_*() convienience macros added by Andrew Clausen <clausen@gnu.org> + * Copyright (C) 2000 Free Software Foundation, Inc. + */ + +#ifndef _EXT2_FS_H +#define _EXT2_FS_H + +#include <parted/endian.h> +#include <stdint.h> + +/* + * The second extended file system constants/structures + */ + +#define EXT2_SUPER_MAGIC_CONST 0xEF53 +#define EXT2_MIN_BLOCK_SIZE 1024 +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) +#define EXT2_VALID_FS 0x0001 +#define EXT2_ERROR_FS 0x0002 +#define EXT2_RESERVED_INODE_COUNT 11 + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Feature set definitions + */ +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_HAS_DIR_INDEX 0x0020 + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 + +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 + +/* + * Special inodes numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_ACL_IDX_INO 3 /* ACL inode */ +#define EXT2_ACL_DATA_INO 4 /* ACL inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +struct ext2_dir_entry_2 +{ + uint32_t inode; + uint16_t rec_len; + uint8_t name_len; + uint8_t file_type; + char name[255]; +}; + +struct ext2_group_desc +{ + uint32_t bg_block_bitmap; + uint32_t bg_inode_bitmap; + uint32_t bg_inode_table; + uint16_t bg_free_blocks_count; + uint16_t bg_free_inodes_count; + uint16_t bg_used_dirs_count; + uint16_t bg_pad; + uint32_t bg_reserved[3]; +}; + +struct ext2_inode +{ + uint16_t i_mode; /* File mode */ + uint16_t i_uid; /* Owner Uid */ + uint32_t i_size; /* Size in bytes */ + uint32_t i_atime; /* Access time */ + uint32_t i_ctime; /* Creation time */ + uint32_t i_mtime; /* Modification time */ + uint32_t i_dtime; /* Deletion Time */ + uint16_t i_gid; /* Group Id */ + uint16_t i_links_count; /* Links count */ + uint32_t i_blocks; /* Blocks count */ + uint32_t i_flags; /* File flags */ + union { + struct { + uint32_t l_i_reserved1; + } linux1; + struct { + uint32_t h_i_translator; + } hurd1; + struct { + uint32_t m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + uint32_t i_generation; /* File version (for NFS) */ + uint32_t i_file_acl; /* File ACL */ + uint32_t i_dir_acl; /* Directory ACL */ + uint32_t i_faddr; /* Fragment address */ + union { + struct { + uint8_t l_i_frag; /* Fragment number */ + uint8_t l_i_fsize; /* Fragment size */ + uint16_t i_pad1; + uint32_t l_i_reserved2[2]; + } linux2; + struct { + uint8_t h_i_frag; /* Fragment number */ + uint8_t h_i_fsize; /* Fragment size */ + uint16_t h_i_mode_high; + uint16_t h_i_uid_high; + uint16_t h_i_gid_high; + uint32_t h_i_author; + } hurd2; + struct { + uint8_t m_i_frag; /* Fragment number */ + uint8_t m_i_fsize; /* Fragment size */ + uint16_t m_pad1; + uint32_t m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ +}; + +#define i_size_high i_dir_acl + +struct ext2_super_block +{ + uint32_t s_inodes_count; /* Inodes count */ + uint32_t s_blocks_count; /* Blocks count */ + uint32_t s_r_blocks_count; /* Reserved blocks count */ + uint32_t s_free_blocks_count; /* Free blocks count */ + uint32_t s_free_inodes_count; /* Free inodes count */ + uint32_t s_first_data_block; /* First Data Block */ + uint32_t s_log_block_size; /* Block size */ + int32_t s_log_frag_size; /* Fragment size */ + uint32_t s_blocks_per_group; /* # Blocks per group */ + uint32_t s_frags_per_group; /* # Fragments per group */ + uint32_t s_inodes_per_group; /* # Inodes per group */ + uint32_t s_mtime; /* Mount time */ + uint32_t s_wtime; /* Write time */ + uint16_t s_mnt_count; /* Mount count */ + int16_t s_max_mnt_count; /* Maximal mount count */ + uint16_t s_magic; /* Magic signature */ + uint16_t s_state; /* File system state */ + uint16_t s_errors; /* Behaviour when detecting errors */ + uint16_t s_minor_rev_level; /* minor revision level */ + uint32_t s_lastcheck; /* time of last check */ + uint32_t s_checkinterval; /* max. time between checks */ + uint32_t s_creator_os; /* OS */ + uint32_t s_rev_level; /* Revision level */ + uint16_t s_def_resuid; /* Default uid for reserved blocks */ + uint16_t s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the file system. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + uint32_t s_first_ino; /* First non-reserved inode */ + uint16_t s_inode_size; /* size of inode structure */ + uint16_t s_block_group_nr; /* block group # of this superblock */ + uint32_t s_feature_compat; /* compatible feature set */ + uint32_t s_feature_incompat; /* incompatible feature set */ + uint32_t s_feature_ro_compat; /* readonly-compatible feature set */ + uint8_t s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + uint32_t s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + uint8_t s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + uint8_t s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + uint16_t s_padding1; + /* + * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. + */ + uint8_t s_journal_uuid[16]; /* uuid of journal superblock */ + uint32_t s_journal_inum; /* inode number of journal file */ + uint32_t s_journal_dev; /* device number of journal file */ + uint32_t s_last_orphan; /* start of list of inodes to delete */ + + uint32_t s_reserved[197]; /* Padding to the end of the block */ +}; + +#define EXT2_DIRENT_INODE(dir_ent) (PED_LE32_TO_CPU((dir_ent).inode)) +#define EXT2_DIRENT_REC_LEN(dir_ent) (PED_LE16_TO_CPU((dir_ent).rec_len)) +#define EXT2_DIRENT_NAME_LEN(dir_ent) ((dir_ent).name_len) +#define EXT2_DIRENT_FILE_TYPE(dir_ent) ((dir_ent).file_type) + +#define EXT2_GROUP_BLOCK_BITMAP(gd) (PED_LE32_TO_CPU((gd).bg_block_bitmap)) +#define EXT2_GROUP_INODE_BITMAP(gd) (PED_LE32_TO_CPU((gd).bg_inode_bitmap)) +#define EXT2_GROUP_INODE_TABLE(gd) (PED_LE32_TO_CPU((gd).bg_inode_table)) +#define EXT2_GROUP_FREE_BLOCKS_COUNT(gd) \ + (PED_LE16_TO_CPU((gd).bg_free_blocks_count)) +#define EXT2_GROUP_FREE_INODES_COUNT(gd) \ + (PED_LE16_TO_CPU((gd).bg_free_inodes_count)) +#define EXT2_GROUP_USED_DIRS_COUNT(gd) \ + (PED_LE16_TO_CPU((gd).bg_used_dirs_count)) + +#define EXT2_INODE_MODE(inode) (PED_LE16_TO_CPU((inode).i_mode)) +#define EXT2_INODE_UID(inode) (PED_LE16_TO_CPU((inode).i_uid)) +#define EXT2_INODE_SIZE(inode) \ + ((uint64_t) PED_LE32_TO_CPU((inode).i_size) \ + + ((uint64_t) PED_LE32_TO_CPU((inode).i_size_high) << 32)) +#define EXT2_INODE_ATIME(inode) (PED_LE32_TO_CPU((inode).i_atime)) +#define EXT2_INODE_CTIME(inode) (PED_LE32_TO_CPU((inode).i_ctime)) +#define EXT2_INODE_MTIME(inode) (PED_LE32_TO_CPU((inode).i_mtime)) +#define EXT2_INODE_DTIME(inode) (PED_LE32_TO_CPU((inode).i_dtime)) +#define EXT2_INODE_GID(inode) (PED_LE16_TO_CPU((inode).i_gid)) +#define EXT2_INODE_LINKS_COUNT(inode) (PED_LE16_TO_CPU((inode).i_links_count)) +#define EXT2_INODE_BLOCKS(inode) (PED_LE32_TO_CPU((inode).i_blocks)) +#define EXT2_INODE_FLAGS(inode) (PED_LE32_TO_CPU((inode).i_flags)) +#define EXT2_INODE_TRANSLATOR(inode) (PED_LE32_TO_CPU((inode).osd1.hurd1.h_i_translator)) +#define EXT2_INODE_BLOCK(inode, blk) (PED_LE32_TO_CPU((inode).i_block[blk])) + +#define EXT2_SUPER_INODES_COUNT(sb) (PED_LE32_TO_CPU((sb).s_inodes_count)) +#define EXT2_SUPER_BLOCKS_COUNT(sb) (PED_LE32_TO_CPU((sb).s_blocks_count)) +#define EXT2_SUPER_R_BLOCKS_COUNT(sb) (PED_LE32_TO_CPU((sb).s_r_blocks_count)) +#define EXT2_SUPER_FREE_BLOCKS_COUNT(sb) \ + (PED_LE32_TO_CPU((sb).s_free_blocks_count)) +#define EXT2_SUPER_FREE_INODES_COUNT(sb) \ + (PED_LE32_TO_CPU((sb).s_free_inodes_count)) +#define EXT2_SUPER_FIRST_DATA_BLOCK(sb) \ + (PED_LE32_TO_CPU((sb).s_first_data_block)) +#define EXT2_SUPER_LOG_BLOCK_SIZE(sb) (PED_LE32_TO_CPU((sb).s_log_block_size)) +#define EXT2_SUPER_LOG_FRAG_SIZE(sb) \ + ((int32_t) PED_LE32_TO_CPU((sb).s_log_frag_size)) +#define EXT2_SUPER_BLOCKS_PER_GROUP(sb) \ + (PED_LE32_TO_CPU((sb).s_blocks_per_group)) +#define EXT2_SUPER_FRAGS_PER_GROUP(sb) \ + (PED_LE32_TO_CPU((sb).s_frags_per_group)) +#define EXT2_SUPER_INODES_PER_GROUP(sb) \ + (PED_LE32_TO_CPU((sb).s_inodes_per_group)) +#define EXT2_SUPER_MTIME(sb) (PED_LE32_TO_CPU((sb).s_mtime)) +#define EXT2_SUPER_WTIME(sb) (PED_LE32_TO_CPU((sb).s_wtime)) +#define EXT2_SUPER_MNT_COUNT(sb) (PED_LE16_TO_CPU((sb).s_mnt_count)) +#define EXT2_SUPER_MAX_MNT_COUNT(sb) \ + ((int16_t) PED_LE16_TO_CPU((sb).s_max_mnt_count)) +#define EXT2_SUPER_MAGIC(sb) (PED_LE16_TO_CPU((sb).s_magic)) +#define EXT2_SUPER_STATE(sb) (PED_LE16_TO_CPU((sb).s_state)) +#define EXT2_SUPER_ERRORS(sb) (PED_LE16_TO_CPU((sb).s_errors)) +#define EXT2_SUPER_MINOR_REV_LEVEL(sb) \ + (PED_LE16_TO_CPU((sb).s_minor_rev_level)) +#define EXT2_SUPER_LASTCHECK(sb) (PED_LE32_TO_CPU((sb).s_lastcheck)) +#define EXT2_SUPER_CHECKINTERVAL(sb) (PED_LE32_TO_CPU((sb).s_checkinterval)) +#define EXT2_SUPER_CREATOR_OS(sb) (PED_LE32_TO_CPU((sb).s_creator_os)) +#define EXT2_SUPER_REV_LEVEL(sb) (PED_LE32_TO_CPU((sb).s_rev_level)) +#define EXT2_SUPER_DEF_RESUID(sb) (PED_LE16_TO_CPU((sb).s_def_resuid)) +#define EXT2_SUPER_DEF_RESGID(sb) (PED_LE16_TO_CPU((sb).s_def_resgid)) + +#define EXT2_SUPER_FIRST_INO(sb) (PED_LE32_TO_CPU((sb).s_first_ino)) +#define EXT2_SUPER_INODE_SIZE(sb) (PED_LE16_TO_CPU((sb).s_inode_size)) +#define EXT2_SUPER_BLOCK_GROUP_NR(sb) (PED_LE16_TO_CPU((sb).s_block_group_nr)) +#define EXT2_SUPER_FEATURE_COMPAT(sb) (PED_LE32_TO_CPU((sb).s_feature_compat)) +#define EXT2_SUPER_FEATURE_INCOMPAT(sb) \ + (PED_LE32_TO_CPU((sb).s_feature_incompat)) +#define EXT2_SUPER_FEATURE_RO_COMPAT(sb) \ + (PED_LE32_TO_CPU((sb).s_feature_ro_compat)) +#define EXT2_SUPER_UUID(sb) ((sb).s_uuid) +#define EXT2_SUPER_VOLUME_NAME(sb) ((sb).s_volume_name) +#define EXT2_SUPER_LAST_MOUNTED(sb) ((sb).s_last_mounted) +#define EXT2_SUPER_ALGORITHM_USAGE_BITMAP(sb) \ + (PED_LE32_TO_CPU((sb).s_algorithm_usage_bitmap)) + +#define EXT2_SUPER_JOURNAL_UUID(sb) ((sb).s_journal_uuid) +#define EXT2_SUPER_JOURNAL_INUM(sb) (PED_LE32_TO_CPU((sb).s_journal_inum)) +#define EXT2_SUPER_JOURNAL_DEV(sb) (PED_LE32_TO_CPU((sb).s_journal_dev)) +#define EXT2_SUPER_LAST_ORPHAN(sb) (PED_LE32_TO_CPU((sb).s_last_orphan)) + +#endif diff --git a/libparted/fs/ext2/ext2_inode_relocator.c b/libparted/fs/ext2/ext2_inode_relocator.c new file mode 100644 index 0000000..82a94bf --- /dev/null +++ b/libparted/fs/ext2/ext2_inode_relocator.c @@ -0,0 +1,600 @@ +/* + ext2_inode_relocator.c -- ext2 inode relocator + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> /* for S_ISDIR */ +#include "ext2.h" + + + + + + +struct ext2_reference +{ + blk_t block; + off_t offset; +}; + +struct ext2_inode_entry +{ + ino_t num; + ino_t dest; + unsigned numreferences:16; + unsigned isdir:1; + struct ext2_reference *ref; +}; + +struct ext2_inode_relocator_state +{ + int usedentries; + int resolvedentries; + struct ext2_inode_entry *inode; + struct ext2_reference *last; +}; + + + + + +static struct ext2_inode_entry *findit(struct ext2_inode_relocator_state *state, ino_t inode) +{ + int min; + int max; + struct ext2_inode_entry *retv; + int t; + blk_t tval; + + max = state->usedentries - 1; + min = 0; + retv = NULL; + + repeat: + if (min > max) + goto out; + + t = (min + max) >> 1; + tval = state->inode[t].num; + + t--; + if (tval > inode) + max = t; + + t += 2; + if (tval < inode) + min = t; + + t--; + + if (tval != inode) + goto repeat; + + retv = &state->inode[t]; + + out: + return retv; +} + +static int addref(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, ino_t inode, blk_t block, off_t offset) +{ + struct ext2_inode_entry *ent; + int i; + + if ((ent = findit(state, inode)) == NULL) + return 1; + + for (i=0;i<ent->numreferences;i++) + if (!ent->ref[i].block) + break; + + if (i == ent->numreferences) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Found an inode with a incorrect link count. " + "Better go run e2fsck first!")); + return 0; + } + + if (i == ent->numreferences - 1) + state->resolvedentries++; + + ent->ref[i].block = block; + ent->ref[i].offset = offset; + + return 1; +} + +static int doblock(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, blk_t blockno) +{ + struct ext2_buffer_head *bh; + off_t offset; + + bh = ext2_bread(fs, blockno); + if (!bh) + return 0; + + offset = 0; + do + { + struct ext2_dir_entry_2 *ptr; + + ptr = (struct ext2_dir_entry_2 *)(bh->data + offset); + + if (ptr->name_len) + if (!addref(fs, state, EXT2_DIRENT_INODE(*ptr), blockno, + offset)) + return 0; + + PED_ASSERT (ptr->rec_len > 0, return 0); + offset += EXT2_DIRENT_REC_LEN (*ptr); + } while (offset < fs->blocksize); + + ext2_brelse(bh, 0); + return 1; +} + +static int doindblock(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, blk_t blockno) +{ + struct ext2_buffer_head *bh; + blk_t blk; + int i; + + bh = ext2_bread(fs, blockno); + + for (i=0;i<(fs->blocksize>>2);i++) + if ((blk = PED_LE32_TO_CPU(((uint32_t *)bh->data)[i])) != 0) + if (!doblock(fs, state, blk)) + return 0; + + ext2_brelse(bh, 0); + return 1; +} + +static int dodindblock(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, blk_t blockno) +{ + struct ext2_buffer_head *bh; + blk_t blk; + int i; + + bh = ext2_bread(fs, blockno); + if (!bh) + return 0; + + for (i=0;i<(fs->blocksize>>2);i++) + if ((blk = PED_LE32_TO_CPU(((uint32_t *)bh->data)[i])) != 0) + if (!doindblock(fs, state, blk)) + return 0; + + ext2_brelse(bh, 0); + return 1; +} + +static int dotindblock(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, blk_t blockno) +{ + struct ext2_buffer_head *bh; + blk_t blk; + int i; + + bh = ext2_bread(fs, blockno); + if (!bh) + return 0; + + for (i=0;i<(fs->blocksize>>2);i++) + if ((blk = PED_LE32_TO_CPU(((uint32_t *)bh->data)[i])) != 0) + if (!dodindblock(fs, state, blk)) + return 0; + + ext2_brelse(bh, 0); + return 1; +} + +static int doinode(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, ino_t inode) +{ + struct ext2_inode buf; + int i; + + if (!ext2_read_inode(fs, inode, &buf)) + return 0; + if (S_ISDIR(EXT2_INODE_MODE(buf))) + { + blk_t blk; + + for (i=0;i<EXT2_NDIR_BLOCKS;i++) + if ((blk = EXT2_INODE_BLOCK(buf, i)) != 0) + if (!doblock(fs, state, blk)) + return 0; + + if ((blk = EXT2_INODE_BLOCK(buf, EXT2_IND_BLOCK)) != 0) + if (!doindblock(fs, state, blk)) + return 0; + + if ((blk = EXT2_INODE_BLOCK(buf, EXT2_DIND_BLOCK)) != 0) + if (!dodindblock(fs, state, blk)) + return 0; + + if ((blk = EXT2_INODE_BLOCK(buf, EXT2_TIND_BLOCK)) != 0) + if (!dotindblock(fs, state, blk)) + return 0; + } + + return 1; +} + +static int doscangroup(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, int group) +{ + struct ext2_buffer_head *bh; + unsigned int i; + int offset; + + if (fs->opt_verbose) + fprintf(stderr, " scanning group %i.... ", group); + + bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group])); + offset = group * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1; + + for (i=0;i<EXT2_SUPER_INODES_PER_GROUP(fs->sb);i++) + if (bh->data[i>>3] & _bitmap[i&7]) + { + if (!doinode(fs, state, offset + i)) + { + ext2_brelse(bh, 0); + return 0; + } + + if (state->resolvedentries == state->usedentries) + break; + } + + ext2_brelse(bh, 0); + + if (fs->opt_verbose) + fprintf(stderr, + "%i/%i inodes resolved\r", + state->resolvedentries, + state->usedentries); + + return 1; +} + +/* basically: this builds a dependency graph of the inodes in the entire file + * system. inodes are only referenced by the directory tree (or the magic + * ones implicitly, like the bad blocks inode), so we just walk the directory + * tree adding references. + */ +static int doscan(struct ext2_fs *fs, struct ext2_inode_relocator_state *state) +{ + int i; + + /* while the journal will usually be inode 8 (and therefore will never + * need to be moved), we don't have any guarantee (grrr). So, we + * need to be prepared to move it... (and update the reference in the + * super block) + */ + if (fs->has_internal_journal) + addref(fs, state, EXT2_SUPER_JOURNAL_INUM(fs->sb), + 1, offsetof(struct ext2_super_block, s_journal_inum)); + + if (!doscangroup(fs, state, 0)) + return 0; + + if (state->resolvedentries != state->usedentries) + for (i=fs->numgroups-1;i>0;i--) + { + if (!doscangroup(fs, state, i)) + return 0; + + if (state->resolvedentries == state->usedentries) + break; + } + + if (fs->opt_verbose) + fprintf(stderr, "\n"); + + return 1; +} + + + + + + + +static int ext2_inode_relocator_copy(struct ext2_fs *fs, struct ext2_inode_relocator_state *state) +{ + int i; + + for (i=0;i<state->usedentries;i++) + { + struct ext2_inode buf; + struct ext2_inode_entry *entry; + + entry = &state->inode[i]; + + if (fs->opt_debug) + if (!ext2_get_inode_state(fs, entry->num) || + ext2_get_inode_state(fs, entry->dest)) + fprintf(stderr, "inodebitmaperror\n"); + + if (!ext2_read_inode(fs, entry->num, &buf)) + return 0; + if (!ext2_write_inode(fs, entry->dest, &buf)) + return 0; + + entry->isdir = S_ISDIR(EXT2_INODE_MODE(buf))?1:0; + } + + if (fs->opt_safe) + if (!ext2_sync(fs)) + return 0; + return 1; +} + +static int ext2_inode_relocator_finish(struct ext2_fs *fs, struct ext2_inode_relocator_state *state) +{ + int i; + + for (i=0;i<state->usedentries;i++) + { + struct ext2_inode_entry *entry; + + entry = &state->inode[i]; + ext2_set_inode_state(fs, entry->dest, 1, 1); + ext2_set_inode_state(fs, entry->num, 0, 1); + ext2_zero_inode(fs, entry->num); + } + + if (fs->opt_safe) + if (!ext2_sync(fs)) + return 0; + return 1; +} + +static int ext2_inode_relocator_ref(struct ext2_fs *fs, struct ext2_inode_relocator_state *state) +{ + int i; + static int numerrors = 0; + + for (i=0;i<state->usedentries;i++) + { + struct ext2_inode_entry *entry; + int j; + uint32_t t; + + entry = &state->inode[i]; + t = entry->dest; + + for (j=0;j<entry->numreferences;j++) + { + struct ext2_buffer_head *bh; + + bh = ext2_bread(fs, entry->ref[j].block); + if (!bh) + return 0; + + if (fs->opt_debug) + { + if (PED_LE32_TO_CPU((*((uint32_t *)(bh->data + entry->ref[j].offset)))) != entry->num) + { + fprintf(stderr, + "inode %li ref error! (->%li, [%i]={%i, %i})\n", + (long) entry->num, + (long) entry->dest, + j, + entry->ref[j].block, + (int) entry->ref[j].offset); + ext2_brelse(bh, 0); + + if (numerrors++ < 4) + continue; + + fprintf(stderr, "all is not well!\n"); + return 0; + } + } + + *((uint32_t *)(bh->data + entry->ref[j].offset)) + = PED_CPU_TO_LE32(t); + bh->dirty = 1; + + ext2_brelse(bh, 0); + } + + if (entry->isdir) + { + int oldgroup; + int newgroup; + + oldgroup = (entry->num - 1) + / EXT2_SUPER_INODES_PER_GROUP(fs->sb); + newgroup = (entry->dest - 1) + / EXT2_SUPER_INODES_PER_GROUP(fs->sb); + + fs->gd[oldgroup].bg_used_dirs_count = PED_CPU_TO_LE16 ( + EXT2_GROUP_USED_DIRS_COUNT(fs->gd[oldgroup]) + - 1); + fs->gd[newgroup].bg_used_dirs_count = PED_CPU_TO_LE16 ( + EXT2_GROUP_USED_DIRS_COUNT(fs->gd[newgroup]) + + 1); + + fs->metadirty = EXT2_META_GD; + } + } + + if (fs->opt_safe) + if (!ext2_sync(fs)) + return 0; + + return 1; +} + +static int ext2_inode_relocator_grab_inodes(struct ext2_fs *fs, struct ext2_inode_relocator_state *state) +{ + int i; + int ptr; + + ptr = 0; + + for (i=0;i<fs->numgroups;i++) + if (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[i])) + { + struct ext2_buffer_head *bh; + unsigned int j; + int offset; + + bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[i])); + if (!bh) + return 0; + offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1; + + j = i ? 0 : 13; + for (;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++) + if (!(bh->data[j>>3] & _bitmap[j&7])) + { + state->inode[ptr++].dest = offset + j; + + if (ptr == state->usedentries) + { + ext2_brelse(bh, 0); + return 1; + } + } + + ext2_brelse(bh, 0); + } + + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Not enough free inodes!")); + return 0; +} + +static int ext2_inode_relocator_flush(struct ext2_fs *fs, struct ext2_inode_relocator_state *state) +{ + if (!state->usedentries) + return 1; + + if (!doscan(fs, state)) + return 0; + + if (!ext2_inode_relocator_grab_inodes(fs, state)) + return 0; + + if (!ext2_inode_relocator_copy(fs, state)) + return 0; + + if (!ext2_inode_relocator_ref(fs, state)) + return 0; + + if (!ext2_inode_relocator_finish(fs, state)) + return 0; + + state->usedentries = 0; + state->resolvedentries = 0; + state->last = (struct ext2_reference *)fs->relocator_pool_end; + + if (fs->opt_safe) + if (!ext2_sync(fs)) + return 0; + + return 1; +} + +static int ext2_inode_relocator_mark(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, ino_t inode) +{ + struct ext2_inode buf; + struct ext2_inode_entry *ent; + int i; + + if (!ext2_read_inode(fs, inode, &buf)) + return 0; + + { + register void *adv; + register void *rec; + + adv = state->inode + state->usedentries + 1; + rec = state->last - EXT2_INODE_LINKS_COUNT(buf); + + if (adv >= rec) + ext2_inode_relocator_flush(fs, state); + } + + state->last -= EXT2_INODE_LINKS_COUNT(buf); + + ent = &state->inode[state->usedentries]; + ent->num = inode; + ent->dest = 0; + ent->numreferences = EXT2_INODE_LINKS_COUNT(buf); + ent->ref = state->last; + + for (i=0;i<ent->numreferences;i++) + { + ent->ref[i].block = 0; + ent->ref[i].offset = 0; + } + + state->usedentries++; + + return 1; +} + + +int ext2_inode_relocate(struct ext2_fs *fs, int newgroups) +{ + int i; + struct ext2_inode_relocator_state state; + + if (fs->opt_verbose) + fprintf(stderr, "ext2_inode_relocate\n"); + + state.usedentries = 0; + state.resolvedentries = 0; + state.inode = (struct ext2_inode_entry *)fs->relocator_pool; + state.last = (struct ext2_reference *)fs->relocator_pool_end; + + for (i=newgroups;i<fs->numgroups;i++) + { + struct ext2_buffer_head *bh; + unsigned int j; + int offset; + + bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[i])); + if (!bh) + return 0; + offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1; + + for (j=0;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++) + if (bh->data[j>>3] & _bitmap[j&7]) + ext2_inode_relocator_mark(fs, &state, + offset + j); + + ext2_brelse(bh, 0); + } + + if (!ext2_inode_relocator_flush(fs, &state)) + return 0; + + return 1; +} +#endif /* !DISCOVER_ONLY */ + diff --git a/libparted/fs/ext2/ext2_meta.c b/libparted/fs/ext2/ext2_meta.c new file mode 100644 index 0000000..b2109ce --- /dev/null +++ b/libparted/fs/ext2/ext2_meta.c @@ -0,0 +1,146 @@ +/* + ext2_meta.c -- ext2 metadata mover + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +#include <stdio.h> +#include <stdlib.h> +#include "ext2.h" + +int ext2_metadata_push(struct ext2_fs *fs, blk_t newsize) +{ + int i; + int newgdblocks; + blk_t newitoffset; + + newgdblocks = ped_div_round_up (newsize + - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb), + EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)); + newgdblocks = ped_div_round_up (newgdblocks + * sizeof(struct ext2_group_desc), + fs->blocksize); + newitoffset = newgdblocks + 3; + + if (newitoffset <= fs->itoffset) + return 1; + + for (i=0;i<fs->numgroups;i++) + { + blk_t diff; + blk_t j; + blk_t fromblock; + blk_t start; + + start = (i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)) + + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + + if (EXT2_GROUP_INODE_TABLE(fs->gd[i]) >= start + newitoffset + && EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) >= start + newitoffset - 2 + && EXT2_GROUP_INODE_BITMAP(fs->gd[i]) >= start + newitoffset - 1) + continue; + + diff = newitoffset - (EXT2_GROUP_INODE_TABLE(fs->gd[i]) - start); + + /* inode table */ + fromblock = EXT2_GROUP_INODE_TABLE(fs->gd[i]) + fs->inodeblocks; + + if (fs->opt_debug) + { + for (j=0;j<diff;j++) + if (ext2_get_block_state(fs, fromblock+j)) + { + fprintf(stderr, + "error: block relocator " + "should have relocated " + "%i\n", + fromblock); + + return 0; + } + } + + for (j=0;j<diff;j++) + if (!ext2_set_block_state(fs, fromblock+j, 1, 0)) + return 0; + + if (!ext2_move_blocks(fs, + EXT2_GROUP_INODE_TABLE(fs->gd[i]), + fs->inodeblocks, + EXT2_GROUP_INODE_TABLE(fs->gd[i]) + diff)) + return 0; + fs->gd[i].bg_inode_table = PED_CPU_TO_LE32 ( + EXT2_GROUP_INODE_TABLE(fs->gd[i]) + diff); + fs->metadirty |= EXT2_META_GD; + + if (fs->opt_safe) + if (!ext2_sync(fs)) + return 0; + + /* block bitmap and inode bitmap */ + fromblock = EXT2_GROUP_INODE_TABLE(fs->gd[i]); + if (ext2_is_group_sparse(fs, i)) + { + if (!ext2_copy_block(fs, + EXT2_GROUP_INODE_BITMAP(fs->gd[i]), + EXT2_GROUP_INODE_BITMAP(fs->gd[i]) + diff)) + return 0; + fs->gd[i].bg_inode_bitmap = PED_CPU_TO_LE32 ( + EXT2_GROUP_INODE_BITMAP(fs->gd[i]) + diff); + fs->metadirty |= EXT2_META_GD; + + if (fs->opt_safe) + if (!ext2_sync(fs)) + return 0; + + if (!ext2_copy_block(fs, + EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]), + EXT2_GROUP_BLOCK_BITMAP(fs->gd[i])+diff)) + return 0; + fs->gd[i].bg_block_bitmap = PED_CPU_TO_LE32 ( + EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) + diff); + fs->metadirty |= EXT2_META_GD; + + if (fs->opt_safe) + if (!ext2_sync(fs)) + return 0; + + fromblock = EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]); + } + + ext2_zero_blocks(fs, fromblock-diff, diff); + for (j=0;j<diff;j++) + if (!ext2_set_block_state(fs, fromblock+j-diff, 0, 0)) + return 0; + + if (fs->opt_verbose) + fprintf(stderr, + "ext2_metadata_push: group %i/%i\r", + i+1, fs->numgroups); + } + + fs->itoffset = newitoffset; + + if (fs->opt_verbose) + fprintf(stderr, "\n"); + + return 1; +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/ext2/ext2_mkfs.c b/libparted/fs/ext2/ext2_mkfs.c new file mode 100644 index 0000000..a37e63c --- /dev/null +++ b/libparted/fs/ext2/ext2_mkfs.c @@ -0,0 +1,601 @@ +/* + ext2_mkfs.c -- ext2 fs creator + Copyright (C) 1999, 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +#define USE_EXT2_IS_DATA_BLOCK + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <uuid/uuid.h> +#include "ext2.h" + +/* formula grabbed from linux ext2 kernel source + * + * returns 1 iff: + * x == y^N, N is some natural number + * OR x == 0 + */ +static __inline__ int is_root(int x, int y) +{ + if (!x) return 1; + + while (1) + { + if (x == 1) return 1; + + if (x % y) return 0; + + x /= y; + } +} + +static __inline__ int is_group_sparse(int sparsesbfs, int group) +{ + if (!sparsesbfs) + return 1; + + if (is_root(group, 3) || is_root(group, 5) || is_root(group, 7)) + return 1; + + return 0; +} + +/* has implicit parameter 'sb' !! */ +#define is_sparse(group) is_group_sparse(EXT2_SUPER_FEATURE_RO_COMPAT(*sb) \ + & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, (group)) + +static int ext2_mkfs_write_main(struct ext2_dev_handle *handle, + struct ext2_super_block *sb, + struct ext2_group_desc *gd) +{ + int freeit; + int i; + int numgroups; + int gdblocks; + unsigned char *sbbuf; + struct ext2_super_block *sb_for_io; + + freeit = 0; + sbbuf = (unsigned char *)sb; + sb_for_io = sb; + if (EXT2_SUPER_LOG_BLOCK_SIZE(*sb)) + { + sbbuf = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)); + if (!(handle->ops->read)(handle->cookie, sbbuf, 0, 1)) + return 0; + memcpy (sbbuf+1024, sb, 1024); + freeit = 1; + sb_for_io = (struct ext2_super_block*) (sbbuf + 1024); + } + + numgroups = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(*sb) + - EXT2_SUPER_FIRST_DATA_BLOCK(*sb), + EXT2_SUPER_BLOCKS_PER_GROUP(*sb)); + gdblocks = ped_div_round_up (numgroups * sizeof(struct ext2_group_desc), + 1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)); + + for (i=0;i<numgroups;i++) + { + if (is_sparse(i)) + { + int offset; + + offset = EXT2_SUPER_FIRST_DATA_BLOCK(*sb) + + i * EXT2_SUPER_BLOCKS_PER_GROUP(*sb); + + sb_for_io->s_block_group_nr = PED_CPU_TO_LE16 (i); + + if (!handle->ops->write(handle->cookie, sbbuf, + offset, 1)) + return 0; + if (!handle->ops->write(handle->cookie, gd, offset+1, + gdblocks)) + return 0; + } + } + + sb_for_io->s_block_group_nr = 0; + + if (freeit) + ped_free(sbbuf); + return 1; +} + +static int ext2_mkfs_write_meta(struct ext2_dev_handle *handle, + struct ext2_super_block *sb, + struct ext2_group_desc *gd, + PedTimer* timer) +{ + int blocksize; + int gdtsize; + int i; + int itsize; + int numgroups; + unsigned char *bb; + unsigned char *ib; + unsigned char *zero; + + blocksize = 1 << (EXT2_SUPER_LOG_BLOCK_SIZE(*sb) + 13); + + numgroups = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(*sb) + - EXT2_SUPER_FIRST_DATA_BLOCK(*sb), + EXT2_SUPER_BLOCKS_PER_GROUP(*sb)); + itsize = ped_div_round_up (sizeof(struct ext2_inode) + * EXT2_SUPER_INODES_PER_GROUP(*sb), + (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb))); + gdtsize = ped_div_round_up (sizeof(struct ext2_group_desc) * numgroups, + (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb))); + + bb = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)); + if (!bb) goto error; + ib = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)); + if (!ib) goto error_free_bb; + zero = ped_malloc((1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)) * itsize); + if (!zero) goto error_free_zero; + + memset(zero, 0, (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)) * itsize); + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("writing per-group metadata")); + + for (i=0;i<numgroups;i++) + { + int admin; + blk_t bbblock; + int groupsize; + int groupoffset; + blk_t ibblock; + int j; + + ped_timer_update (timer, 1.0 * i / numgroups); + + groupoffset = i*EXT2_SUPER_BLOCKS_PER_GROUP(*sb) + + EXT2_SUPER_FIRST_DATA_BLOCK(*sb); + groupsize = PED_MIN(EXT2_SUPER_BLOCKS_COUNT(*sb) - groupoffset, + EXT2_SUPER_BLOCKS_PER_GROUP(*sb)); + + admin = itsize + 2; + bbblock = groupoffset; + ibblock = groupoffset + 1; + if (is_sparse(i)) + { + admin += gdtsize + 1; + bbblock = groupoffset + gdtsize + 1; + ibblock = groupoffset + gdtsize + 2; + } + + { + memset(bb, 0, 1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)); + if (is_sparse(i)) + for (j=0;j<gdtsize+1;j++) + bb[j>>3] |= _bitmap[j&7]; + + j = bbblock - groupoffset; + bb[j>>3] |= _bitmap[j&7]; + + j = ibblock - groupoffset; + bb[j>>3] |= _bitmap[j&7]; + + for (j=0;j<itsize;j++) + { + int k = j + gdtsize + 3; + + bb[k>>3] |= _bitmap[k&7]; + } + + for (j=groupsize;j<blocksize;j++) + bb[j>>3] |= _bitmap[j&7]; + + if (!handle->ops->write(handle->cookie, bb, bbblock, 1)) + goto error_free_zero; + } + + { + memset(ib, 0, 1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)); + + for (j=EXT2_SUPER_INODES_PER_GROUP(*sb);j<blocksize;j++) + bb[j>>3] |= _bitmap[j&7]; + + if (!handle->ops->write(handle->cookie, ib, ibblock, 1)) + goto error_free_zero; + } + + if (!handle->ops->write(handle->cookie, zero, + groupoffset + gdtsize + 3, itsize)) + goto error_free_zero; + + gd[i].bg_block_bitmap = PED_CPU_TO_LE32(bbblock); + gd[i].bg_inode_bitmap = PED_CPU_TO_LE32(ibblock); + gd[i].bg_inode_table = PED_CPU_TO_LE32(groupoffset + gdtsize + + 3); + gd[i].bg_free_blocks_count = PED_CPU_TO_LE16(groupsize - admin); + gd[i].bg_free_inodes_count = PED_CPU_TO_LE16( + EXT2_SUPER_INODES_PER_GROUP(*sb)); + gd[i].bg_used_dirs_count = 0; + gd[i].bg_used_dirs_count = 0; + gd[i].bg_pad = 0; + gd[i].bg_reserved[0] = 0; + gd[i].bg_reserved[1] = 0; + gd[i].bg_reserved[2] = 0; + + sb->s_free_blocks_count = PED_CPU_TO_LE32 ( + EXT2_SUPER_FREE_BLOCKS_COUNT(*sb) + + EXT2_GROUP_FREE_BLOCKS_COUNT(gd[i])); + } + + ped_timer_update (timer, 1.0); + + ped_free(zero); + ped_free(ib); + ped_free(bb); + return 1; + +error_free_zero: + ped_free(zero); +error_free_ib: + ped_free(ib); +error_free_bb: + ped_free(bb); +error: + return 0; +} + +/* returns the offset into the buffer of the start of the next dir entry */ +static int _set_dirent(void* buf, int offset, int block_size, int is_last, + uint32_t inode, char* name, int file_type) +{ + struct ext2_dir_entry_2 *dirent = (void*) (((char*)buf) + offset); + int name_len = strlen(name); + int rec_len; + + if (is_last) + rec_len = block_size - offset; + else + rec_len = ped_round_up_to(name_len + 1 + 8, 4); + + memset (dirent, 0, rec_len); + + dirent->inode = PED_CPU_TO_LE32(inode); + dirent->name_len = name_len; + dirent->rec_len = PED_CPU_TO_LE16(rec_len); + dirent->file_type = file_type; + strcpy(dirent->name, name); + + return offset + rec_len; +} + +static int ext2_mkfs_create_lost_and_found_inode(struct ext2_fs *fs) +{ + struct ext2_buffer_head *bh; + blk_t blocks[12]; + uint32_t* data = ped_malloc ((fs->blocksize / 4) * sizeof(uint32_t)); + int i; + struct ext2_inode inode; + int offset; + + for (i=0;i<12;i++) + { + if (!(blocks[i] = ext2_find_free_block(fs))) + return 0; + + if (!ext2_set_block_state(fs, blocks[i], 1, 1)) + return 0; + } + + /* create the directory entries, preallocating lots of blocks */ + /* first block contains . and .. */ + bh = ext2_bcreate(fs, blocks[0]); + if (!bh) + return 0; + memset(bh->data, 0, fs->blocksize); + offset = _set_dirent(bh->data, 0, fs->blocksize, 0, + 11, ".", EXT2_FT_DIR); + offset = _set_dirent(bh->data, offset, fs->blocksize, 1, + EXT2_ROOT_INO, "..", EXT2_FT_DIR); + bh->dirty = 1; + ext2_brelse(bh, 1); + + /* subsequent blocks are empty */ + memset(data, 0, fs->blocksize); + data[0] = 0; + data[1] = PED_CPU_TO_LE32(fs->blocksize); + for (i=1;i<12;i++) + { + bh = ext2_bcreate(fs, blocks[i]); + memcpy(bh->data, data, fs->blocksize); + bh->dirty = 1; + ext2_brelse(bh, 1); + } + + /* create inode */ + memset(&inode, 0, sizeof(struct ext2_inode)); + inode.i_mode = PED_CPU_TO_LE16(S_IFDIR | 0755); + inode.i_uid = 0; + inode.i_size = PED_CPU_TO_LE32(12 * fs->blocksize); + inode.i_atime = PED_CPU_TO_LE32(time(NULL)); + inode.i_ctime = PED_CPU_TO_LE32(time(NULL)); + inode.i_mtime = PED_CPU_TO_LE32(time(NULL)); + inode.i_dtime = 0; + inode.i_gid = 0; + inode.i_links_count = PED_CPU_TO_LE16(2); + inode.i_blocks = PED_CPU_TO_LE32((12 * fs->blocksize) >> 9); + inode.i_flags = 0; + for (i=0;i<12;i++) + inode.i_block[i] = PED_CPU_TO_LE32(blocks[i]); + + if (!ext2_write_inode(fs, 11, &inode)) + return 0; + fs->gd[0].bg_used_dirs_count = PED_CPU_TO_LE16( + EXT2_GROUP_USED_DIRS_COUNT(fs->gd[0]) + 1); + fs->metadirty |= EXT2_META_GD; + + return 1; +} + +static int ext2_mkfs_create_root_inode(struct ext2_fs *fs) +{ + struct ext2_buffer_head *bh; + blk_t block; + struct ext2_inode inode; + int offset; + + if (!(block = ext2_find_free_block(fs))) + return 0; + if (!ext2_set_block_state(fs, block, 1, 1)) + return 0; + + /* create directory entries */ + bh = ext2_bcreate(fs, block); + memset(bh->data, 0, fs->blocksize); + offset = _set_dirent(bh->data, 0, fs->blocksize, 0, + EXT2_ROOT_INO, ".", EXT2_FT_DIR); + offset = _set_dirent(bh->data, offset, fs->blocksize, 0, + EXT2_ROOT_INO, "..", EXT2_FT_DIR); + offset = _set_dirent(bh->data, offset, fs->blocksize, 1, + 11, "lost+found", EXT2_FT_DIR); + bh->dirty = 1; + if (!ext2_brelse(bh, 1)) + return 0; + + /* create inode */ + memset(&inode, 0, sizeof(struct ext2_inode)); + inode.i_mode = PED_CPU_TO_LE16(S_IFDIR | 0755); + inode.i_uid = 0; + inode.i_size = PED_CPU_TO_LE32(fs->blocksize); + inode.i_atime = PED_CPU_TO_LE32(time(NULL)); + inode.i_ctime = PED_CPU_TO_LE32(time(NULL)); + inode.i_mtime = PED_CPU_TO_LE32(time(NULL)); + inode.i_dtime = 0; + inode.i_gid = 0; + inode.i_links_count = PED_CPU_TO_LE16(3); + inode.i_blocks = PED_CPU_TO_LE32(fs->blocksize >> 9); + inode.i_flags = 0; + inode.i_block[0] = PED_CPU_TO_LE32(block); + + if (!ext2_write_inode(fs, 2, &inode)) + return 0; + fs->gd[0].bg_used_dirs_count = PED_CPU_TO_LE16 ( + EXT2_GROUP_USED_DIRS_COUNT(fs->gd[0]) + 1); + fs->metadirty |= EXT2_META_GD; + + return 1; +} + +static int ext2_reserve_inodes(struct ext2_fs *fs) +{ + int i; + + for (i=1;i<12;i++) + if (!ext2_set_inode_state(fs, i, 1, 1)) + return 0; + return 1; +} + +static int ext2_mkfs_init_sb (struct ext2_super_block *sb, blk_t numblocks, + int numgroups, int first_block, + int log_block_size, blk_t blocks_per_group, + int inodes_per_group, int sparse_sb, + int reserved_block_percentage) +{ + /* catch a bug in gcc 2.95.2 */ + PED_ASSERT(numgroups != 0, return 0); + + memset(sb, 0, 1024); + + sb->s_inodes_count = PED_CPU_TO_LE32(numgroups * inodes_per_group); + sb->s_blocks_count = PED_CPU_TO_LE32(numblocks); + sb->s_r_blocks_count = PED_CPU_TO_LE32(((uint64_t)numblocks + * reserved_block_percentage) / 100); + + /* hack: this get's inc'd as we go through each group in + * ext2_mkfs_write_meta() + */ + sb->s_free_blocks_count = 0; + sb->s_free_inodes_count = PED_CPU_TO_LE32 (numgroups + * inodes_per_group); + sb->s_first_data_block = PED_CPU_TO_LE32(first_block); + sb->s_log_block_size = PED_CPU_TO_LE32(log_block_size - 10); + sb->s_log_frag_size = sb->s_log_block_size; + sb->s_blocks_per_group = PED_CPU_TO_LE32(blocks_per_group); + sb->s_frags_per_group = PED_CPU_TO_LE32(blocks_per_group); + sb->s_inodes_per_group = PED_CPU_TO_LE32(inodes_per_group); + sb->s_mtime = 0; + sb->s_wtime = 0; + sb->s_mnt_count = 0; + sb->s_max_mnt_count = PED_CPU_TO_LE16(30); + sb->s_magic = PED_CPU_TO_LE16(0xEF53); + sb->s_state = PED_CPU_TO_LE16(EXT2_VALID_FS); + sb->s_errors = PED_CPU_TO_LE16(EXT2_ERRORS_DEFAULT); + sb->s_minor_rev_level = 0; + sb->s_lastcheck = 0; + sb->s_checkinterval = 0; + sb->s_creator_os = 0; + sb->s_rev_level = PED_CPU_TO_LE32(1); + sb->s_def_resuid = 0; + sb->s_def_resgid = 0; + sb->s_first_ino = PED_CPU_TO_LE32(11); + sb->s_inode_size = PED_CPU_TO_LE16(128); + sb->s_block_group_nr = 0; + sb->s_feature_compat = 0; + sb->s_feature_incompat = 0; + sb->s_feature_ro_compat = 0; + if (sparse_sb) + sb->s_feature_ro_compat + |= PED_CPU_TO_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER); + +/* FIXME: let the user decide? _set_dirent() assumes FILETYPE */ + sb->s_feature_incompat + |= PED_CPU_TO_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE); + + uuid_generate(sb->s_uuid); + memset(sb->s_volume_name, 0, 16); + memset(sb->s_last_mounted, 0, 64); + sb->s_algorithm_usage_bitmap = 0; + sb->s_prealloc_blocks = 0; + sb->s_prealloc_dir_blocks = 0; + sb->s_padding1 = 0; + + return 1; +} + +struct ext2_fs *ext2_mkfs(struct ext2_dev_handle *handle, + blk_t numblocks, + int log_block_size, + blk_t blocks_per_group, + int inodes_per_group, + int sparse_sb, + int reserved_block_percentage, + PedTimer* timer) +{ + struct ext2_fs *fs; + struct ext2_super_block sb; + struct ext2_group_desc *gd; + int numgroups; + int first_block; + int non_sparse_admin; + int sparse_admin; + int last_group_blocks; + int last_group_admin; + + /* if the FS is > 512Mb, use 4k blocks, otherwise 1k blocks */ + if (log_block_size == 0) { + handle->ops->set_blocksize(handle->cookie, 12); + if (handle->ops->get_size(handle->cookie) > (512 * 1024)) + log_block_size = 12; + else + log_block_size = 10; + } + + /* FIXME: block size must be > MAX(logicalbs, physicalbs) + * to avoid modify-on-write. + * -- Leslie + */ + + + handle->ops->set_blocksize(handle->cookie, log_block_size); + + if (numblocks == 0) + numblocks = handle->ops->get_size(handle->cookie); + + if (blocks_per_group == (unsigned int) 0) + blocks_per_group = 8 << log_block_size; + + first_block = (log_block_size == 10) ? 1 : 0; + + numgroups = ped_div_round_up (numblocks + - first_block, blocks_per_group); + + if (inodes_per_group == 0) + inodes_per_group = ped_round_up_to ( + numblocks / numgroups / 2, + (1 << log_block_size) / sizeof(struct ext2_inode)); + + if (sparse_sb == -1) + sparse_sb = 1; + + /* FIXME: 5% not appropriate for modern drive sizes */ + if (reserved_block_percentage == -1) + reserved_block_percentage = 5; + + last_group_blocks = (numblocks - first_block) % blocks_per_group; + if (!last_group_blocks) last_group_blocks = blocks_per_group; + non_sparse_admin = 2 + + inodes_per_group * sizeof(struct ext2_inode) + / (1 << log_block_size); + sparse_admin = non_sparse_admin + + ped_div_round_up (numgroups + * sizeof(struct ext2_group_desc), + 1 << log_block_size); + last_group_admin = is_group_sparse(sparse_sb, numgroups - 1) + ? sparse_admin : non_sparse_admin; + if (last_group_admin >= last_group_blocks) { + numgroups--; + numblocks -= last_group_blocks; + } + if (!numgroups + || (numgroups == 1 + && (last_group_blocks - last_group_admin < 8 + || inodes_per_group < 16))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("File system too small for ext2.")); + goto error; + } + + gd = ped_malloc(numgroups * sizeof(struct ext2_group_desc) + + (1 << log_block_size)); + if (!gd) + goto error; + + if (!ext2_mkfs_init_sb(&sb, numblocks, numgroups, first_block, + log_block_size, blocks_per_group, + inodes_per_group, sparse_sb, + reserved_block_percentage)) + goto error_free_gd; + if (!ext2_mkfs_write_meta(handle, &sb, gd, timer)) + goto error_free_gd; + if (!ext2_mkfs_write_main(handle, &sb, gd)) + goto error_free_gd; + + fs = ext2_open(handle, 0); + if (!fs) goto error_close_fs; + if (!ext2_reserve_inodes(fs)) goto error_close_fs; + if (!ext2_mkfs_create_root_inode(fs)) goto error_close_fs; + if (!ext2_mkfs_create_lost_and_found_inode(fs)) + goto error_close_fs; + if (!ext2_sync(fs)) goto error_close_fs; + ped_free(gd); + return fs; + +error_close_fs: + ext2_close(fs); +error_free_gd: + ped_free (gd); +error: + return NULL; +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/ext2/ext2_resize.c b/libparted/fs/ext2/ext2_resize.c new file mode 100644 index 0000000..54899f7 --- /dev/null +++ b/libparted/fs/ext2/ext2_resize.c @@ -0,0 +1,734 @@ +/* + ext2_resize.c -- ext2 resizer + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +#include <stdio.h> +#include <stdlib.h> +#include "ext2.h" + +static int ext2_add_group(struct ext2_fs *fs, blk_t groupsize) +{ + blk_t admin; + int group; + blk_t groupstart; + blk_t newgdblocks; + int sparse; + + if (fs->opt_verbose) + fprintf(stderr, "ext2_add_group\n"); + + if (!ped_realloc ((void*) &fs->gd, + (fs->numgroups+1) * sizeof(struct ext2_group_desc) + + fs->blocksize)) + return 0; + + if (fs->opt_debug) + { + if (EXT2_SUPER_BLOCKS_COUNT(fs->sb) != + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + + fs->numgroups * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)) + { + fprintf(stderr, + "ext2_add_group: last (existing) group " + "isn't complete!\n"); + + return 0; + } + } + + group = fs->numgroups; + sparse = ext2_is_group_sparse(fs, group); + groupstart = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + + group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + admin = fs->adminblocks; + if (!sparse) + admin -= fs->gdblocks + 1; + + if (fs->opt_debug) + { + if (groupsize < fs->adminblocks || + groupsize > EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)) + { + fprintf(stderr, + "ext2_add_group: groups of %i blocks are " + "impossible!\n", groupsize); + + return 0; + } + } + + newgdblocks = ped_div_round_up((fs->numgroups + 1) + * sizeof(struct ext2_group_desc), + fs->blocksize); + if (newgdblocks != fs->gdblocks) + { + int i; + + for (i=0;i<fs->numgroups;i++) + if (ext2_is_group_sparse(fs, i)) + { + blk_t start; + + start = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + + i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + ext2_set_block_state(fs, + start + fs->gdblocks + 1, 1, 1); + } + + fs->gdblocks++; + fs->adminblocks++; + if (sparse) + admin++; + } + + fs->numgroups++; + + fs->sb.s_inodes_count = PED_CPU_TO_LE32( + EXT2_SUPER_INODES_COUNT(fs->sb) + + EXT2_SUPER_INODES_PER_GROUP(fs->sb)); + fs->sb.s_blocks_count = PED_CPU_TO_LE32( + EXT2_SUPER_BLOCKS_COUNT(fs->sb) + groupsize); + fs->sb.s_free_blocks_count = PED_CPU_TO_LE32( + EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) + groupsize - admin); + fs->sb.s_free_inodes_count = PED_CPU_TO_LE32( + EXT2_SUPER_FREE_INODES_COUNT(fs->sb) + + EXT2_SUPER_INODES_PER_GROUP(fs->sb)); + fs->metadirty |= EXT2_META_SB; + + { + blk_t off; + blk_t sparseoff; + + off = groupstart; + sparseoff = off + fs->itoffset - 2; + + if (sparse) + { + fs->gd[group].bg_block_bitmap + = PED_CPU_TO_LE32(sparseoff); + fs->gd[group].bg_inode_bitmap + = PED_CPU_TO_LE32(sparseoff + 1); + } + else + { + fs->gd[group].bg_block_bitmap + = PED_CPU_TO_LE32(off); + fs->gd[group].bg_inode_bitmap + = PED_CPU_TO_LE32(off + 1); + } + + /* Hey, I don't know _why_ either */ + fs->gd[group].bg_inode_table = PED_CPU_TO_LE32(sparseoff + 2); + } + + fs->gd[group].bg_free_blocks_count = PED_CPU_TO_LE16(groupsize - admin); + fs->gd[group].bg_free_inodes_count = PED_CPU_TO_LE16( + EXT2_SUPER_INODES_PER_GROUP(fs->sb)); + fs->gd[group].bg_used_dirs_count = 0; + fs->metadirty |= EXT2_META_SB | EXT2_META_GD; + + { + struct ext2_buffer_head *bh; + blk_t i; + + bh = ext2_bcreate(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group])); + if (!bh) + return 0; + + if (sparse) + { + bh->data[0] |= _bitmap[0]; + for (i=1;i<=fs->gdblocks;i++) + bh->data[i>>3] |= _bitmap[i&7]; + } + + i = EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]) - groupstart; + bh->data[i>>3] |= _bitmap[i&7]; + + i = EXT2_GROUP_INODE_BITMAP(fs->gd[group]) - groupstart; + bh->data[i>>3] |= _bitmap[i&7]; + + for (i=0;i<fs->inodeblocks;i++) + { + blk_t j; + + j = EXT2_GROUP_INODE_TABLE(fs->gd[group]) + - groupstart + i; + bh->data[j>>3] |= _bitmap[j&7]; + } + + for (i=groupsize;i<EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);i++) + bh->data[i>>3] |= _bitmap[i&7]; + + ext2_brelse(bh, 0); /* this is a block bitmap */ + } + + if (!ext2_zero_blocks(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]), 1)) + return 0; + if (!ext2_zero_blocks(fs, EXT2_GROUP_INODE_TABLE(fs->gd[group]), + fs->inodeblocks)) + return 0; + + if (fs->opt_safe) + if (!ext2_sync(fs)) + return 0; + + return 1; +} + +static int ext2_del_group(struct ext2_fs *fs) +{ + blk_t admin; + int group; + blk_t groupsize; + blk_t newgdblocks; + int sparse; + + if (fs->opt_verbose) + fprintf(stderr, "ext2_del_group\n"); + + group = fs->numgroups - 1; + sparse = ext2_is_group_sparse(fs, group); + + admin = fs->adminblocks; + if (!sparse) + admin -= fs->gdblocks + 1; + + groupsize = EXT2_SUPER_BLOCKS_COUNT(fs->sb) + - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + - group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + if (EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) < groupsize - admin) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system is too full to remove a group!")); + + return 0; + } + + if (EXT2_SUPER_FREE_INODES_COUNT(fs->sb) + < EXT2_SUPER_INODES_PER_GROUP(fs->sb)) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has too many allocated inodes to " + "remove a group!")); + return 0; + } + + if (fs->opt_debug) + { + if (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[group]) != + EXT2_SUPER_INODES_PER_GROUP(fs->sb)) + { + fprintf(stderr, + "ext2_del_group: this should not " + "happen anymore!\n"); + + return 0; + } + } + + newgdblocks = ped_div_round_up((fs->numgroups - 1) * + sizeof(struct ext2_group_desc), fs->blocksize); + + if (newgdblocks != fs->gdblocks) + { + int i; + + for (i=0;i<fs->numgroups;i++) + if (ext2_is_group_sparse(fs, i)) + { + blk_t start; + + start = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + + i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + ext2_set_block_state(fs, + start + fs->gdblocks, + 0, 1); + } + + fs->gdblocks--; + fs->adminblocks--; + if (sparse) + admin--; + } + + if (fs->opt_debug) + { + if (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[group]) + != groupsize - admin) + { + blk_t i; + blk_t num; + blk_t offset; + + offset = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + + group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + num = EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + for (i=0;i<num;i++) + if (ext2_is_data_block(fs, offset+i) && + ext2_get_block_state(fs, offset+i)) + { + fprintf(stderr, + "error: block relocator " + "should have relocated " + "%i\n", + offset+i); + + return 0; + } + } + } + + fs->numgroups--; + + fs->sb.s_inodes_count = PED_CPU_TO_LE32( + EXT2_SUPER_INODES_COUNT(fs->sb) + - EXT2_SUPER_INODES_PER_GROUP(fs->sb)); + fs->sb.s_blocks_count = PED_CPU_TO_LE32( + EXT2_SUPER_BLOCKS_COUNT(fs->sb) - groupsize); + fs->sb.s_free_blocks_count = PED_CPU_TO_LE32( + EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) - (groupsize - admin)); + fs->sb.s_free_inodes_count = PED_CPU_TO_LE32( + EXT2_SUPER_FREE_INODES_COUNT(fs->sb) + - EXT2_SUPER_INODES_PER_GROUP(fs->sb)); + fs->metadirty |= EXT2_META_SB; + + if (fs->opt_safe) + ext2_sync(fs); + + ped_realloc ((void*) &fs->gd, + fs->numgroups * sizeof(struct ext2_group_desc) + + fs->blocksize); + + return 1; +} + +static int ext2_grow_group(struct ext2_fs *fs, blk_t newsize) +{ + int group; + blk_t groupoff; + blk_t gblocks; + blk_t i; + + if (fs->opt_verbose) + fprintf(stderr, "ext2_grow_group\n"); + + group = fs->numgroups - 1; + groupoff = group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb) + + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + gblocks = EXT2_SUPER_BLOCKS_COUNT(fs->sb) - groupoff; + + if (fs->opt_debug) + { + if (newsize < gblocks) + { + fprintf(stderr, + "ext2_grow_group: called to shrink group!\n"); + + return 0; + } + + if (gblocks == newsize) + { + fprintf(stderr, "ext2_grow_group: nothing to do!\n"); + return 0; + } + } + + for (i=gblocks;i<newsize;i++) + ext2_set_block_state(fs, groupoff + i, 0, 1); + + fs->sb.s_blocks_count = PED_CPU_TO_LE32( + EXT2_SUPER_BLOCKS_COUNT(fs->sb) + newsize - gblocks); + fs->metadirty |= EXT2_META_SB; + + if (fs->opt_safe) + ext2_sync(fs); + + return 1; +} + +static int ext2_shrink_group(struct ext2_fs *fs, blk_t newsize) +{ + blk_t admin; + int group; + blk_t groupoff; + blk_t gblocks; + blk_t i; + + if (fs->opt_verbose) + fprintf(stderr, "ext2_shrink_group\n"); + + group = fs->numgroups - 1; + + admin = fs->adminblocks; + if (!ext2_is_group_sparse(fs, group)) + admin -= fs->gdblocks + 1; + + groupoff = group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb) + + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + gblocks = EXT2_SUPER_BLOCKS_COUNT(fs->sb) - groupoff; + + if (fs->opt_debug) + { + if (newsize < admin) + { + fprintf(stderr, + "ext2_shrink_group: cant shrink a group " + "to %i blocks\n", newsize); + + return 0; + } + + if (newsize > gblocks) + { + fprintf(stderr, + "ext2_shrink_group: called to grow group!\n"); + + return 0; + } + + if (gblocks == newsize) + { + fprintf(stderr, + "ext2_shrink_group: nothing to do!\n"); + + return 0; + } + } + + for (i=newsize;i<gblocks;i++) + { + if (fs->opt_debug && ext2_get_block_state(fs, groupoff + i)) + { + fprintf(stderr, + "error: block relocator should have relocated " + "%i\n", + groupoff + i); + + return 0; + } + + ext2_set_block_state(fs, groupoff + i, 1, 0); + } + + i = gblocks - newsize; + fs->sb.s_blocks_count = PED_CPU_TO_LE32( + EXT2_SUPER_BLOCKS_COUNT(fs->sb) - i); + fs->sb.s_free_blocks_count = PED_CPU_TO_LE32( + EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) - i); + fs->gd[group].bg_free_blocks_count = PED_CPU_TO_LE16( + EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[group]) - i); + + fs->metadirty |= EXT2_META_SB | EXT2_META_GD; + + if (fs->opt_safe) + ext2_sync(fs); + + return 1; +} + + + + + + +static int ext2_grow_fs(struct ext2_fs *fs, blk_t newsize, PedTimer* timer) +{ + blk_t diff; + blk_t sizelast; + blk_t origsize = EXT2_SUPER_BLOCKS_COUNT(fs->sb); + + if (fs->opt_verbose) + fprintf(stderr, "ext2_grow_fs\n"); + + if (!ext2_block_relocate(fs, newsize)) + return 0; + + if (!ext2_metadata_push(fs, newsize)) + return 0; + + diff = newsize - EXT2_SUPER_BLOCKS_COUNT(fs->sb); + sizelast = EXT2_SUPER_BLOCKS_COUNT(fs->sb) + - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + - (fs->numgroups-1) * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + if (sizelast != EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)) + { + blk_t growto; + + growto = sizelast + diff; + if (growto > EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)) + growto = EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + if (!ext2_grow_group(fs, growto)) + return 0; + + diff -= growto - sizelast; + } + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("adding groups")); + + while (diff) + { + ped_timer_update (timer, + 1.0 - 1.0 * diff / (newsize - origsize)); + + sizelast = PED_MIN(diff, EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)); + if (!ext2_add_group(fs, sizelast)) + return 0; + + diff -= sizelast; + } + + ped_timer_update (timer, 1.0); + + return 1; +} + +static int ext2_shrink_fs(struct ext2_fs *fs, blk_t newsize, + PedTimer* timer) +{ + blk_t origsize = EXT2_SUPER_BLOCKS_COUNT (fs->sb); + blk_t diff; + int newgroups; + blk_t sizelast; + + if (fs->opt_verbose) + fprintf(stderr, "ext2_shrink_fs\n"); + + newgroups = ped_div_round_up (newsize + - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb), + EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)); + if (EXT2_SUPER_BLOCKS_COUNT(fs->sb) + - EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) > newsize) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Your file system is too full to resize it to %i " + "blocks. Sorry."), newsize); + return 0; + } + + if (EXT2_SUPER_INODES_COUNT(fs->sb) + - EXT2_SUPER_FREE_INODES_COUNT(fs->sb) + > newgroups * EXT2_SUPER_INODES_PER_GROUP(fs->sb)) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Your file system has too many occupied inodes to " + "resize it to %i blocks. Sorry."), newsize); + return 0; + } + + if (!ext2_inode_relocate(fs, newgroups)) + return 0; + + if (!ext2_block_relocate(fs, newsize)) + return 0; + + diff = EXT2_SUPER_BLOCKS_COUNT(fs->sb) - newsize; + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("shrinking")); + + while (diff > 0) + { + ped_timer_update (timer, + 1.0 - 1.0 * diff / (origsize - newsize)); + + sizelast = EXT2_SUPER_BLOCKS_COUNT(fs->sb) + - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) - + (fs->numgroups - 1) + * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + + if (diff < sizelast) + { + if (!ext2_shrink_group(fs, sizelast - diff)) + return 0; + + diff = 0; + } + else + { + if (!ext2_del_group(fs)) + return 0; + + diff -= sizelast; + } + } + + ped_timer_update (timer, 1.0); + + return 1; +} + +int ext2_determine_itoffset(struct ext2_fs *fs) +{ + int i; + + fs->itoffset = EXT2_GROUP_INODE_TABLE(fs->gd[0]) + - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); + + /*PED_DEBUG (0x20, "itoffset is %d", fs->itoffset); + + PED_DEBUG (0x20, "walking %d groups", fs->numgroups);*/ + + for (i=0;i<fs->numgroups;i++) + { + blk_t start; + blk_t bb; + blk_t ib; + blk_t it; + + start = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + + (i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)); + it = start + fs->itoffset; + + /*PED_DEBUG (0x21, "start = %d, it = %d", start, it);*/ + + if (ext2_is_group_sparse(fs, i)) + { + /*PED_DEBUG (0x21, "%d has a superblock copy", i);*/ + bb = it - 2; + ib = it - 1; + } + else + { + /*PED_DEBUG (0x21, "%d doesn't have a superblock copy", + i);*/ + bb = start; + ib = start + 1; + } + + if (EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) != bb || + EXT2_GROUP_INODE_BITMAP(fs->gd[i]) != ib || + EXT2_GROUP_INODE_TABLE(fs->gd[i]) != it) + { + /* ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("This ext2 file system has a rather strange layout! " + "Parted can't resize this (yet)."));*/ + + /* PED_DEBUG (0x21, "calculated block bitmap to be %d, " + "but fs says %d.", bb, + EXT2_GROUP_BLOCK_BITMAP(fs->gd[i])); + PED_DEBUG (0x21, "calculated inode bitmap to be %d, " + "but fs says %d.", ib, + EXT2_GROUP_INODE_BITMAP(fs->gd[i])); + PED_DEBUG (0x21, "calculated inode table to be %d, " + "but fs says %d.", it, + EXT2_GROUP_INODE_TABLE(fs->gd[i]));*/ + + return 0; + } + } + + return 1; +} + +int ext2_resize_fs(struct ext2_fs *fs, blk_t newsize, PedTimer* timer) +{ + blk_t residue; + int status; + + if (EXT2_SUPER_STATE(fs->sb) & EXT2_ERROR_FS) + { + ped_exception_throw ( + PED_EXCEPTION_WARNING, PED_EXCEPTION_CANCEL, + _("File system has errors! You should run e2fsck.")); + return 0; + } + + if (!(EXT2_SUPER_STATE(fs->sb) & EXT2_VALID_FS)) + { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system was not cleanly unmounted! " + "You should run e2fsck.")); + return 0; + } + + if (EXT2_SUPER_FEATURE_COMPAT(fs->sb) + & EXT2_FEATURE_COMPAT_HAS_DIR_INDEX) { + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE_CANCEL, + _("The file system has the 'dir_index' feature " + "enabled. Parted can only resize the file system " + "if it disables this feature. You can enable it " + "later by running 'tune2fs -O dir_index DEVICE' " + "and then 'e2fsck -fD DEVICE'.")) + != PED_EXCEPTION_IGNORE) + return 0; + fs->sb.s_feature_compat + = PED_CPU_TO_LE32(EXT2_SUPER_FEATURE_COMPAT(fs->sb) + & ~EXT2_FEATURE_COMPAT_HAS_DIR_INDEX); + fs->metadirty |= EXT2_META_SB; + } + + if (!ext2_determine_itoffset(fs) && ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK_CANCEL, + _("A resize operation on this file system will " + "use EXPERIMENTAL code\n" + "that MAY CORRUPT it (although it hasn't done" + "so yet\n" + "in the past).\n" + "You should at least backup your data and " + "run 'e2fsck -f' afterwards.")) + == PED_EXCEPTION_CANCEL) + { + return 0; + } + + if (fs->opt_verbose) + fprintf(stderr, "ext2_resize_fs\n"); + + residue = (newsize - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)) + % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb); + if (residue && residue <= fs->adminblocks) + newsize -= residue; + + if (newsize == EXT2_SUPER_BLOCKS_COUNT(fs->sb)) + return 1; + + fs->relocator_pool + = (unsigned char *)ped_malloc(ext2_relocator_pool_size << 10); + if (!fs->relocator_pool) + return 0; + fs->relocator_pool_end + = fs->relocator_pool + (ext2_relocator_pool_size << 10); + + if (newsize < EXT2_SUPER_BLOCKS_COUNT(fs->sb)) + status = ext2_shrink_fs(fs, newsize, timer); + else + status = ext2_grow_fs(fs, newsize, timer); + + ped_free(fs->relocator_pool); + fs->relocator_pool = NULL; + fs->relocator_pool_end = NULL; + + return status; +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/ext2/interface.c b/libparted/fs/ext2/interface.c new file mode 100644 index 0000000..7b48f4c --- /dev/null +++ b/libparted/fs/ext2/interface.c @@ -0,0 +1,355 @@ +/* + interface.c -- parted binding glue to libext2resize + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* VERSION: libext2resize 1.1.6 (by Lennert) + * merged 1.1.11 changes (by Andrew) + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include "ext2.h" +#include "parted_io.h" + +static PedFileSystemType _ext2_type; +static PedFileSystemType _ext3_type; + +struct ext2_dev_handle* ext2_make_dev_handle_from_parted_geometry(PedGeometry* geom); + +static PedGeometry* +_ext2_generic_probe (PedGeometry* geom, int expect_ext3) +{ + struct ext2_super_block sb; + + if (!ped_geometry_read(geom, &sb, 2, 2)) + return NULL; + + if (EXT2_SUPER_MAGIC(sb) == EXT2_SUPER_MAGIC_CONST) { + PedSector block_size = 1 << (EXT2_SUPER_LOG_BLOCK_SIZE(sb) + 1); + PedSector block_count = EXT2_SUPER_BLOCKS_COUNT(sb); + PedSector group_blocks = EXT2_SUPER_BLOCKS_PER_GROUP(sb); + PedSector group_nr = EXT2_SUPER_BLOCK_GROUP_NR(sb); + PedSector first_data_block = EXT2_SUPER_FIRST_DATA_BLOCK(sb); + int version = EXT2_SUPER_REV_LEVEL(sb); + int is_ext3 = (EXT2_SUPER_FEATURE_COMPAT(sb) + & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0; + + if (expect_ext3 != is_ext3) + return NULL; + + if (version > 0 && group_nr > 0) { + PedSector start; + PedGeometry probe_geom; + + start = geom->start + - group_blocks * group_nr + - first_data_block; + + if (start < 0) + return NULL; + ped_geometry_init (&probe_geom, geom->dev, + start, block_count * block_size); + return _ext2_generic_probe (&probe_geom, expect_ext3); + } else { + return ped_geometry_new (geom->dev, geom->start, + block_count * block_size); + } + } + return NULL; +} + +static PedGeometry* +_ext2_probe (PedGeometry* geom) +{ + return _ext2_generic_probe (geom, 0); +} + +static PedGeometry* +_ext3_probe (PedGeometry* geom) +{ + return _ext2_generic_probe (geom, 1); +} + +#ifndef DISCOVER_ONLY +static int +_ext2_clobber (PedGeometry* geom) +{ + struct ext2_super_block sb; + + if (!ped_geometry_read(geom, &sb, 2, 2)) + return 0; + if (EXT2_SUPER_MAGIC(sb) != EXT2_SUPER_MAGIC_CONST) + return 1; + + sb.s_magic = 0; + return ped_geometry_write(geom, &sb, 2, 2); +} + +static PedFileSystem* +_ext2_open (PedGeometry* geom) +{ + PedFileSystem* fs; + struct ext2_fs* fs_info; + struct ext2_dev_handle* handle; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) goto error; + + fs->type = &_ext2_type; + fs->geom = ped_geometry_duplicate (geom); + fs->checked = 1; + + handle = ext2_make_dev_handle_from_parted_geometry(fs->geom); + if (!handle) goto error_free_fs; + + fs_info = (struct ext2_fs*) ext2_open(handle, 0); + if (!fs_info) goto error_free_handle; + + fs->type_specific = (void*) fs_info; + fs_info->opt_verbose = 0; + + return fs; + +error_free_handle: + ext2_destroy_dev_handle(handle); +error_free_fs: + ped_free(fs); +error: + return NULL; +} + +static PedFileSystem* +_ext2_create (PedGeometry* geom, PedTimer* timer) +{ + PedFileSystem* fs; + struct ext2_fs* fs_info; + struct ext2_dev_handle* handle; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) goto error; + + fs->type = &_ext2_type; + fs->geom = ped_geometry_duplicate (geom); + + handle = ext2_make_dev_handle_from_parted_geometry(fs->geom); + if (!handle) goto error_free_fs; + + fs_info = ext2_mkfs (handle, 0, 0, 0, 0, -1, -1, timer); + if (!fs_info) goto error_free_handle; + + fs->type_specific = (void*) fs_info; + fs_info->opt_verbose = 0; + + return fs; + +error_free_handle: + ext2_destroy_dev_handle(handle); +error_free_fs: + ped_free(fs); +error: + return NULL; +} + +static int +_ext2_close (PedFileSystem *fs) +{ + struct ext2_dev_handle* handle; + + handle = ((struct ext2_fs*)fs->type_specific)->devhandle; + ext2_close(fs->type_specific); + ext2_destroy_dev_handle(handle); + + ped_free(fs); + return 1; +} + +static int +_ext2_check (PedFileSystem *fs, PedTimer* timer) +{ + ped_exception_throw (PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK, + _("The ext2 file system passed a basic check. For a more " + "comprehensive check, use the e2fsck program.")); + return 1; +} + +static int +_ext2_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + struct ext2_fs* f; + PedSector old_length = fs->geom->length; + + PED_ASSERT (fs->geom->dev == geom->dev, return 0); + + if (fs->geom->start != geom->start) + { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, can't move the start of ext2 partitions yet!")); + return 0; + } + + geom->dev->boot_dirty = 1; + + f = (struct ext2_fs *) fs->type_specific; + +/* ensure that the geometry contains the new and old geometry */ + if (old_length > geom->length) { + if (!ext2_resize_fs(f, geom->length >> (f->logsize - 9), + timer)) + goto error; + + fs->geom->length = geom->length; + fs->geom->end = fs->geom->start + geom->length - 1; + } else { + fs->geom->length = geom->length; + fs->geom->end = fs->geom->start + geom->length - 1; + + if (!ext2_resize_fs(f, geom->length >> (f->logsize - 9), + timer)) + goto error; + } + return 1; + +error: + return 0; +} + +static PedConstraint* +_ext2_get_create_constraint (const PedDevice* dev) +{ + PedGeometry full_dev; + + 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, + 64, dev->length); +} + +static PedConstraint* +_ext2_get_resize_constraint (const PedFileSystem* fs) +{ + struct ext2_fs* f = (struct ext2_fs *) fs->type_specific; + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + min_size = (EXT2_SUPER_BLOCKS_COUNT(f->sb) + - EXT2_SUPER_FREE_BLOCKS_COUNT(f->sb)) + * (f->blocksize / dev->sector_size); + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + dev->length); +} +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps _ext2_ops = { + probe: _ext2_probe, +#ifndef DISCOVER_ONLY + clobber: _ext2_clobber, + open: _ext2_open, + create: _ext2_create, + close: _ext2_close, + check: _ext2_check, + resize: _ext2_resize, + copy: NULL, + get_create_constraint: _ext2_get_create_constraint, + get_copy_constraint: NULL, + get_resize_constraint: _ext2_get_resize_constraint +#else /* !DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +#endif /* !DISCOVER_ONLY */ +}; + +static PedFileSystemOps _ext3_ops = { + probe: _ext3_probe, +#ifndef DISCOVER_ONLY + clobber: _ext2_clobber, + open: _ext2_open, + create: NULL, + close: _ext2_close, + check: _ext2_check, + resize: _ext2_resize, + copy: NULL, + get_create_constraint: _ext2_get_create_constraint, + get_copy_constraint: NULL, + get_resize_constraint: _ext2_get_resize_constraint +#else /* !DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_copy_constraint: NULL, + get_resize_constraint: NULL +#endif /* !DISCOVER_ONLY */ +}; + +#define EXT23_BLOCK_SIZES ((int[6]){512, 1024, 2048, 4096, 8192, 0}) + +static PedFileSystemType _ext2_type = { + next: NULL, + ops: &_ext2_ops, + name: "ext2", + block_sizes: EXT23_BLOCK_SIZES +}; + +static PedFileSystemType _ext3_type = { + next: NULL, + ops: &_ext3_ops, + name: "ext3", + block_sizes: EXT23_BLOCK_SIZES +}; + +void ped_file_system_ext2_init () +{ + ped_file_system_type_register (&_ext2_type); + ped_file_system_type_register (&_ext3_type); +} + +void ped_file_system_ext2_done () +{ + ped_file_system_type_unregister (&_ext2_type); + ped_file_system_type_unregister (&_ext3_type); +} diff --git a/libparted/fs/ext2/parted_io.c b/libparted/fs/ext2/parted_io.c new file mode 100644 index 0000000..f23ad45 --- /dev/null +++ b/libparted/fs/ext2/parted_io.c @@ -0,0 +1,136 @@ +/* + parted_io.c -- parted I/O code interface for libext2resize + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +#include <parted/parted.h> +#include <stdio.h> +#include <stdlib.h> +#include "ext2.h" + +/* pseudo-header.... */ + +loff_t llseek(unsigned int fd, loff_t offset, unsigned int whence); + +struct my_cookie +{ + int logsize; + PedGeometry* geom; +}; + +/* ...then this must be pseudo-code :-) */ + +static int do_close (void *cookie); +static int do_sync (void *cookie); +static blk_t do_get_size (void *cookie); +static int do_read (void *cookie, void *ptr, blk_t block, blk_t numblocks); +static int do_set_blocksize(void *cookie, int logsize); +static int do_write (void *cookie, void *ptr, blk_t block, blk_t numblocks); + +struct ext2_dev_ops ops = +{ + close: do_close, + get_size: do_get_size, + read: do_read, + set_blocksize: do_set_blocksize, + sync: do_sync, + write: do_write +}; + + + +static int do_close(void *cookie) +{ + struct my_cookie *monster = cookie; + + return ped_geometry_sync(monster->geom); +} + +static int do_sync(void *cookie) +{ + struct my_cookie *monster = cookie; + + return ped_geometry_sync(monster->geom); +} + +static blk_t do_get_size(void *cookie) +{ + struct my_cookie *monster = cookie; + + return monster->geom->length >> (monster->logsize - 9); +} + +static int do_read(void *cookie, void *ptr, blk_t block, blk_t num) +{ + struct my_cookie *monster = cookie; + + return ped_geometry_read(monster->geom, ptr, block << (monster->logsize - 9), num << (monster->logsize - 9)); +} + +static int do_set_blocksize(void *cookie, int logsize) +{ + struct my_cookie *monster = cookie; + + monster->logsize = logsize; + return 1; +} + +static int do_write(void *cookie, void *ptr, blk_t block, blk_t num) +{ + struct my_cookie *monster = cookie; + + return ped_geometry_write(monster->geom, ptr, + block << (monster->logsize - 9), + num << (monster->logsize - 9)); +} + + +struct ext2_dev_handle *ext2_make_dev_handle_from_parted_geometry(PedGeometry* geom) +{ + struct ext2_dev_handle *dh; + struct my_cookie *monster; + + if ((dh = ped_malloc(sizeof(struct ext2_dev_handle))) == NULL) + goto error; + + if ((monster = ped_malloc(sizeof(struct my_cookie))) == NULL) + goto error_free_dh; + + dh->ops = &ops; + dh->cookie = monster; + monster->logsize = 9; + monster->geom = geom; + + return dh; + +error_free_dh: + ped_free(dh); +error: + return NULL; +} + +void ext2_destroy_dev_handle(struct ext2_dev_handle *handle) +{ + ped_geometry_destroy(((struct my_cookie *)handle->cookie)->geom); + ped_free(handle->cookie); + ped_free(handle); +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/ext2/parted_io.h b/libparted/fs/ext2/parted_io.h new file mode 100644 index 0000000..f796138 --- /dev/null +++ b/libparted/fs/ext2/parted_io.h @@ -0,0 +1,28 @@ +/* + parted_io.h + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef _PARTED_IO_H +#define _PARTED_IO_H + +#include "ext2.h" + +void ext2_destroy_dev_handle(struct ext2_dev_handle *handle); + +#endif + diff --git a/libparted/fs/ext2/tune.c b/libparted/fs/ext2/tune.c new file mode 100644 index 0000000..980bd3c --- /dev/null +++ b/libparted/fs/ext2/tune.c @@ -0,0 +1,40 @@ +/* + tune.c -- tuneable stuff + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifndef DISCOVER_ONLY + +/* + * maybe i'll make this all command-line configurable one day + */ + +/* The size of the buffer cache in kilobytes. Note that this is only + the actual buffer memory. On top of this amount additional memory + will be allocated for buffer cache bookkeeping. */ +int ext2_buffer_cache_pool_size = 512; + +/* The size of the buffer cache hash table (log2 of # of buckets). */ +int ext2_hash_bits = 8; + +/* The block/inode relocator pool size in kilobytes. Make this as big + as you can. The smaller this is, the more disk I/O is required for + doing relocations. */ +int ext2_relocator_pool_size = 4096; +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/ext2/tune.h b/libparted/fs/ext2/tune.h new file mode 100644 index 0000000..f6adece --- /dev/null +++ b/libparted/fs/ext2/tune.h @@ -0,0 +1,30 @@ +/* + tune.h -- ext2 tunables header + Copyright (C) 1998-2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef _TUNE_H +#define _TUNE_H + +#define MAXCONT 256 + +extern int ext2_buffer_cache_pool_size; +extern int ext2_hash_bits; +extern int ext2_max_groups; +extern int ext2_relocator_pool_size; + +#endif diff --git a/libparted/fs/fat/Makefile.am b/libparted/fs/fat/Makefile.am new file mode 100644 index 0000000..a90cf7a --- /dev/null +++ b/libparted/fs/fat/Makefile.am @@ -0,0 +1,25 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libfat.la +libfat_la_SOURCES = bootsector.c \ + bootsector.h \ + calc.c \ + calc.h \ + clstdup.c \ + clstdup.h \ + context.c \ + context.h \ + count.c \ + count.h \ + fat.c \ + fat.h \ + fatio.c \ + fatio.h \ + table.c \ + table.h \ + traverse.c \ + traverse.h \ + resize.c + +INCLUDES = $(partedincludedir) @INTLINCS@ + diff --git a/libparted/fs/fat/bootsector.c b/libparted/fs/fat/bootsector.c new file mode 100644 index 0000000..10426cd --- /dev/null +++ b/libparted/fs/fat/bootsector.c @@ -0,0 +1,449 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000, 2002, 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "fat.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +/* Reads in the boot sector (superblock), and does a minimum of sanity + * checking. The goals are: + * - to detect fat file systems, even if they are damaged [i.e. not + * return an error / throw an exception] + * - to fail detection if there's not enough information for + * fat_boot_sector_probe_type() to work (or possibly crash on a divide-by-zero) + */ +int +fat_boot_sector_read (FatBootSector* bs, const PedGeometry *geom) +{ + PED_ASSERT (bs != NULL, return 0); + PED_ASSERT (geom != NULL, return 0); + + if (!ped_geometry_read (geom, bs, 0, 1)) + return 0; + + if (PED_LE16_TO_CPU (bs->boot_sign) != 0xAA55) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid signature for a FAT " + "file system.")); + return 0; + } + + if (!bs->system_id[0]) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid signature for a FAT " + "file system.")); + return 0; + } + + if (!bs->sector_size || PED_LE16_TO_CPU (bs->sector_size) % 512) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid sector size for a FAT " + "file system.")); + return 0; + } + + if (!bs->cluster_size) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid cluster size for a FAT " + "file system.")); + return 0; + } + + if (!bs->reserved) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid number of reserved " + "sectors for a FAT file system.")); + return 0; + } + + if (bs->fats < 1 || bs->fats > 4) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid number of FATs.")); + return 0; + } + + return 1; +} + +/* + Don't trust the FAT12, FAT16 or FAT32 label string. + */ +FatType +fat_boot_sector_probe_type (const FatBootSector* bs, const PedGeometry* geom) +{ + PedSector logical_sector_size; + PedSector first_cluster_sector; + FatCluster cluster_count; + + if (!PED_LE16_TO_CPU (bs->dir_entries)) + return FAT_TYPE_FAT32; + + logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512; + + first_cluster_sector + = PED_LE16_TO_CPU (bs->reserved) * logical_sector_size + + 2 * PED_LE16_TO_CPU (bs->fat_length) * logical_sector_size + + PED_LE16_TO_CPU (bs->dir_entries) + / (512 / sizeof (FatDirEntry)); + cluster_count = (geom->length - first_cluster_sector) + / bs->cluster_size / logical_sector_size; + if (cluster_count > MAX_FAT12_CLUSTERS) + return FAT_TYPE_FAT16; + else + return FAT_TYPE_FAT12; +} + +/* Analyses the boot sector, and sticks appropriate numbers in + fs->type_specific. + + Note: you need to subtract (2 * cluster_sectors) off cluster offset, + because the first cluster is number 2. (0 and 1 are not real clusters, + and referencing them is a bug) + */ +int +fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedExceptionOption ex_status; + int fat_entry_size; + + PED_ASSERT (bs != NULL, return 0); + + if (PED_LE16_TO_CPU (bs->sector_size) != 512) { + if (ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_IGNORE_CANCEL, + _("This file system has a logical sector size of %d. " + "GNU Parted is known not to work properly with sector " + "sizes other than 512 bytes."), + (int) PED_LE16_TO_CPU (bs->sector_size)) + != PED_EXCEPTION_IGNORE) + return 0; + } + + fs_info->logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512; + + fs_info->sectors_per_track = PED_LE16_TO_CPU (bs->secs_track); + fs_info->heads = PED_LE16_TO_CPU (bs->heads); + if (fs_info->sectors_per_track < 1 || fs_info->sectors_per_track > 63 + || fs_info->heads < 1 || fs_info->heads > 255) { + PedCHSGeometry* bios_geom = &fs->geom->dev->bios_geom; + int cyl_count = 0; + + if (fs_info->heads > 0 && fs_info->sectors_per_track > 0) + cyl_count = fs->geom->dev->length / fs_info->heads + / fs_info->sectors_per_track; + + switch (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX + PED_EXCEPTION_IGNORE + + PED_EXCEPTION_CANCEL, + _("The file system's CHS geometry is (%d, %d, %d), " + "which is invalid. The partition table's CHS " + "geometry is (%d, %d, %d). If you select Ignore, " + "the file system's CHS geometry will be left " + "unchanged. If you select Fix, the file system's " + "CHS geometry will be set to match the partition " + "table's CHS geometry."), + cyl_count, fs_info->heads, fs_info->sectors_per_track, + bios_geom->cylinders, bios_geom->heads, + bios_geom->sectors)) { + + case PED_EXCEPTION_FIX: + fs_info->sectors_per_track = bios_geom->sectors; + fs_info->heads = bios_geom->heads; + bs->secs_track + = PED_CPU_TO_LE16 (fs_info->sectors_per_track); + bs->heads = PED_CPU_TO_LE16 (fs_info->heads); + if (!fat_boot_sector_write (bs, fs)) + return 0; + break; + + case PED_EXCEPTION_CANCEL: + return 0; + + case PED_EXCEPTION_IGNORE: + break; + } + } + + if (bs->sectors) + fs_info->sector_count = PED_LE16_TO_CPU (bs->sectors) + * fs_info->logical_sector_size; + else + fs_info->sector_count = PED_LE32_TO_CPU (bs->sector_count) + * fs_info->logical_sector_size; + + fs_info->fat_table_count = bs->fats; + fs_info->root_dir_entry_count = PED_LE16_TO_CPU (bs->dir_entries); + fs_info->fat_offset = PED_LE16_TO_CPU (bs->reserved) + * fs_info->logical_sector_size; + fs_info->cluster_sectors = bs->cluster_size + * fs_info->logical_sector_size; + fs_info->cluster_size = fs_info->cluster_sectors * 512; + + if (fs_info->logical_sector_size == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says logical sector size is 0. " + "This is weird. ")); + return 0; + } + if (fs_info->fat_table_count == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says there are no FAT tables. This " + "is weird. ")); + return 0; + } + if (fs_info->cluster_sectors == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says clusters are 0 sectors. This " + "is weird. ")); + return 0; + } + + fs_info->fat_type = fat_boot_sector_probe_type (bs, fs->geom); + if (fs_info->fat_type == FAT_TYPE_FAT12) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("File system is FAT12, which is unsupported.")); + return 0; + } + if (fs_info->fat_type == FAT_TYPE_FAT16) { + fs_info->fat_sectors = PED_LE16_TO_CPU (bs->fat_length) + * fs_info->logical_sector_size; + fs_info->serial_number + = PED_LE32_TO_CPU (bs->u.fat16.serial_number); + fs_info->root_cluster = 0; + fs_info->root_dir_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + fs_info->root_dir_sector_count + = fs_info->root_dir_entry_count * sizeof (FatDirEntry) + / (512 * fs_info->logical_sector_size); + fs_info->cluster_offset + = fs_info->root_dir_offset + + fs_info->root_dir_sector_count; + } + if (fs_info->fat_type == FAT_TYPE_FAT32) { + fs_info->fat_sectors = PED_LE32_TO_CPU (bs->u.fat32.fat_length) + * fs_info->logical_sector_size; + fs_info->serial_number + = PED_LE32_TO_CPU (bs->u.fat32.serial_number); + fs_info->info_sector_offset + = PED_LE16_TO_CPU (fs_info->boot_sector.u.fat32.info_sector) + * fs_info->logical_sector_size; + fs_info->boot_sector_backup_offset + = PED_LE16_TO_CPU (fs_info->boot_sector.u.fat32.backup_sector) + * fs_info->logical_sector_size; + fs_info->root_cluster + = PED_LE32_TO_CPU (bs->u.fat32.root_dir_cluster); + fs_info->root_dir_offset = 0; + fs_info->root_dir_sector_count = 0; + fs_info->cluster_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + } + + fs_info->cluster_count + = (fs_info->sector_count - fs_info->cluster_offset) + / fs_info->cluster_sectors; + + fat_entry_size = fat_table_entry_size (fs_info->fat_type); + if (fs_info->cluster_count + 2 + > fs_info->fat_sectors * 512 / fat_entry_size) + fs_info->cluster_count + = fs_info->fat_sectors * 512 / fat_entry_size - 2; + + fs_info->dir_entries_per_cluster + = fs_info->cluster_size / sizeof (FatDirEntry); + return 1; +} + +#ifndef DISCOVER_ONLY +int +fat_boot_sector_set_boot_code (FatBootSector* bs) +{ + PED_ASSERT (bs != NULL, return 0); + + memset (bs, 0, 512); + memcpy (bs->boot_jump, FAT_BOOT_JUMP, 3); + memcpy (bs->u.fat32.boot_code, FAT_BOOT_CODE, FAT_BOOT_CODE_LENGTH); + return 1; +} + +int +fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (bs != NULL, return 0); + + memcpy (bs->system_id, "MSWIN4.1", 8); + bs->sector_size = PED_CPU_TO_LE16 (fs_info->logical_sector_size * 512); + bs->cluster_size = fs_info->cluster_sectors + / fs_info->logical_sector_size; + bs->reserved = PED_CPU_TO_LE16 (fs_info->fat_offset + / fs_info->logical_sector_size); + bs->fats = fs_info->fat_table_count; + + bs->dir_entries = (fs_info->fat_type == FAT_TYPE_FAT16) + ? PED_CPU_TO_LE16 (fs_info->root_dir_entry_count) + : 0; + + if (fs_info->sector_count / fs_info->logical_sector_size > 0xffff + || fs_info->fat_type == FAT_TYPE_FAT32) { + bs->sectors = 0; + bs->sector_count = PED_CPU_TO_LE32 (fs_info->sector_count + / fs_info->logical_sector_size); + } else { + bs->sectors = PED_CPU_TO_LE16 (fs_info->sector_count + / fs_info->logical_sector_size); + bs->sector_count = 0; + } + + bs->media = 0xf8; + + bs->secs_track = PED_CPU_TO_LE16 (fs_info->sectors_per_track); + bs->heads = PED_CPU_TO_LE16 (fs_info->heads); + bs->hidden = PED_CPU_TO_LE32 (fs->geom->start); + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + bs->fat_length = 0; + bs->u.fat32.fat_length = PED_CPU_TO_LE32 (fs_info->fat_sectors + / fs_info->logical_sector_size); + bs->u.fat32.flags = 0; /* FIXME: what the hell are these? */ + bs->u.fat32.version = 0; /* must be 0, for Win98 bootstrap */ + bs->u.fat32.root_dir_cluster + = PED_CPU_TO_LE32 (fs_info->root_cluster); + bs->u.fat32.info_sector + = PED_CPU_TO_LE16 (fs_info->info_sector_offset + / fs_info->logical_sector_size); + bs->u.fat32.backup_sector + = PED_CPU_TO_LE16 (fs_info->boot_sector_backup_offset + / fs_info->logical_sector_size); + + bs->u.fat32.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */ + + memset (bs->u.fat32.empty_1, 0, 12); + + bs->u.fat32.ext_signature = 0x29; + bs->u.fat32.serial_number + = PED_CPU_TO_LE32 (fs_info->serial_number); + memcpy (bs->u.fat32.volume_name, "NO NAME ", 11); + memcpy (bs->u.fat32.fat_name, "FAT32 ", 8); + } else { + bs->fat_length + = PED_CPU_TO_LE16 (fs_info->fat_sectors + / fs_info->logical_sector_size); + + bs->u.fat16.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */ + + bs->u.fat16.ext_signature = 0x29; + bs->u.fat16.serial_number + = PED_CPU_TO_LE32 (fs_info->serial_number); + memcpy (bs->u.fat16.volume_name, "NO NAME ", 11); + memcpy (bs->u.fat16.fat_name, "FAT16 ", 8); + } + + bs->boot_sign = PED_CPU_TO_LE16 (0xaa55); + + return 1; +} + +int +fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (bs != NULL, return 0); + + if (!ped_geometry_write (fs->geom, bs, 0, 1)) + return 0; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!ped_geometry_write (fs->geom, bs, + fs_info->boot_sector_backup_offset, 1)) + return 0; + } + return ped_geometry_sync (fs->geom); +} + +int +fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int status; + + PED_ASSERT (is != NULL, return 0); + + if (!ped_geometry_read (fs->geom, is, fs_info->info_sector_offset, 1)) + return 0; + + if (PED_LE32_TO_CPU (is->signature_2) != FAT32_INFO_MAGIC2) { + status = ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The information sector has the wrong " + "signature (%x). Select cancel for now, " + "and send in a bug report. If you're " + "desperate, it's probably safe to ignore."), + PED_LE32_TO_CPU (is->signature_2)); + if (status == PED_EXCEPTION_CANCEL) return 0; + } + return 1; +} + +int +fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (is != NULL, return 0); + + fat_table_count_stats (fs_info->fat); + + memset (is, 0, 512); + + is->signature_1 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC1); + is->signature_2 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC2); + is->free_clusters = PED_CPU_TO_LE32 (fs_info->fat->free_cluster_count); + is->next_cluster = PED_CPU_TO_LE32 (fs_info->fat->last_alloc); + is->signature_3 = PED_CPU_TO_LE16 (FAT32_INFO_MAGIC3); + + return 1; +} + +int +fat_info_sector_write (const FatInfoSector* is, PedFileSystem *fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (is != NULL, return 0); + + if (!ped_geometry_write (fs->geom, is, fs_info->info_sector_offset, 1)) + return 0; + return ped_geometry_sync (fs->geom); +} +#endif /* !DISCOVER_ONLY */ + diff --git a/libparted/fs/fat/bootsector.h b/libparted/fs/fat/bootsector.h new file mode 100644 index 0000000..95f1c10 --- /dev/null +++ b/libparted/fs/fat/bootsector.h @@ -0,0 +1,138 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef PED_FAT_BOOTSECTOR_H +#define PED_FAT_BOOTSECTOR_H + +typedef struct _FatBootSector FatBootSector; +typedef struct _FatInfoSector FatInfoSector; + +#include "fat.h" + +#define FAT32_INFO_MAGIC1 0x41615252 +#define FAT32_INFO_MAGIC2 0x61417272 +#define FAT32_INFO_MAGIC3 0xaa55 + +/* stolen from mkdosfs, by Dave Hudson */ + +#define FAT_BOOT_MESSAGE \ +"This partition does not have an operating system loader installed on it.\n\r"\ +"Press a key to reboot..." + +#define FAT_BOOT_JUMP "\xeb\x58\x90" /* jmp +5a */ + +#define FAT_BOOT_CODE "\x0e" /* push cs */ \ + "\x1f" /* pop ds */ \ + "\xbe\x74\x7e" /* mov si, offset message */ \ + /* write_msg_loop: */ \ + "\xac" /* lodsb */ \ + "\x22\xc0" /* and al, al */ \ + "\x74\x06" /* jz done (+8) */ \ + "\xb4\x0e" /* mov ah, 0x0e */ \ + "\xcd\x10" /* int 0x10 */ \ + "\xeb\xf5" /* jmp write_msg_loop */ \ + /* done: */ \ + "\xb4\x00" /* mov ah, 0x00 */ \ + "\xcd\x16" /* int 0x16 */ \ + "\xb4\x00" /* mov ah, 0x00 */ \ + "\xcd\x19" /* int 0x19 */ \ + "\xeb\xfe" /* jmp +0 - in case int 0x19 */ \ + /* doesn't work */ \ + /* message: */ \ + FAT_BOOT_MESSAGE + +#define FAT_BOOT_CODE_LENGTH 128 + +struct _FatBootSector { + uint8_t boot_jump[3]; /* 00: Boot strap short or near jump */ + uint8_t system_id[8]; /* 03: system name */ + uint16_t sector_size; /* 0b: bytes per logical sector */ + uint8_t cluster_size; /* 0d: sectors/cluster */ + uint16_t reserved; /* 0e: reserved sectors */ + uint8_t fats; /* 10: number of FATs */ + uint16_t dir_entries; /* 11: number of root directory entries */ + uint16_t sectors; /* 13: if 0, total_sect supersedes */ + uint8_t media; /* 15: media code */ + uint16_t fat_length; /* 16: sectors/FAT for FAT12/16 */ + uint16_t secs_track; /* 18: sectors per track */ + uint16_t heads; /* 1a: number of heads */ + uint32_t hidden; /* 1c: hidden sectors (partition start) */ + uint32_t sector_count; /* 20: no. of sectors (if sectors == 0) */ + +union { +/* FAT16 fields */ +struct { + uint8_t drive_num; /* 24: */ + uint8_t empty_1; /* 25: */ + uint8_t ext_signature; /* 26: always 0x29 */ + uint32_t serial_number; /* 27: */ + uint8_t volume_name [11]; /* 2b: */ + uint8_t fat_name [8]; /* 36: */ + + uint8_t boot_code[448]; /* 3f: Boot code (or message) */ +} __attribute__ ((packed)) fat16; + +/* FAT32 fields */ +struct { + uint32_t fat_length; /* 24: size of FAT in sectors */ + uint16_t flags; /* 28: bit8: fat mirroring, low4: active fat */ + uint16_t version; /* 2a: minor * 256 + major */ + uint32_t root_dir_cluster; /* 2c: */ + uint16_t info_sector; /* 30: */ + uint16_t backup_sector; /* 32: */ + + uint8_t empty_1 [12]; /* 34: */ + + uint16_t drive_num; /* 40: */ + uint8_t ext_signature; /* 42: always 0x29 */ + uint32_t serial_number; /* 43: */ + uint8_t volume_name [11]; /* 47: */ + uint8_t fat_name [8]; /* 52: */ + + uint8_t boot_code[420]; /* 5a: Boot code (or message) */ +} __attribute ((packed)) fat32; +} __attribute ((packed)) u; + + uint16_t boot_sign; /* 1fe: always 0xAA55 */ +} __attribute__ ((packed)); + +struct _FatInfoSector { + uint32_t signature_1; /* should be 0x41615252 */ + uint8_t unused [480]; + uint32_t signature_2; /* should be 0x61417272 */ + uint32_t free_clusters; + uint32_t next_cluster; /* most recently allocated cluster */ + uint8_t unused2 [0xe]; + uint16_t signature_3; /* should be 0xaa55 */ +} __attribute__ ((packed)); + +int fat_boot_sector_read (FatBootSector* bs, const PedGeometry* geom); +FatType fat_boot_sector_probe_type (const FatBootSector* bs, + const PedGeometry* geom); +int fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs); +int fat_boot_sector_set_boot_code (FatBootSector* bs); +int fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs); +int fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs); + +int fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs); +int fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs); +int fat_info_sector_write (const FatInfoSector* is, PedFileSystem* fs); + +#endif /* PED_FAT_BOOTSECTOR_H */ + diff --git a/libparted/fs/fat/calc.c b/libparted/fs/fat/calc.c new file mode 100644 index 0000000..d09959f --- /dev/null +++ b/libparted/fs/fat/calc.c @@ -0,0 +1,437 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000, 2002 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "fat.h" + +#ifndef DISCOVER_ONLY + +/* returns the minimum size of clusters for a given file system type */ +PedSector +fat_min_cluster_size (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; + case FAT_TYPE_FAT16: return 1024/512; + case FAT_TYPE_FAT32: return 4096/512; + } + return 0; +} + +static PedSector +_smallest_power2_over (PedSector ceiling) +{ + PedSector result = 1; + + while (result < ceiling) + result *= 2; + + return result; +} + +/* returns the minimum size of clusters for a given file system type */ +PedSector +fat_recommend_min_cluster_size (FatType fat_type, PedSector size) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; + case FAT_TYPE_FAT16: return fat_min_cluster_size(fat_type); + case FAT_TYPE_FAT32: + return PED_MAX(_smallest_power2_over(size + / MAX_FAT32_CLUSTERS), + fat_min_cluster_size (fat_type)); + } + return 0; +} + +/* returns the maxmimum size of clusters for a given file system type */ +PedSector +fat_max_cluster_size (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; /* dunno... who cares? */ + case FAT_TYPE_FAT16: return 32768/512; + case FAT_TYPE_FAT32: return 65536/512; + } + return 0; +} + +/* returns the minimum number of clusters for a given file system type */ +FatCluster +fat_min_cluster_count (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: + case FAT_TYPE_FAT16: + return fat_max_cluster_count (fat_type) / 2; + + case FAT_TYPE_FAT32: return 0xfff0; + } + return 0; +} + +/* returns the maximum number of clusters for a given file system type */ +FatCluster +fat_max_cluster_count (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 0xff0; + case FAT_TYPE_FAT16: return 0xfff0; + case FAT_TYPE_FAT32: return 0x0ffffff0; + } + return 0; +} + +/* what is this supposed to be? What drugs are M$ on? (Can I have some? :-) */ +PedSector +fat_min_reserved_sector_count (FatType fat_type) +{ + return (fat_type == FAT_TYPE_FAT32) ? 32 : 1; +} + +int +fat_check_resize_geometry (const PedFileSystem* fs, + const PedGeometry* geom, + PedSector new_cluster_sectors, + FatCluster new_cluster_count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector free_space; + PedSector min_free_space; + PedSector total_space; + PedSector new_total_space; + PedSector dir_space; + + PED_ASSERT (geom != NULL, return 0); + + dir_space = fs_info->total_dir_clusters * fs_info->cluster_sectors; + free_space = fs_info->fat->free_cluster_count + * fs_info->cluster_sectors; + total_space = fs_info->fat->cluster_count * fs_info->cluster_sectors; + new_total_space = new_cluster_count * new_cluster_sectors; + min_free_space = total_space - new_total_space + dir_space; + + PED_ASSERT (new_cluster_count + <= fat_max_cluster_count (FAT_TYPE_FAT32), + return 0); + + if (free_space < min_free_space) { + char* needed = ped_unit_format (geom->dev, min_free_space); + char* have = ped_unit_format (geom->dev, free_space); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("You need %s of free disk space to shrink this " + "partition to this size. Currently, only %s is " + "free."), + needed, have); + ped_free (needed); + ped_free (have); + return 0; + } + + return 1; +} + + +/******************************************************************************/ + +/* DO NOT EDIT THIS ALGORITHM! + * As far as I can tell, this is the same algorithm used by Microsoft to + * calculate the size of the file allocaion tables, and the number of clusters. + * I have not verified this by dissassembling Microsoft code - I came to this + * conclusion by empirical analysis (i.e. trial and error - this was HORRIBLE). + * + * If you think this code makes no sense, then you are right. I will restrain + * the urge to inflict serious bodily harm on Microsoft people. + */ + +static int +entries_per_sector (FatType fat_type) +{ + switch (fat_type) { + case FAT_TYPE_FAT12: + return 512 * 3 / 2; + case FAT_TYPE_FAT16: + return 512 / 2; + case FAT_TYPE_FAT32: + return 512 / 4; + } + return 0; +} + +static int +calc_sizes (PedSector size, PedSector align, FatType fat_type, + PedSector root_dir_sectors, PedSector cluster_sectors, + FatCluster* out_cluster_count, PedSector* out_fat_size) +{ + PedSector data_fat_space; /* space available to clusters + FAT */ + PedSector fat_space; /* space taken by each FAT */ + PedSector cluster_space; /* space taken by clusters */ + FatCluster cluster_count; + int i; + + PED_ASSERT (out_cluster_count != NULL, return 0); + PED_ASSERT (out_fat_size != NULL, return 0); + + data_fat_space = size - fat_min_reserved_sector_count (fat_type) + - align; + if (fat_type == FAT_TYPE_FAT16) + data_fat_space -= root_dir_sectors; + + fat_space = 0; + for (i = 0; i < 2; i++) { + if (fat_type == FAT_TYPE_FAT32) + cluster_space = data_fat_space - fat_space; + else + cluster_space = data_fat_space - 2 * fat_space; + + cluster_count = cluster_space / cluster_sectors; + fat_space = ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type)); + } + + cluster_space = data_fat_space - 2 * fat_space; + cluster_count = cluster_space / cluster_sectors; + + /* looks like this should be part of the loop condition? + * Need to build the Big Table TM again to check + */ + if (fat_space < ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type))) { + fat_space = ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type)); + } + + if (cluster_count > fat_max_cluster_count (fat_type) + || cluster_count < fat_min_cluster_count (fat_type)) + return 0; + + *out_cluster_count = cluster_count; + *out_fat_size = fat_space; + + return 1; +} + +/****************************************************************************/ + +int +fat_calc_sizes (PedSector size, PedSector align, FatType fat_type, + PedSector root_dir_sectors, + PedSector* out_cluster_sectors, FatCluster* out_cluster_count, + PedSector* out_fat_size) +{ + PedSector cluster_sectors; + + PED_ASSERT (out_cluster_sectors != NULL, return 0); + PED_ASSERT (out_cluster_count != NULL, return 0); + PED_ASSERT (out_fat_size != NULL, return 0); + + for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size); + cluster_sectors <= fat_max_cluster_size (fat_type); + cluster_sectors *= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size); + cluster_sectors >= fat_min_cluster_size (fat_type); + cluster_sectors /= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + /* only make the cluster size really small (<4k) if a bigger one is + * isn't possible. Windows never makes FS's like this, but it + * seems to work... (do more tests!) + */ + for (cluster_sectors = 4; cluster_sectors > 0; cluster_sectors /= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + return 0; +} + +/* Same as fat_calc_sizes, except it only attempts to match a particular + * cluster size. This is useful, because the FAT resizer can only shrink the + * cluster size. + */ +int +fat_calc_resize_sizes ( + const PedGeometry* geom, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector cluster_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size) +{ + PedSector min_cluster_sectors; + + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (out_cluster_sectors != NULL, return 0); + PED_ASSERT (out_cluster_count != NULL, return 0); + PED_ASSERT (out_fat_size != NULL, return 0); + +/* libparted can only reduce the cluster size at this point */ + for (*out_cluster_sectors = cluster_sectors; + *out_cluster_sectors >= fat_min_cluster_size (fat_type); + *out_cluster_sectors /= 2) { + if (calc_sizes (geom->length, align, fat_type, root_dir_sectors, + *out_cluster_sectors, + out_cluster_count, out_fat_size)) + return 1; + } + return 0; +} + +/* Calculates the number of sectors needed to be added to cluster_offset, + to make the cluster on the new file system match up with the ones + on the old file system. + However, some space is reserved by fat_calc_resize_sizes() and + friends, to allow room for this space. If too much of this space is left + over, everyone will complain, so we have to be greedy, and use it all up... + */ +PedSector +fat_calc_align_sectors (const PedFileSystem* new_fs, + const PedFileSystem* old_fs) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + PedSector raw_old_meta_data_end; + PedSector new_meta_data_size; + PedSector min_new_meta_data_end; + PedSector new_data_size; + PedSector new_clusters_size; + PedSector align; + + new_meta_data_size + = fat_min_reserved_sector_count (new_fs_info->fat_type) + + new_fs_info->fat_sectors * 2; + + if (new_fs_info->fat_type == FAT_TYPE_FAT16) + new_meta_data_size += new_fs_info->root_dir_sector_count; + + raw_old_meta_data_end = old_fs->geom->start + + old_fs_info->cluster_offset; + + min_new_meta_data_end = new_fs->geom->start + new_meta_data_size; + + if (raw_old_meta_data_end > min_new_meta_data_end) + align = (raw_old_meta_data_end - min_new_meta_data_end) + % new_fs_info->cluster_sectors; + else + align = (new_fs_info->cluster_sectors + - ( (min_new_meta_data_end - raw_old_meta_data_end) + % new_fs_info->cluster_sectors )) + % new_fs_info->cluster_sectors; + + new_data_size = new_fs->geom->length - new_meta_data_size; + new_clusters_size = new_fs_info->cluster_count + * new_fs_info->cluster_sectors; + + while (new_clusters_size + align + new_fs_info->cluster_sectors + <= new_data_size) + align += new_fs_info->cluster_sectors; + + return align; +} + +int +fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + return sector >= fs_info->cluster_offset + && sector < fs_info->cluster_offset + + fs_info->cluster_sectors * fs_info->cluster_count; +} + +FatFragment +fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2, + return 0); + + return (cluster - 2) * fs_info->cluster_frags; +} + +FatCluster +fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0); + + return frag / fs_info->cluster_frags + 2; +} + +PedSector +fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0); + + return frag * fs_info->frag_sectors + fs_info->cluster_offset; +} + +FatFragment +fat_sector_to_frag (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (sector >= fs_info->cluster_offset, return 0); + + return (sector - fs_info->cluster_offset) / fs_info->frag_sectors; +} + +PedSector +fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2, + return 0); + + return (cluster - 2) * fs_info->cluster_sectors + + fs_info->cluster_offset; +} + +FatCluster +fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (sector >= fs_info->cluster_offset, return 0); + + return (sector - fs_info->cluster_offset) / fs_info->cluster_sectors + + 2; +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/calc.h b/libparted/fs/fat/calc.h new file mode 100644 index 0000000..cd126e5 --- /dev/null +++ b/libparted/fs/fat/calc.h @@ -0,0 +1,78 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef PED_FAT_CALC_H +#define PED_FAT_CALC_H + +extern PedSector fat_min_cluster_size (FatType fat_type); +extern PedSector fat_max_cluster_size (FatType fat_type); +extern FatCluster fat_min_cluster_count (FatType fat_type); +extern FatCluster fat_max_cluster_count (FatType fat_type); + +extern PedSector fat_min_reserved_sector_count (FatType fat_type); + +extern int fat_check_resize_geometry (const PedFileSystem* fs, + const PedGeometry* geom, + PedSector new_cluster_sectors, + FatCluster new_cluster_count); + +extern int fat_calc_sizes (PedSector size, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size); + +extern int fat_calc_resize_sizes (const PedGeometry* geom, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector cluster_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size); + +extern PedSector +fat_calc_align_sectors (const PedFileSystem* new_fs, + const PedFileSystem* old_fs); + +extern int +fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector); + +extern FatFragment +fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster); + +extern FatCluster +fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag); + +extern PedSector +fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag); + +extern FatFragment +fat_sector_to_frag (const PedFileSystem* fs, PedSector sector); + +extern PedSector +fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster); + +extern FatCluster +fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector); + +#endif /* PED_FAT_CALC_H */ + diff --git a/libparted/fs/fat/clstdup.c b/libparted/fs/fat/clstdup.c new file mode 100644 index 0000000..b9b0534 --- /dev/null +++ b/libparted/fs/fat/clstdup.c @@ -0,0 +1,425 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <string.h> + +#include "fat.h" + +#ifndef DISCOVER_ONLY + +static int +needs_duplicating (const FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatCluster cluster = fat_frag_to_cluster (ctx->old_fs, frag); + FatClusterFlag flag; + + PED_ASSERT (cluster >= 2 && cluster < old_fs_info->cluster_count + 2, + return 0); + + flag = fat_get_fragment_flag (ctx->old_fs, frag); + switch (flag) { + case FAT_FLAG_FREE: + return 0; + + case FAT_FLAG_DIRECTORY: + return 1; + + case FAT_FLAG_FILE: + return fat_op_context_map_static_fragment (ctx, frag) == -1; + + case FAT_FLAG_BAD: + return 0; + } + + return 0; +} + +static int +search_next_fragment (FatOpContext* ctx) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + + for (; ctx->buffer_offset < fs_info->frag_count; ctx->buffer_offset++) { + if (needs_duplicating (ctx, ctx->buffer_offset)) + return 1; + } + return 0; /* all done! */ +} + +static int +read_marked_fragments (FatOpContext* ctx, FatFragment length) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + int status; + FatFragment i; + + ped_exception_fetch_all (); + status = fat_read_fragments (ctx->old_fs, fs_info->buffer, + ctx->buffer_offset, length); + ped_exception_leave_all (); + if (status) + return 1; + + ped_exception_catch (); + +/* something bad happened, so read fragments one by one. (The error may + have occurred on an unused fragment: who cares) */ + for (i = 0; i < length; i++) { + if (ctx->buffer_map [i]) { + if (!fat_read_fragment (ctx->old_fs, + fs_info->buffer + i * fs_info->frag_size, + ctx->buffer_offset + i)) + return 0; + } + } + + return 1; +} + +static int +fetch_fragments (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment fetch_length = 0; + FatFragment frag; + + for (frag = 0; frag < ctx->buffer_frags; frag++) + ctx->buffer_map [frag] = -1; + + for (frag = 0; + frag < ctx->buffer_frags + && ctx->buffer_offset + frag < old_fs_info->frag_count; + frag++) { + if (needs_duplicating (ctx, ctx->buffer_offset + frag)) { + ctx->buffer_map [frag] = 1; + fetch_length = frag + 1; + } + } + + if (!read_marked_fragments (ctx, fetch_length)) + return 0; + + return 1; +} + +/***************************************************************************** + * here starts the write code. All assumes that ctx->buffer_map [first] and + * ctx->buffer_map [last] are occupied by fragments that need to be duplicated. + *****************************************************************************/ + +/* finds the first fragment that is not going to get overwritten (that needs to + get read in) */ +static FatFragment +get_first_underlay (const FatOpContext* ctx, int first, int last) +{ + int old; + FatFragment new; + + PED_ASSERT (first <= last, return 0); + + new = ctx->buffer_map [first]; + for (old = first + 1; old <= last; old++) { + if (ctx->buffer_map [old] == -1) + continue; + new++; + if (ctx->buffer_map [old] != new) + return new; + } + return -1; +} + +/* finds the last fragment that is not going to get overwritten (that needs to + get read in) */ +static FatFragment +get_last_underlay (const FatOpContext* ctx, int first, int last) +{ + int old; + FatFragment new; + + PED_ASSERT (first <= last, return 0); + + new = ctx->buffer_map [last]; + for (old = last - 1; old >= first; old--) { + if (ctx->buffer_map [old] == -1) + continue; + new--; + if (ctx->buffer_map [old] != new) + return new; + } + return -1; +} + +/* "underlay" refers to the "static" fragments, that remain unchanged. + * when writing large chunks at a time, we don't want to clobber these, + * so we read them in, and write them back again. MUCH quicker that way. + */ +static int +quick_group_write_read_underlay (FatOpContext* ctx, int first, int last) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment first_underlay; + FatFragment last_underlay; + FatFragment underlay_length; + + PED_ASSERT (first <= last, return 0); + + first_underlay = get_first_underlay (ctx, first, last); + if (first_underlay == -1) + return 1; + last_underlay = get_last_underlay (ctx, first, last); + + PED_ASSERT (first_underlay <= last_underlay, return 0); + + underlay_length = last_underlay - first_underlay + 1; + if (!fat_read_fragments (ctx->new_fs, + new_fs_info->buffer + + (first_underlay - ctx->buffer_map [first]) + * new_fs_info->frag_size, + first_underlay, + underlay_length)) + return 0; + return 1; +} + +/* quick_group_write() makes no attempt to recover from errors - just + * does things fast. If there is an error, slow_group_write() is + * called. + * Note: we do syncing writes, to make sure there isn't any + * error writing out. It's rather difficult recovering from errors + * further on. + */ +static int +quick_group_write (FatOpContext* ctx, int first, int last) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int active_length; + int i; + int offset; + + PED_ASSERT (first <= last, return 0); + + ped_exception_fetch_all (); + if (!quick_group_write_read_underlay (ctx, first, last)) + goto error; + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + offset = ctx->buffer_map [i] - ctx->buffer_map [first]; + memcpy (new_fs_info->buffer + offset * new_fs_info->frag_size, + old_fs_info->buffer + i * new_fs_info->frag_size, + new_fs_info->frag_size); + } + + active_length = ctx->buffer_map [last] - ctx->buffer_map [first] + 1; + if (!fat_write_sync_fragments (ctx->new_fs, new_fs_info->buffer, + ctx->buffer_map [first], active_length)) + goto error; + + ped_exception_leave_all (); + return 1; + +error: + ped_exception_catch (); + ped_exception_leave_all (); + return 0; +} + +/* Writes fragments out, one at a time, avoiding errors on redundant writes + * on damaged parts of the disk we already know about. If there's an error + * on one of the required fragments, it gets marked as bad, and a replacement + * is found. + */ +static int +slow_group_write (FatOpContext* ctx, int first, int last) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int i; + + PED_ASSERT (first <= last, return 0); + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + while (!fat_write_sync_fragment (ctx->new_fs, + old_fs_info->buffer + i * old_fs_info->frag_size, + ctx->buffer_map [i])) { + fat_table_set_bad (new_fs_info->fat, + ctx->buffer_map [i]); + ctx->buffer_map [i] = fat_table_alloc_cluster + (new_fs_info->fat); + if (ctx->buffer_map [i] == 0) + return 0; + } + } + return 1; +} + +static int +update_remap (FatOpContext* ctx, int first, int last) +{ + int i; + + PED_ASSERT (first <= last, return 0); + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + ctx->remap [ctx->buffer_offset + i] = ctx->buffer_map [i]; + } + + return 1; +} + +static int +group_write (FatOpContext* ctx, int first, int last) +{ + PED_ASSERT (first <= last, return 0); + + if (!quick_group_write (ctx, first, last)) { + if (!slow_group_write (ctx, first, last)) + return 0; + } + if (!update_remap (ctx, first, last)) + return 0; + return 1; +} + +/* assumes fragment size and new_fs's cluster size are equal */ +static int +write_fragments (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int group_start; + int group_end = -1; /* shut gcc up! */ + FatFragment mapped_length; + FatFragment i; + FatCluster new_cluster; + + PED_ASSERT (ctx->buffer_offset < old_fs_info->frag_count, return 0); + + group_start = -1; + for (i = 0; i < ctx->buffer_frags; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + ctx->frags_duped++; + + new_cluster = fat_table_alloc_cluster (new_fs_info->fat); + if (!new_cluster) + return 0; + fat_table_set_eof (new_fs_info->fat, new_cluster); + ctx->buffer_map [i] = fat_cluster_to_frag (ctx->new_fs, + new_cluster); + + if (group_start == -1) + group_start = group_end = i; + + PED_ASSERT (ctx->buffer_map [i] + >= ctx->buffer_map [group_start], + return 0); + + mapped_length = ctx->buffer_map [i] + - ctx->buffer_map [group_start] + 1; + if (mapped_length <= ctx->buffer_frags) { + group_end = i; + } else { + /* ran out of room in the buffer, so write this group, + * and start a new one... + */ + if (!group_write (ctx, group_start, group_end)) + return 0; + group_start = group_end = i; + } + } + + PED_ASSERT (group_start != -1, return 0); + + if (!group_write (ctx, group_start, group_end)) + return 0; + return 1; +} + +/* default all fragments to unmoved + */ +static void +init_remap (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment i; + + for (i = 0; i < old_fs_info->frag_count; i++) + ctx->remap[i] = fat_op_context_map_static_fragment (ctx, i); +} + +static FatFragment +count_frags_to_dup (FatOpContext* ctx) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment i; + FatFragment total; + + total = 0; + + for (i = 0; i < fs_info->frag_count; i++) { + if (needs_duplicating (ctx, i)) + total++; + } + + return total; +} + +/* duplicates unreachable file clusters, and all directory clusters + */ +int +fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment total_frags_to_dup; + + init_remap (ctx); + total_frags_to_dup = count_frags_to_dup (ctx); + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, "moving data"); + + ctx->buffer_offset = 0; + ctx->frags_duped = 0; + while (search_next_fragment (ctx)) { + ped_timer_update ( + timer, 1.0 * ctx->frags_duped / total_frags_to_dup); + + if (!fetch_fragments (ctx)) + return 0; + if (!write_fragments (ctx)) + return 0; + ctx->buffer_offset += ctx->buffer_frags; + } + + ped_timer_update (timer, 1.0); + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/clstdup.h b/libparted/fs/fat/clstdup.h new file mode 100644 index 0000000..e99a2d3 --- /dev/null +++ b/libparted/fs/fat/clstdup.h @@ -0,0 +1,29 @@ +/* + libparted + Copyright (C) 1999 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef PED_FAT_CLSTDUP_H_INCLUDED +#define PED_FAT_CLSTDUP_H_INCLUDED + +#include "context.h" + +/* the big important one :-) */ +extern int fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer); + +#endif /* PED_FAT_CLSTDUP_H_INCLUDED */ + diff --git a/libparted/fs/fat/context.c b/libparted/fs/fat/context.c new file mode 100644 index 0000000..9cb3366 --- /dev/null +++ b/libparted/fs/fat/context.c @@ -0,0 +1,260 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <string.h> + +#include "fat.h" + +#ifndef DISCOVER_ONLY + +/* Note: this deals with file system start and end sectors, even if the physical + * devices are different (eg for fat_copy()) Perhaps this is a hack, but it + * works ;-) + */ +static int +calc_deltas (FatOpContext* ctx) +{ + PedFileSystem* old_fs = ctx->old_fs; + PedFileSystem* new_fs = ctx->new_fs; + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + PedSector old_cluster_ofs; + PedSector new_cluster_ofs; + PedSector sector_delta; + + old_cluster_ofs = old_fs->geom->start + old_fs_info->cluster_offset; + new_cluster_ofs = new_fs->geom->start + new_fs_info->cluster_offset; + + if (new_cluster_ofs > old_cluster_ofs) { + ctx->start_move_dir = FAT_DIR_FORWARD; + sector_delta = new_cluster_ofs - old_cluster_ofs; + } else { + ctx->start_move_dir = FAT_DIR_BACKWARD; + sector_delta = old_cluster_ofs - new_cluster_ofs; + } + + if (sector_delta % new_fs_info->cluster_sectors) { + ped_exception_throw ( + PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("Cluster start delta = %d, which is not a multiple " + "of the cluster size %d."), + (int) sector_delta, + (int) new_fs_info->cluster_sectors); + return 0; + } + + ctx->start_move_delta = sector_delta / ctx->frag_sectors; + +#ifdef PED_VERBOSE + printf ("Start move delta is: %d %s.\n", + (int) ctx->start_move_delta, + (ctx->start_move_dir == FAT_DIR_FORWARD)? + "forwards" : "backwards"); +#endif + + return 1; +} + +FatOpContext* +fat_op_context_new (PedFileSystem* new_fs, PedFileSystem* old_fs) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + FatOpContext* ctx; + + ctx = (FatOpContext*) ped_malloc (sizeof (FatOpContext)); + if (!ctx) + goto error; + + ctx->frag_sectors = PED_MIN (old_fs_info->cluster_sectors, + new_fs_info->cluster_sectors); + if (!fat_set_frag_sectors (new_fs, ctx->frag_sectors)) + goto error; + if (!fat_set_frag_sectors (old_fs, ctx->frag_sectors)) + goto error; + + ctx->buffer_frags = old_fs_info->buffer_sectors / ctx->frag_sectors; + ctx->buffer_map = (FatFragment*) ped_malloc (sizeof (FatFragment) + * ctx->buffer_frags); + if (!ctx->buffer_map) + goto error_free_ctx; + + ctx->remap = (FatFragment*) ped_malloc (sizeof (FatFragment) + * old_fs_info->frag_count); + if (!ctx->remap) + goto error_free_buffer_map; + + ctx->new_fs = new_fs; + ctx->old_fs = old_fs; + if (!calc_deltas (ctx)) + goto error_free_buffer_map; + + return ctx; + +error_free_buffer_map: + ped_free (ctx->buffer_map); +error_free_ctx: + ped_free (ctx); +error: + return NULL; +} + +void +fat_op_context_destroy (FatOpContext* ctx) +{ + ped_free (ctx->buffer_map); + ped_free (ctx->remap); + ped_free (ctx); +} + +FatFragment +fat_op_context_map_static_fragment (const FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment result; + + if (ctx->new_fs->geom->dev != ctx->old_fs->geom->dev) + return -1; + + if (ctx->start_move_dir == FAT_DIR_FORWARD) { + if (frag < ctx->start_move_delta) + return -1; + result = frag - ctx->start_move_delta; + } else { + result = frag + ctx->start_move_delta; + } + + if (result >= new_fs_info->frag_count) + return -1; + + return result; +} + +FatCluster +fat_op_context_map_static_cluster (const FatOpContext* ctx, FatCluster clst) +{ + FatFragment mapped_frag; + + mapped_frag = fat_op_context_map_static_fragment (ctx, + fat_cluster_to_frag (ctx->old_fs, clst)); + if (mapped_frag != -1) + return fat_frag_to_cluster (ctx->new_fs, mapped_frag); + else + return 0; +} + +FatFragment +fat_op_context_map_fragment (const FatOpContext* ctx, FatFragment frag) +{ + return ctx->remap [frag]; +} + +FatCluster +fat_op_context_map_cluster (const FatOpContext* ctx, FatCluster clst) +{ + FatFragment mapped_frag; + + mapped_frag = fat_op_context_map_fragment (ctx, + fat_cluster_to_frag (ctx->old_fs, clst)); + if (mapped_frag != -1) + return fat_frag_to_cluster (ctx->new_fs, mapped_frag); + else + return 0; +} + +/* This function sets the initial fat for the new resized file system. + This is in *NO WAY* a proper FAT table - all it does is: + a) mark bad clusters as bad. + b) mark used clusters (that is, clusters from the original FS that are + reachable from the resized one). Marks as EOF (i.e. used, end of + file chain). + c) mark original file system metadata as EOF (i.e. used), to prevent + it from being clobbered. This will leave the original file system + intact, until the partition table is modified, if the start of + the partition is moved. + + The FATs are rebuilt *properly* after cluster relocation. This here is + only to mark clusters as used, so when cluster relocation occurs, clusters + aren't relocated on top of ones marked in a, b or c. +*/ +int +fat_op_context_create_initial_fat (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster clst; + FatCluster new_clst; + PedSector sect; + PedSector new_sect; + FatFragment frag; + FatFragment new_frag; + FatClusterFlag frag_flag; + + new_fs_info->fat = fat_table_new ( + new_fs_info->fat_type, + new_fs_info->fat_sectors * 512 + / fat_table_entry_size (new_fs_info->fat_type)); + if (!new_fs_info->fat) + return 0; + + if (!fat_table_set_cluster_count (new_fs_info->fat, + new_fs_info->cluster_count)) + return 0; + +/* mark bad and used clusters */ + for (frag = 0; frag < old_fs_info->frag_count; frag++) { + frag_flag = fat_get_fragment_flag (ctx->old_fs, frag); + if (frag_flag == FAT_FLAG_FREE) + continue; + + new_frag = fat_op_context_map_static_fragment (ctx, frag); + if (new_frag == -1) + continue; + + new_clst = fat_frag_to_cluster (ctx->new_fs, new_frag); + PED_ASSERT (new_clst != 0, return 0); + + if (frag_flag == FAT_FLAG_BAD) { + if (!fat_table_set_bad (new_fs_info->fat, new_clst)) + return 0; + } else { + if (!fat_table_set_eof (new_fs_info->fat, new_clst)) + return 0; + } + } + +/* mark metadata regions that map to clusters on the new FS */ + for (sect = 0; sect < old_fs_info->cluster_offset; sect++) { + new_sect = ped_geometry_map (ctx->new_fs->geom, + ctx->old_fs->geom, sect); + if (new_sect == -1 + || !fat_is_sector_in_clusters (ctx->new_fs, new_sect)) + continue; + + clst = fat_sector_to_cluster (ctx->new_fs, new_sect); + PED_ASSERT (clst != 0, return 0); + + if (!fat_table_set_eof (new_fs_info->fat, clst)) + return 0; + } + + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/context.h b/libparted/fs/fat/context.h new file mode 100644 index 0000000..f552bcb --- /dev/null +++ b/libparted/fs/fat/context.h @@ -0,0 +1,70 @@ +/* + libparted + Copyright (C) 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef PED_FAT_CONTEXT_H_INCLUDED +#define PED_FAT_CONTEXT_H_INCLUDED + +#include "count.h" + +enum _FatDirection { + FAT_DIR_FORWARD, + FAT_DIR_BACKWARD +}; +typedef enum _FatDirection FatDirection; + +struct _FatOpContext { + PedFileSystem* old_fs; + PedFileSystem* new_fs; + + PedSector frag_sectors; /* should equal old_fs and + new_fs's frag_sectors */ + + FatDirection start_move_dir; + FatFragment start_move_delta; + + FatFragment buffer_offset; + FatFragment buffer_frags; + FatFragment* buffer_map; + + FatFragment frags_duped; + + FatFragment* remap; + + FatCluster new_root_dir [32]; +}; +typedef struct _FatOpContext FatOpContext; + +extern FatOpContext* fat_op_context_new (PedFileSystem* new_fs, + PedFileSystem* old_fs); + +extern void fat_op_context_destroy (FatOpContext* ctx); + +extern FatFragment fat_op_context_map_static_fragment (const FatOpContext* ctx, + FatFragment frag); +extern FatCluster fat_op_context_map_static_cluster (const FatOpContext* ctx, + FatCluster clst); + +extern FatFragment fat_op_context_map_fragment (const FatOpContext* ctx, + FatFragment frag); +extern FatCluster fat_op_context_map_cluster (const FatOpContext* ctx, + FatCluster clst); + +extern int fat_op_context_create_initial_fat (FatOpContext* ctx); + +#endif /* PED_FAT_CONTEXT_H_INCLUDED */ diff --git a/libparted/fs/fat/count.c b/libparted/fs/fat/count.c new file mode 100644 index 0000000..747f3ef --- /dev/null +++ b/libparted/fs/fat/count.c @@ -0,0 +1,410 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + I can also be contacted at: + + Andrew Clausen + 18 Shaw St + Ashwood, 3147 + Victoria, Australia + +*/ + +#include "fat.h" +#include "traverse.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +#if 0 +/* extremely ugly hack: stick everything that obviously isn't an unmovable file + * in here. Note: DAT is a bit dubious. Unfortunately, it's used by the + * registry, so it'll be all over the place :-( + */ +static char* movable_extensions[] = { + "", + "1ST", + "AVI", + "BAK", "BAT", "BMP", + "CFG", "COM", "CSS", + "DAT", "DLL", "DOC", "DRV", + "EXE", + "FAQ", "FLT", "FON", + "GID", "GIF", + "HLP", "HTT", "HTM", + "ICO", "INI", + "JPG", + "LNK", "LOG", + "KBD", + "ME", "MID", "MSG", + "OCX", "OLD", + "PIF", "PNG", "PRV", + "RTF", + "SCR", "SYS", + "TMP", "TTF", "TXT", + "URL", + "WAV", + "VBX", "VOC", "VXD", + NULL +}; + +static char* +get_extension (char* file_name) +{ + char* ext; + + ext = strrchr (file_name, '.'); + if (!ext) + return ""; + if (strchr (ext, '\\')) + return ""; + return ext + 1; +} + +static int +is_movable_system_file (char* file_name) +{ + char* ext = get_extension (file_name); + int i; + + for (i = 0; movable_extensions [i]; i++) { + if (strcasecmp (ext, movable_extensions [i]) == 0) + return 1; + } + + return 0; +} +#endif /* 0 */ + +/* + prints out the sequence of clusters for a given file chain, beginning + at start_cluster. +*/ +static void +print_chain (PedFileSystem* fs, FatCluster start) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster clst; + int this_row; + + this_row = 0; + for (clst = start; !fat_table_is_eof (fs_info->fat, clst); + clst = fat_table_get (fs_info->fat, clst)) { + printf (" %d", (int) clst); + if (++this_row == 7) { + printf ("\n"); + this_row = 0; + } + } + printf ("\n"); +} + +static PedSector +remainder_round_up (PedSector a, PedSector b) +{ + PedSector result; + + result = a % b; + if (!result) + result = b; + return result; +} + +/* + traverse the FAT for a file/directory, marking each entry's flag + to "flag". +*/ +static int +flag_traverse_fat (PedFileSystem* fs, const char* chain_name, FatCluster start, + FatClusterFlag flag, PedSector size) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster clst; + FatCluster prev_clst; + int last_cluster_usage; + FatCluster chain_length = 0; + + if (fat_table_is_eof (fs_info->fat, start)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Bad directory entry for %s: first cluster is the " + "end of file marker."), + chain_name) + != PED_EXCEPTION_IGNORE) + return 0; + } + + for (prev_clst = clst = start; !fat_table_is_eof (fs_info->fat, clst); + prev_clst = clst, clst = fat_table_get (fs_info->fat, clst)) { + chain_length++; + if (!clst) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: unterminated chain for %s. You " + "should run dosfsck or scandisk."), + chain_name); + return 0; + } + + if (clst >= fs_info->fat->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: cluster %d outside file system " + "in chain for %s. You should run dosfsck " + "or scandisk."), + (int) clst, chain_name); + return 0; + } + + if (fs_info->cluster_info [clst].flag != FAT_FLAG_FREE ) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: cluster %d is cross-linked for " + "%s. You should run dosfsck or scandisk."), + (int) clst, chain_name); + return 0; + } + + if (flag == FAT_FLAG_DIRECTORY) + fs_info->total_dir_clusters++; + + fs_info->cluster_info [clst].flag = flag; + fs_info->cluster_info [clst].units_used = 0; /* 0 == 64 */ + } + + if (size + && chain_length + != ped_div_round_up (size, fs_info->cluster_sectors)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("%s is %dk, but it has %d clusters (%dk)."), + chain_name, + (int) size / 2, + (int) chain_length, + (int) chain_length * fs_info->cluster_sectors / 2) + != PED_EXCEPTION_IGNORE) + return 0; + } + + last_cluster_usage + = ped_div_round_up (64 * remainder_round_up (size, + fs_info->cluster_sectors), + fs_info->cluster_sectors); + + fs_info->cluster_info [prev_clst].units_used = last_cluster_usage; + + return 1; +} + +/* + recursively traverses a directory, flagging all clusters in the process. + It frees the traverse_info structure before returning. +*/ +static int +flag_traverse_dir (FatTraverseInfo* trav_info) { + PedFileSystem* fs = trav_info->fs; + FatDirEntry* this_entry; + FatTraverseInfo* subdir_trav_info; + char file_name [512]; + char* file_name_start; + FatCluster first_cluster; + PedSector size; + PedExceptionOption ex_status; + + PED_ASSERT (trav_info != NULL, return 0); + + strcpy (file_name, trav_info->dir_name); + file_name_start = file_name + strlen (file_name); + + while ( (this_entry = fat_traverse_next_dir_entry (trav_info)) ) { + if (fat_dir_entry_is_null_term (this_entry)) + break; + if (!fat_dir_entry_has_first_cluster (this_entry, fs)) + continue; + if (this_entry->name [0] == '.') + continue; /* skip . and .. entries */ + + fat_dir_entry_get_name (this_entry, file_name_start); + first_cluster = fat_dir_entry_get_first_cluster(this_entry, fs); + size = ped_div_round_up (fat_dir_entry_get_length (this_entry), + 512); + +#ifdef PED_VERBOSE + printf ("%s: ", file_name); + print_chain (fs, first_cluster); +#endif + +#if 0 + if (fat_dir_entry_is_system_file (this_entry) + && !is_movable_system_file (file_name)) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The file %s is marked as a system file. " + "This means moving it could cause some " + "programs to stop working."), + file_name); + + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + return 0; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_IGNORE: + } + } +#endif /* 0 */ + + if (fat_dir_entry_is_directory (this_entry)) { + if (!flag_traverse_fat (fs, file_name, first_cluster, + FAT_FLAG_DIRECTORY, size)) + return 0; + + subdir_trav_info = fat_traverse_directory (trav_info, + this_entry); + if (!subdir_trav_info) + return 0; + if (!flag_traverse_dir (subdir_trav_info)) + return 0; + } else if (fat_dir_entry_is_file (this_entry)) { + if (!flag_traverse_fat (fs, file_name, first_cluster, + FAT_FLAG_FILE, size)) + return 0; + } + } + + fat_traverse_complete (trav_info); + return 1; +} + +static void +_mark_bad_clusters (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster cluster; + FatFragment frag; + + for (cluster = 2; cluster < fs_info->cluster_count + 2; cluster++) { + if (fat_table_is_bad (fs_info->fat, cluster)) + fs_info->cluster_info [cluster].flag = FAT_FLAG_BAD; + } +} + +/* + fills in cluster_info. Each FAT entry (= cluster) is flagged as either + FAT_FLAG_FREE, FAT_FLAG_FILE or FAT_FLAG_DIRECTORY. + + Also, the fraction of each cluster (x/64) is recorded +*/ +int +fat_collect_cluster_info (PedFileSystem* fs) { + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTraverseInfo* trav_info; + + /* set all clusters to unused as a default */ + memset (fs_info->cluster_info, 0, fs_info->fat->cluster_count + 2); + fs_info->total_dir_clusters = 0; + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + trav_info = fat_traverse_begin (fs, fs_info->root_cluster, + "\\"); + if (!flag_traverse_dir (trav_info)) + return 0; + if (!flag_traverse_fat (fs, "\\", fs_info->root_cluster, + FAT_FLAG_DIRECTORY, 0)) + return 0; + } else { + trav_info = fat_traverse_begin (fs, FAT_ROOT, "\\"); + if (!flag_traverse_dir (trav_info)) + return 0; + } + + _mark_bad_clusters (fs); + return 1; +} + +FatClusterFlag +fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + return fs_info->cluster_info [cluster].flag; +} + +PedSector +fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int fraction; + + if (fs_info->cluster_info [cluster].flag == FAT_FLAG_FREE) + return 0; + + fraction = fs_info->cluster_info [cluster].units_used; + if (fraction == 0) + fraction = 64; + + return fraction * fs_info->cluster_sectors / 64; +} + +FatClusterFlag +fat_get_fragment_flag (PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster cluster = fat_frag_to_cluster (fs, frag); + FatFragment offset = frag % fs_info->cluster_frags; + FatFragment last_frag_used; + FatClusterFlag flag; + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2, + return 0); + + flag = fat_get_cluster_flag (fs, cluster); + if (flag != FAT_FLAG_FILE && flag != FAT_FLAG_DIRECTORY) + return flag; + last_frag_used = (fat_get_cluster_usage (fs, cluster) - 1) + / fs_info->frag_sectors; + if (offset > last_frag_used) + return FAT_FLAG_FREE; + else + return flag; +} + +int +fat_is_fragment_active (PedFileSystem* fs, FatFragment frag) +{ + switch (fat_get_fragment_flag (fs, frag)) { + case FAT_FLAG_FREE: + case FAT_FLAG_BAD: + return 0; + + case FAT_FLAG_FILE: + case FAT_FLAG_DIRECTORY: + return 1; + } + return 0; +} + +#endif /* !DISCOVER_ONLY */ + diff --git a/libparted/fs/fat/count.h b/libparted/fs/fat/count.h new file mode 100644 index 0000000..38b2c56 --- /dev/null +++ b/libparted/fs/fat/count.h @@ -0,0 +1,52 @@ +/* + libparted + Copyright (C) 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + I can also be contacted at: + + Andrew Clausen + 18 Shaw St + Ashwood, 3147 + Victoria, Australia +*/ + +#ifndef COUNT_H_INCLUDED +#define COUNT_H_INCLUDED + +enum _FatClusterFlag { + FAT_FLAG_FREE=0, + FAT_FLAG_FILE=1, + FAT_FLAG_DIRECTORY=2, + FAT_FLAG_BAD=3 +}; +typedef enum _FatClusterFlag FatClusterFlag; + +struct _FatClusterInfo { + unsigned int units_used:6; /* 1 unit = cluster_size / 64 */ + FatClusterFlag flag:2; +} __attribute__ ((packed)) fat16; +typedef struct _FatClusterInfo FatClusterInfo; + +extern int fat_collect_cluster_info (PedFileSystem *fs); +extern FatClusterFlag fat_get_cluster_flag (PedFileSystem* fs, + FatCluster cluster); +extern PedSector fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster); +extern FatClusterFlag fat_get_fragment_flag (PedFileSystem* fs, + FatFragment frag); +extern int fat_is_fragment_active (PedFileSystem* fs, FatFragment frag); + +#endif /* COUNT_H_INCLUDED */ diff --git a/libparted/fs/fat/fat.c b/libparted/fs/fat/fat.c new file mode 100644 index 0000000..ee06f1a --- /dev/null +++ b/libparted/fs/fat/fat.c @@ -0,0 +1,888 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <string.h> +#include <uuid/uuid.h> + +#include "fat.h" +#include "calc.h" + +PedFileSystem* +fat_alloc (const PedGeometry* geom) +{ + PedFileSystem* fs; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + goto error; + + fs->type_specific = (FatSpecific*) ped_malloc (sizeof (FatSpecific)); + if (!fs->type_specific) + goto error_free_fs; + + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) + goto error_free_type_specific; + + fs->checked = 0; + return fs; + +error_free_type_specific: + ped_free (fs->type_specific); +error_free_fs: + ped_free (fs); +error: + return NULL; +} + +/* Requires the boot sector to be analysed */ +int +fat_alloc_buffers (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + fs_info->buffer_sectors = BUFFER_SIZE; + fs_info->buffer = ped_malloc (fs_info->buffer_sectors * 512); + if (!fs_info->buffer) + goto error; + + fs_info->cluster_info = ped_malloc (fs_info->cluster_count + 2); + if (!fs_info->cluster_info) + goto error_free_buffer; + + return 1; + +error_free_buffer: + ped_free (fs_info->buffer); +error: + return 0; +}; + +void +fat_free_buffers (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + ped_free (fs_info->cluster_info); + ped_free (fs_info->buffer); +} + +void +fat_free (PedFileSystem* fs) +{ + ped_geometry_destroy (fs->geom); + ped_free (fs->type_specific); + ped_free (fs); +} + +int +fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (fs_info->cluster_sectors % frag_sectors == 0 + && frag_sectors <= fs_info->cluster_sectors, + return 0); + + fs_info->frag_size = frag_sectors * 512; + fs_info->frag_sectors = frag_sectors; + fs_info->buffer_frags = fs_info->buffer_sectors / frag_sectors; + fs_info->cluster_frags = fs_info->cluster_sectors / frag_sectors; + fs_info->frag_count = fs_info->cluster_count * fs_info->cluster_frags; + + return 1; +} + +PedGeometry* +fat_probe (PedGeometry* geom, FatType* fat_type) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + PedGeometry* result; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + if (!fat_boot_sector_read (&fs_info->boot_sector, geom)) + goto error_free_fs; + if (!fat_boot_sector_analyse (&fs_info->boot_sector, fs)) + goto error_free_fs; + + *fat_type = fs_info->fat_type; + result = ped_geometry_new (geom->dev, geom->start, + fs_info->sector_count); + + fat_free (fs); + return result; + +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +PedGeometry* +fat_probe_fat16 (PedGeometry* geom) +{ + FatType fat_type; + PedGeometry* probed_geom = fat_probe (geom, &fat_type); + + if (probed_geom) { + if (fat_type == FAT_TYPE_FAT16) + return probed_geom; + ped_geometry_destroy (probed_geom); + } + return NULL; +} + +PedGeometry* +fat_probe_fat32 (PedGeometry* geom) +{ + FatType fat_type; + PedGeometry* probed_geom = fat_probe (geom, &fat_type); + + if (probed_geom) { + if (fat_type == FAT_TYPE_FAT32) + return probed_geom; + ped_geometry_destroy (probed_geom); + } + return NULL; +} + +#ifndef DISCOVER_ONLY +int +fat_clobber (PedGeometry* geom) +{ + FatBootSector boot_sector; + + if (!fat_boot_sector_read (&boot_sector, geom)) + return 1; + + boot_sector.system_id[0] = 0; + boot_sector.boot_sign = 0; + if (boot_sector.u.fat16.fat_name[0] == 'F') + boot_sector.u.fat16.fat_name[0] = 0; + if (boot_sector.u.fat32.fat_name[0] == 'F') + boot_sector.u.fat32.fat_name[0] = 0; + + return ped_geometry_write (geom, &boot_sector, 0, 1); +} + +static int +_init_fats (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int i; + FatCluster table_size; + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + fs_info->fat = fat_table_new (fs_info->fat_type, table_size); + if (!fs_info->fat) + goto error; + + if (!fat_table_read (fs_info->fat, fs, 0)) + goto error_free_fat; + + return 1; + +error_free_fat: + fat_table_destroy (fs_info->fat); +error: + return 0; +} + +PedFileSystem* +fat_open (PedGeometry* geom) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + if (!fat_boot_sector_read (&fs_info->boot_sector, geom)) + goto error_free_fs; + if (!fat_boot_sector_analyse (&fs_info->boot_sector, fs)) + goto error_free_fs; + fs->type = (fs_info->fat_type == FAT_TYPE_FAT16) + ? &fat16_type + : &fat32_type; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!fat_info_sector_read (&fs_info->info_sector, fs)) + goto error_free_fs; + } + + if (!_init_fats (fs)) + goto error_free_fs; + if (!fat_alloc_buffers (fs)) + goto error_free_fat_table; + if (!fat_collect_cluster_info (fs)) + goto error_free_buffers; + + return fs; + +error_free_buffers: + fat_free_buffers (fs); +error_free_fat_table: + fat_table_destroy (fs_info->fat); +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +static int +fat_root_dir_clear (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + memset (fs_info->buffer, 0, 512 * fs_info->root_dir_sector_count); + return ped_geometry_write (fs->geom, fs_info->buffer, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count); +} + +/* hack: use the ext2 uuid library to generate a reasonably random (hopefully + * with /dev/random) number. Unfortunately, we can only use 4 bytes of it + */ +static uint32_t +_gen_new_serial_number () +{ + uuid_t uuid; + + uuid_generate (uuid); + return * (uint32_t*) &uuid [0]; +} + +PedFileSystem* +fat_create (PedGeometry* geom, FatType fat_type, PedTimer* timer) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + FatCluster table_size; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + fs_info->logical_sector_size = 1; + fs_info->sectors_per_track = geom->dev->bios_geom.sectors; + fs_info->heads = geom->dev->bios_geom.heads; + fs_info->sector_count = fs->geom->length; + fs_info->fat_table_count = 2; +/* some initial values, to be changed later */ + fs_info->root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT + / (512 / sizeof (FatDirEntry)); + fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT; + + fs_info->fat_type = fat_type; + if (!fat_calc_sizes (fs->geom->length, 0, + fs_info->fat_type, + fs_info->root_dir_sector_count, + &fs_info->cluster_sectors, + &fs_info->cluster_count, + &fs_info->fat_sectors)) { + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Partition too big/small for a %s file system."), + (fat_type == FAT_TYPE_FAT16) + ? fat16_type.name + : fat32_type.name); + goto error_free_fs; + } + + fs_info->cluster_size = fs_info->cluster_sectors * 512; + + fs_info->fat_offset = fat_min_reserved_sector_count (fs_info->fat_type); + fs_info->dir_entries_per_cluster + = fs_info->cluster_size / sizeof (FatDirEntry); + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + /* FAT16 */ + fs->type = &fat16_type; + + if (fs_info->cluster_count + > fat_max_cluster_count (fs_info->fat_type)) { + fs_info->cluster_count + = fat_max_cluster_count (fs_info->fat_type); + } + + fs_info->root_dir_sector_count + = FAT_ROOT_DIR_ENTRY_COUNT + / (512 / sizeof (FatDirEntry)); + fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT; + fs_info->root_dir_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + fs_info->cluster_offset + = fs_info->root_dir_offset + + fs_info->root_dir_sector_count; + } else { + /* FAT32 */ + fs->type = &fat32_type; + + fs_info->info_sector_offset = 1; + fs_info->boot_sector_backup_offset = 6; + + fs_info->root_dir_sector_count = 0; + fs_info->root_dir_entry_count = 0; + fs_info->root_dir_offset = 0; + + fs_info->cluster_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + } + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + fs_info->fat = fat_table_new (fs_info->fat_type, table_size); + if (!fs_info->fat) + goto error_free_fs; + fat_table_set_cluster_count (fs_info->fat, fs_info->cluster_count); + if (!fat_alloc_buffers (fs)) + goto error_free_fat_table; + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + fs_info->root_cluster + = fat_table_alloc_cluster (fs_info->fat); + fat_table_set_eof (fs_info->fat, fs_info->root_cluster); + memset (fs_info->buffer, 0, fs_info->cluster_size); + if (!fat_write_cluster (fs, fs_info->buffer, + fs_info->root_cluster)) + return 0; + } + + fs_info->serial_number = _gen_new_serial_number (); + + if (!fat_boot_sector_set_boot_code (&fs_info->boot_sector)) + goto error_free_buffers; + if (!fat_boot_sector_generate (&fs_info->boot_sector, fs)) + goto error_free_buffers; + if (!fat_boot_sector_write (&fs_info->boot_sector, fs)) + goto error_free_buffers; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!fat_info_sector_generate (&fs_info->info_sector, fs)) + goto error_free_buffers; + if (!fat_info_sector_write (&fs_info->info_sector, fs)) + goto error_free_buffers; + } + + if (!fat_table_write_all (fs_info->fat, fs)) + goto error_free_buffers; + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + if (!fat_root_dir_clear (fs)) + goto error_free_buffers; + } + + return fs; + +error_free_buffers: + fat_free_buffers (fs); +error_free_fat_table: + fat_table_destroy (fs_info->fat); +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +PedFileSystem* +fat_create_fat16 (PedGeometry* geom, PedTimer* timer) +{ + return fat_create (geom, FAT_TYPE_FAT16, timer); +} + +PedFileSystem* +fat_create_fat32 (PedGeometry* geom, PedTimer* timer) +{ + return fat_create (geom, FAT_TYPE_FAT32, timer); +} + +int +fat_close (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + fat_free_buffers (fs); + fat_table_destroy (fs_info->fat); + fat_free (fs); + return 1; +} + +/* Hack: just resize the file system outside of its boundaries! */ +PedFileSystem* +fat_copy (const PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + PedFileSystem* new_fs; + + new_fs = ped_file_system_open (fs->geom); + if (!new_fs) + goto error; + if (!ped_file_system_resize (new_fs, geom, timer)) + goto error_close_new_fs; + return new_fs; + +error_close_new_fs: + ped_file_system_close (new_fs); +error: + return 0; +} + +static int +_compare_fats (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTable* table_copy; + FatCluster table_size; + int i; + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + + table_copy = fat_table_new (fs_info->fat_type, table_size); + if (!table_copy) + goto error; + + for (i = 1; i < fs_info->fat_table_count; i++) { + if (!fat_table_read (table_copy, fs, i)) + goto error_free_table_copy; + if (!fat_table_compare (fs_info->fat, table_copy)) { + if (ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The FATs don't match. If you don't know " + "what this means, then select cancel, run " + "scandisk on the file system, and then come " + "back.")) + != PED_EXCEPTION_IGNORE) + goto error_free_table_copy; + } + } + + fat_table_destroy (table_copy); + return 1; + +error_free_table_copy: + fat_table_destroy (table_copy); +error: + return 0; +} + +int +fat_check (PedFileSystem* fs, PedTimer* timer) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector cluster_sectors; + FatCluster cluster_count; + PedSector fat_sectors; + PedSector align_sectors; + FatCluster info_free_clusters; + + align_sectors = fs_info->fat_offset + - fat_min_reserved_sector_count (fs_info->fat_type); + + if (!fat_calc_sizes (fs->geom->length, + align_sectors, + fs_info->fat_type, + fs_info->root_dir_sector_count, + &cluster_sectors, + &cluster_count, + &fat_sectors)) { + if (ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_IGNORE_CANCEL, + _("There are no possible configurations for this FAT " + "type.")) + != PED_EXCEPTION_IGNORE) + goto error; + } + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + if (cluster_sectors != fs_info->cluster_sectors + || cluster_count != fs_info->cluster_count + || fat_sectors != fs_info->fat_sectors) { + if (ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("File system doesn't have expected sizes for " + "Windows to like it. " + "Cluster size is %dk (%dk expected); " + "number of clusters is %d (%d expected); " + "size of FATs is %d sectors (%d expected)."), + (int) fs_info->cluster_sectors / 2, + (int) cluster_sectors / 2, + (int) fs_info->cluster_count, + (int) cluster_count, + (int) fs_info->fat_sectors, + (int) fat_sectors) + != PED_EXCEPTION_IGNORE) + goto error; + } + } + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + info_free_clusters + = PED_LE32_TO_CPU (fs_info->info_sector.free_clusters); + if (info_free_clusters != (FatCluster) -1 + && info_free_clusters != fs_info->fat->free_cluster_count) { + if (ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("File system is reporting the free space as " + "%d clusters, not %d clusters."), + info_free_clusters, + fs_info->fat->free_cluster_count) + != PED_EXCEPTION_IGNORE) + goto error; + } + } + + if (!_compare_fats (fs)) + goto error; + + fs->checked = 1; + return 1; /* existence of fs implies consistency ;-) */ + +error: + return 0; +} + +/* Calculates how much space there will be in clusters in: + * old_fs intersect the-new-fs + */ +static PedSector +_calc_resize_data_size ( + const PedFileSystem* old_fs, + PedSector new_cluster_sectors, + FatCluster new_cluster_count, + PedSector new_fat_size) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + PedSector fat_size_delta; + + fat_size_delta = old_fs_info->fat_sectors - new_fat_size; + return new_cluster_sectors * new_cluster_count - fat_size_delta * 2; +} + +static int +_test_resize_size (const PedFileSystem* fs, + PedSector length, PedSector min_data_size) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedGeometry geom; + PedSector _cluster_sectors; + FatCluster _cluster_count; + PedSector _fat_size; + + ped_geometry_init (&geom, fs->geom->dev, fs->geom->start, length); + + if (fat_calc_resize_sizes ( + &geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT16, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &_cluster_sectors, + &_cluster_count, + &_fat_size) + && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count, + _fat_size) + >= min_data_size) + return 1; + + if (fat_calc_resize_sizes ( + &geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT32, + 0, + fs_info->cluster_sectors, + &_cluster_sectors, + &_cluster_count, + &_fat_size) + && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count, + _fat_size) + >= min_data_size) + return 1; + + return 0; +} + +/* does a binary search (!) for the mininum size. Too hard to compute directly + * (see calc_sizes() for why!) + */ +static PedSector +_get_min_resize_size (const PedFileSystem* fs, PedSector min_data_size) +{ + PedSector min_length = 0; + PedSector max_length = fs->geom->length; + PedSector length; + + while (min_length < max_length - 1) { + length = (min_length + max_length) / 2; + if (_test_resize_size (fs, length, min_data_size)) + max_length = length; + else + min_length = length; + } + +/* adds a bit of leeway (64 sectors), for resolving extra issues, like root + * directory allocation, that aren't covered here. + */ + return max_length + 64; +} + +PedConstraint* +fat_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedGeometry full_dev; + PedSector min_cluster_count; + FatCluster used_clusters; + PedSector min_data_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + used_clusters = fs_info->fat->cluster_count + - fs_info->fat->free_cluster_count; + min_cluster_count = used_clusters + fs_info->total_dir_clusters; + min_data_size = min_cluster_count * fs_info->cluster_sectors; + + return ped_constraint_new (ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + _get_min_resize_size (fs, min_data_size), + dev->length); +} + +PedConstraint* +fat_get_resize_constraint (const PedFileSystem* fs) +{ + return fat_get_copy_constraint (fs, fs->geom->dev); +} + +/* FIXME: fat_calc_sizes() needs to say "too big" or "too small", or + * something. This is a really difficult (maths) problem to do + * nicely... + * So, this algorithm works if dev->length / 2 is a valid fat_type + * size. (Which is how I got the magic numbers below) + */ +#if 0 +/* returns: -1 too small, 0 ok, 1 too big */ +static int +_test_create_size (PedSector length, FatType fat_type, + PedSector cluster_sectors, PedSector cluster_count) +{ + PedSector rootdir_sectors; + PedSector _cluster_sectors; + FatCluster _cluster_count; + PedSector _fat_size; + + rootdir_sectors = (fat_type == FAT_TYPE_FAT16) ? 16 : 0; + + if (!fat_calc_sizes (length, 0, fat_type, rootdir_sectors, + &_cluster_sectors, &_cluster_count, &_fat_size)) + return -1; // XXX: doesn't work... can't see a better way! + + if (_cluster_sectors < cluster_sectors) + return -1; + if (_cluster_sectors > cluster_sectors) + return 1; + + if (_cluster_count < cluster_count) + return -1; + if (_cluster_count > cluster_count) + return 1; + + return 0; +} + +static PedSector +_get_create_size (PedSector upper_bound, FatType fat_type, + PedSector cluster_sectors, FatCluster cluster_count) +{ + PedSector min_length = 0; + PedSector max_length = upper_bound; + PedSector length; + + while (1) { + length = (min_length + max_length) / 2; + switch (_test_create_size (length, fat_type, cluster_sectors, + cluster_count)) { + case -1: min_length = length; break; + case 0: return length; + case 1: max_length = length; break; + } + /* hack... won't always be able to get max cluster count + * with max cluster size, etc. */ + if (max_length - min_length == 1) + return min_length; + } + + return 0; /* shut gcc up */ +} +#endif + +PedConstraint* +fat_get_create_constraint_fat16 (const PedDevice* dev) +{ + PedGeometry full_dev; + PedSector min_size; + PedSector max_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + +#if 0 + min_size = _get_create_size (dev->length, FAT_TYPE_FAT16, + fat_min_cluster_size (FAT_TYPE_FAT16), + fat_min_cluster_count (FAT_TYPE_FAT16)); + max_size = _get_create_size (dev->length, FAT_TYPE_FAT16, + fat_max_cluster_size (FAT_TYPE_FAT16), + fat_max_cluster_count (FAT_TYPE_FAT16)); + if (!min_size) + return NULL; +#else + min_size = 65794; + max_size = 2097153; +#endif + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + min_size, max_size); +} + +PedConstraint* +fat_get_create_constraint_fat32 (const PedDevice* dev) +{ + PedGeometry full_dev; + PedSector min_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + +#if 0 + min_size = _get_create_size (dev->length, FAT_TYPE_FAT32, + fat_min_cluster_size (FAT_TYPE_FAT32), + fat_min_cluster_count (FAT_TYPE_FAT32)); + if (!min_size) + return NULL; +#else + min_size = 525224; +#endif + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + min_size, dev->length); +} +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps fat16_ops = { + probe: fat_probe_fat16, +#ifndef DISCOVER_ONLY + clobber: fat_clobber, + open: fat_open, + create: fat_create_fat16, + close: fat_close, + check: fat_check, + resize: fat_resize, + copy: fat_copy, + get_create_constraint: fat_get_create_constraint_fat16, + get_resize_constraint: fat_get_resize_constraint, + get_copy_constraint: fat_get_copy_constraint, +#else /* !DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL, +#endif /* !DISCOVER_ONLY */ +}; + +static PedFileSystemOps fat32_ops = { + probe: fat_probe_fat32, +#ifndef DISCOVER_ONLY + clobber: fat_clobber, + open: fat_open, + create: fat_create_fat32, + close: fat_close, + check: fat_check, + resize: fat_resize, + copy: fat_copy, + get_create_constraint: fat_get_create_constraint_fat32, + get_resize_constraint: fat_get_resize_constraint, + get_copy_constraint: fat_get_copy_constraint, +#else /* !DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL, +#endif /* !DISCOVER_ONLY */ +}; + +#define FAT_BLOCK_SIZES ((int[2]){512, 0}) + +PedFileSystemType fat16_type = { + next: NULL, + ops: &fat16_ops, + name: "fat16", + block_sizes: FAT_BLOCK_SIZES +}; + +PedFileSystemType fat32_type = { + next: NULL, + ops: &fat32_ops, + name: "fat32", + block_sizes: FAT_BLOCK_SIZES +}; + +void +ped_file_system_fat_init () +{ + if (sizeof (FatBootSector) != 512) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("GNU Parted was miscompiled: the FAT boot sector " + "should be 512 bytes. FAT support will be disabled.")); + } else { + ped_file_system_type_register (&fat16_type); + ped_file_system_type_register (&fat32_type); + } +} + +void +ped_file_system_fat_done () +{ + ped_file_system_type_unregister (&fat16_type); + ped_file_system_type_unregister (&fat32_type); +} + diff --git a/libparted/fs/fat/fat.h b/libparted/fs/fat/fat.h new file mode 100644 index 0000000..c571e90 --- /dev/null +++ b/libparted/fs/fat/fat.h @@ -0,0 +1,161 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef FAT_H_INCLUDED +#define FAT_H_INCLUDED + +#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 <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define BUFFER_SIZE 1024 /* buffer size in sectors (512 bytes) */ + +typedef uint32_t FatCluster; +typedef int32_t FatFragment; + +enum _FatType { + FAT_TYPE_FAT12, + FAT_TYPE_FAT16, + FAT_TYPE_FAT32 +}; +typedef enum _FatType FatType; + +typedef struct _FatSpecific FatSpecific; +typedef struct _FatDirEntry FatDirEntry; + +/* FIXME: YUCKY */ +#include "table.h" +#include "bootsector.h" +#include "context.h" +#include "fatio.h" +#include "traverse.h" +#include "calc.h" +#include "count.h" +#include "clstdup.h" + +struct _FatDirEntry { + char name[8]; + uint8_t extension[3]; + uint8_t attributes; + uint8_t is_upper_case_name; + uint8_t creation_time_low; /* milliseconds */ + uint16_t creation_time_high; + uint16_t creation_date; + uint16_t access_date; + uint16_t first_cluster_high; /* for FAT32 */ + uint16_t time; + uint16_t date; + uint16_t first_cluster; + uint32_t length; +} __attribute__ ((packed)); + +struct _FatSpecific { + FatBootSector boot_sector; /* structure of boot sector */ + FatInfoSector info_sector; /* fat32-only information sector */ + + int logical_sector_size; /* illogical sector size :-) */ + PedSector sector_count; + + int sectors_per_track; /* BIOS CHS stuff (S) */ + int heads; /* BIOS CHS stuff (H) */ + + int cluster_size; + PedSector cluster_sectors; + FatCluster cluster_count; + int dir_entries_per_cluster; + + FatType fat_type; + int fat_table_count; + PedSector fat_sectors; + + uint32_t serial_number; + + PedSector info_sector_offset; /* FAT32 only */ + PedSector fat_offset; + PedSector root_dir_offset; /* non-FAT32 */ + PedSector cluster_offset; + PedSector boot_sector_backup_offset; + + FatCluster root_cluster; /* FAT32 only */ + int root_dir_entry_count; /* non-FAT32 */ + PedSector root_dir_sector_count; /* non-FAT32 */ + FatCluster total_dir_clusters; + + FatTable* fat; + FatClusterInfo* cluster_info; + + PedSector buffer_sectors; + char* buffer; + + int frag_size; + PedSector frag_sectors; + FatFragment frag_count; + FatFragment buffer_frags; + FatFragment cluster_frags; +}; + +#define FAT_SPECIFIC(fs) ((FatSpecific*) fs->type_specific) + +#define FAT_ROOT 0 + +#define DELETED_FLAG 0xe5 + +#define READONLY_ATTR 0x01 +#define HIDDEN_ATTR 0x02 +#define SYSTEM_ATTR 0x04 +#define VOLUME_LABEL_ATTR 0x08 +#define VFAT_ATTR 0x0f +#define DIRECTORY_ATTR 0x10 +#define ARCH_ATTR 0x20 + +#define MAX_FAT12_CLUSTERS 4086 +#define MAX_FAT16_CLUSTERS 65526 +#define MAX_FAT32_CLUSTERS 2000000 + +#define FAT_ROOT_DIR_ENTRY_COUNT 512 + +extern PedFileSystemType fat16_type; +extern PedFileSystemType fat32_type; + +extern void fat_print (const PedFileSystem* fs); + +extern PedFileSystem* fat_alloc (const PedGeometry* geom); +extern void fat_free (PedFileSystem* fs); +extern int fat_alloc_buffers (PedFileSystem* fs); +extern void fat_free_buffers (PedFileSystem* fs); + +extern int fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer); + +extern int fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors); + +#endif /* FAT_H_INCLUDED */ diff --git a/libparted/fs/fat/fatio.c b/libparted/fs/fat/fatio.c new file mode 100644 index 0000000..e88f85d --- /dev/null +++ b/libparted/fs/fat/fatio.c @@ -0,0 +1,151 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "fat.h" +#include "fatio.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> + +#ifndef DISCOVER_ONLY + +int +fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_frag_to_sector (fs, frag); + PedSector sector_count = count * fs_info->frag_sectors; + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0); + + return ped_geometry_read (fs->geom, buf, sector, sector_count); +} + +int +fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_read_fragments (fs, buf, frag, 1); +} + +int +fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_frag_to_sector (fs, frag); + PedSector sector_count = count * fs_info->frag_sectors; + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0); + + return ped_geometry_write (fs->geom, buf, sector, sector_count); +} + +int +fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_write_fragments (fs, buf, frag, 1); +} + +int +fat_write_sync_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + if (!fat_write_fragments (fs, buf, frag, count)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +int +fat_write_sync_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_write_sync_fragments (fs, buf, frag, 1); +} + +int +fat_read_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_cluster_to_sector (fs, cluster); + PedSector sector_count = count * fs_info->cluster_sectors; + + PED_ASSERT (cluster >= 2 + && cluster + count - 1 < fs_info->cluster_count + 2, + return 0); + + return ped_geometry_read (fs->geom, buf, sector, sector_count); +} + +int +fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + return fat_read_clusters (fs, buf, cluster, 1); +} + +int +fat_write_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_cluster_to_sector (fs, cluster); + PedSector sector_count = count * fs_info->cluster_sectors; + + PED_ASSERT (cluster >= 2 + && cluster + count - 1 < fs_info->cluster_count + 2, + return 0); + + return ped_geometry_write (fs->geom, buf, sector, sector_count); +} + +int +fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + return fat_write_clusters (fs, buf, cluster, 1); +} + +int +fat_write_sync_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + if (!fat_write_clusters (fs, buf, cluster, count)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +int +fat_write_sync_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + if (!fat_write_cluster (fs, buf, cluster)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/fatio.h b/libparted/fs/fat/fatio.h new file mode 100644 index 0000000..a37158a --- /dev/null +++ b/libparted/fs/fat/fatio.h @@ -0,0 +1,49 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef FATIO_H_INCLUDED +#define FATIO_H_INCLUDED + +#include "fat.h" + +extern int fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count); +extern int fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count); +extern int fat_write_sync_fragments (PedFileSystem* fs, char* buf, + FatFragment frag, FatFragment count); + +extern int fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag); +extern int fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag); +extern int fat_write_sync_fragment (PedFileSystem* fs, char* buf, + FatFragment frag); + +extern int fat_read_clusters (PedFileSystem* fs, char* buf, FatCluster cluster, + FatCluster count); +extern int fat_write_clusters (PedFileSystem* fs, char* buf, FatCluster cluster, + FatCluster count); +extern int fat_write_sync_clusters (PedFileSystem* fs, char* buf, + FatCluster cluster, FatCluster count); + +extern int fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster); +extern int fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster); +extern int fat_write_sync_cluster (PedFileSystem* fs, char *buf, + FatCluster cluster); + +#endif /* FATIO_H_INCLUDED */ diff --git a/libparted/fs/fat/resize.c b/libparted/fs/fat/resize.c new file mode 100644 index 0000000..681aa48 --- /dev/null +++ b/libparted/fs/fat/resize.c @@ -0,0 +1,870 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "fat.h" +#include "traverse.h" +#include "count.h" +#include "fatio.h" +#include "calc.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <stdarg.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +/* Recursively builds (i.e. makes consistent) the duplicated directory tree + * (leaving the original directory tree in tact) + */ +static int +fat_construct_directory (FatOpContext* ctx, FatTraverseInfo* trav_info) +{ + FatTraverseInfo* sub_dir_info; + FatDirEntry* dir_entry; + FatCluster old_first_cluster; + + while ( (dir_entry = fat_traverse_next_dir_entry (trav_info)) ) { + if (fat_dir_entry_is_null_term (dir_entry)) + break; + if (!fat_dir_entry_has_first_cluster (dir_entry, ctx->old_fs)) + continue; + + fat_traverse_mark_dirty (trav_info); + + old_first_cluster = fat_dir_entry_get_first_cluster (dir_entry, + ctx->old_fs); + fat_dir_entry_set_first_cluster (dir_entry, ctx->new_fs, + fat_op_context_map_cluster (ctx, old_first_cluster)); + + if (fat_dir_entry_is_directory (dir_entry) + && dir_entry->name [0] != '.') { + sub_dir_info + = fat_traverse_directory (trav_info, dir_entry); + if (!sub_dir_info) + return 0; + if (!fat_construct_directory (ctx, sub_dir_info)) + return 0; + } + } + /* remove "stale" entries at the end */ + while ((dir_entry = fat_traverse_next_dir_entry (trav_info))) { + memset (dir_entry, 0, sizeof (FatDirEntry)); + fat_traverse_mark_dirty (trav_info); + } + fat_traverse_complete (trav_info); + return 1; +} + +static int +duplicate_legacy_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + PED_ASSERT (old_fs_info->root_dir_sector_count + == new_fs_info->root_dir_sector_count, return 0); + + if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer, + old_fs_info->root_dir_offset, + old_fs_info->root_dir_sector_count)) + return 0; + + if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer, + new_fs_info->root_dir_offset, + new_fs_info->root_dir_sector_count)) + return 0; + + return 1; +} + +/* + Constructs the new directory tree for legacy (FAT16) file systems. +*/ +static int +fat_construct_legacy_root (FatOpContext* ctx) +{ + FatTraverseInfo* trav_info; + + if (!duplicate_legacy_root_dir (ctx)) + return 0; + trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, "\\"); + return fat_construct_directory (ctx, trav_info); +} + +/* + Constructs the new directory tree for new (FAT32) file systems. +*/ +static int +fat_construct_root (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatTraverseInfo* trav_info; + + trav_info = fat_traverse_begin (ctx->new_fs, new_fs_info->root_cluster, + "\\"); + fat_construct_directory (ctx, trav_info); + return 1; +} + +/* Converts the root directory between FAT16 and FAT32. NOTE: this code + * can also do no conversion. I'm leaving fat_construct_directory(), because + * it's really pretty :-) It also leaves a higher chance of deleted file + * recovery, because it doesn't remove redundant entries. (We do this here, + * because brain-damaged FAT16 has an arbitary limit on root directory entries, + * so we save room) + */ +static int +fat_convert_directory (FatOpContext* ctx, FatTraverseInfo* old_trav, + FatTraverseInfo* new_trav) +{ + FatTraverseInfo* sub_old_dir_trav; + FatTraverseInfo* sub_new_dir_trav; + FatDirEntry* new_dir_entry; + FatDirEntry* old_dir_entry; + FatCluster old_first_cluster; + + while ( (old_dir_entry = fat_traverse_next_dir_entry (old_trav)) ) { + if (fat_dir_entry_is_null_term (old_dir_entry)) + break; + if (!fat_dir_entry_is_active (old_dir_entry)) + continue; + + new_dir_entry = fat_traverse_next_dir_entry (new_trav); + if (!new_dir_entry) { + return ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("There's not enough room in the root " + "directory for all of the files. Either " + "cancel, or ignore to lose the files.")) + == PED_EXCEPTION_IGNORE; + } + + *new_dir_entry = *old_dir_entry; + fat_traverse_mark_dirty (new_trav); + + if (!fat_dir_entry_has_first_cluster (old_dir_entry, + ctx->old_fs)) + continue; + + old_first_cluster = fat_dir_entry_get_first_cluster ( + old_dir_entry, ctx->old_fs); + fat_dir_entry_set_first_cluster (new_dir_entry, ctx->new_fs, + fat_op_context_map_cluster (ctx, old_first_cluster)); + + if (fat_dir_entry_is_directory (old_dir_entry) + && old_dir_entry->name [0] != '.') { + sub_old_dir_trav + = fat_traverse_directory (old_trav, old_dir_entry); + sub_new_dir_trav + = fat_traverse_directory (new_trav, new_dir_entry); + if (!sub_old_dir_trav || !sub_new_dir_trav) + return 0; + + if (!fat_convert_directory (ctx, sub_old_dir_trav, + sub_new_dir_trav)) + return 0; + } + } + + /* remove "stale" entries at the end, just in case there is some + * overlap + */ + while ((new_dir_entry = fat_traverse_next_dir_entry (new_trav))) { + memset (new_dir_entry, 0, sizeof (FatDirEntry)); + fat_traverse_mark_dirty (new_trav); + } + + fat_traverse_complete (old_trav); + fat_traverse_complete (new_trav); + return 1; +} + +static void +clear_cluster (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + memset (fs_info->buffer, 0, fs_info->cluster_size); + fat_write_cluster (fs, fs_info->buffer, cluster); +} + +/* This MUST be called BEFORE the fat_construct_new_fat(), because cluster + * allocation depend on the old FAT. The reason is, old clusters may + * still be needed during the resize, (particularly clusters in the directory + * tree) even if they will be discarded later. + */ +static int +alloc_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster i; + FatCluster cluster; + FatCluster cluster_count; + + PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT32, return 0); + + cluster_count = ped_div_round_up ( + PED_MAX (16, old_fs_info->root_dir_sector_count), + new_fs_info->cluster_sectors); + + for (i = 0; i < cluster_count; i++) { + cluster = fat_table_alloc_check_cluster (new_fs_info->fat, + ctx->new_fs); + if (!cluster) + return 0; + ctx->new_root_dir [i] = cluster; + clear_cluster (ctx->new_fs, cluster); + } + ctx->new_root_dir [i] = 0; + new_fs_info->root_cluster = ctx->new_root_dir [0]; + return 1; +} + +/* when converting FAT32 -> FAT16 + * fat_duplicate clusters() duplicated the root directory unnecessarily. + * Let's free it. + * + * This must be called AFTER fat_construct_new_fat(). (otherwise, our + * changes just get overwritten) + */ +static int +free_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster old_cluster; + FatFragment i; + + PED_ASSERT (old_fs_info->fat_type == FAT_TYPE_FAT32, return 0); + PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT16, return 0); + + for (old_cluster = old_fs_info->root_cluster; + !fat_table_is_eof (old_fs_info->fat, old_cluster); + old_cluster = fat_table_get (old_fs_info->fat, old_cluster)) { + FatFragment old_frag; + old_frag = fat_cluster_to_frag (ctx->old_fs, old_cluster); + for (i = 0; i < new_fs_info->cluster_frags; i++) { + FatFragment new_frag; + FatCluster new_clst; + new_frag = fat_op_context_map_fragment (ctx, + old_frag + i); + new_clst = fat_frag_to_cluster (ctx->old_fs, new_frag); + if (!fat_table_set_avail (new_fs_info->fat, new_clst)) + return 0; + } + } + + return 1; +} + +static int +fat_clear_root_dir (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int i; + + PED_ASSERT (fs_info->fat_type == FAT_TYPE_FAT16, return 0); + PED_ASSERT (fs_info->root_dir_sector_count, return 0); + + memset (fs_info->buffer, 0, 512); + + for (i = 0; i < fs_info->root_dir_sector_count; i++) { + if (!ped_geometry_write (fs->geom, fs_info->buffer, + fs_info->root_dir_offset + i, 1)) { + if (ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Error writing to the root directory.")) + == PED_EXCEPTION_CANCEL) + return 0; + } + } + return 1; +} + +static int +fat_construct_converted_tree (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatTraverseInfo* old_trav_info; + FatTraverseInfo* new_trav_info; + + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_trav_info = fat_traverse_begin (ctx->new_fs, + new_fs_info->root_cluster, "\\"); + old_trav_info = fat_traverse_begin (ctx->old_fs, FAT_ROOT, + "\\"); + } else { + fat_clear_root_dir (ctx->new_fs); + new_trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, + "\\"); + old_trav_info = fat_traverse_begin (ctx->old_fs, + old_fs_info->root_cluster, "\\"); + } + if (!new_trav_info || !old_trav_info) + return 0; + if (!fat_convert_directory (ctx, old_trav_info, new_trav_info)) + return 0; + return 1; +} + +/* + Constructs the new directory tree to match the new file locations. +*/ +static int +fat_construct_dir_tree (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + + if (new_fs_info->fat_type == old_fs_info->fat_type) { + switch (old_fs_info->fat_type) { + case FAT_TYPE_FAT16: + return fat_construct_legacy_root (ctx); + + case FAT_TYPE_FAT32: + return fat_construct_root (ctx); + } + } else { + return fat_construct_converted_tree (ctx); + } + + return 0; +} + +static FatFragment +_get_next_old_frag (FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatCluster cluster; + FatCluster next_cluster; + + if ((frag + 1) % old_fs_info->cluster_frags != 0) { + if (fat_is_fragment_active (ctx->old_fs, frag + 1)) + return frag + 1; + else + return -1; + } else { + cluster = fat_frag_to_cluster (ctx->old_fs, frag); + next_cluster = fat_table_get (old_fs_info->fat, cluster); + + if (fat_table_is_eof (old_fs_info->fat, next_cluster)) + return -1; + else + return fat_cluster_to_frag (ctx->old_fs, next_cluster); + } +} + +/* + Constructs the new fat for the resized file system. +*/ +static int +fat_construct_new_fat (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment old_frag; + FatCluster new_cluster; + FatFragment new_frag; + FatFragment old_next_frag; + FatFragment new_next_frag; + FatCluster new_next_cluster; + FatClusterFlag flag; + int i; + + fat_table_clear (new_fs_info->fat); + if (!fat_table_set_cluster_count (new_fs_info->fat, + new_fs_info->cluster_count)) + return 0; + + for (old_frag = 0; old_frag < old_fs_info->frag_count; old_frag++) { + flag = fat_get_fragment_flag (ctx->old_fs, old_frag); + if (flag == FAT_FLAG_FREE) + continue; + if (flag == FAT_FLAG_BAD) { + new_frag = fat_op_context_map_static_fragment ( + ctx, old_frag); + if (new_frag == -1) + continue; + new_cluster = fat_frag_to_cluster (ctx->new_fs, + new_frag); + fat_table_set_bad (new_fs_info->fat, new_cluster); + continue; + } + + new_frag = fat_op_context_map_fragment (ctx, old_frag); + new_cluster = fat_frag_to_cluster (ctx->new_fs, new_frag); + + old_next_frag = _get_next_old_frag (ctx, old_frag); + if (old_next_frag == -1) { + fat_table_set_eof (new_fs_info->fat, new_cluster); + continue; + } + + new_next_frag = fat_op_context_map_fragment (ctx, + old_next_frag); + PED_ASSERT (new_next_frag != -1, return 0); + + new_next_cluster = fat_frag_to_cluster (ctx->new_fs, + new_next_frag); + PED_ASSERT (new_next_cluster != new_cluster, return 0); + + fat_table_set (new_fs_info->fat, new_cluster, new_next_cluster); + } + +#if 0 +#ifdef PED_VERBOSE + for (old_cluster=2; old_cluster < old_fs_info->cluster_count+2; + old_cluster++) { + if (fat_table_is_available (old_fs_info->fat, old_cluster)) + continue; + + printf ("%d->%d\t(next: %d->%d)\n", + old_cluster, + ctx->remap [old_cluster], + fat_table_get (old_fs_info->fat, old_cluster), + fat_table_get (new_fs_info->fat, + ctx->remap [old_cluster])); + } +#endif /* PED_VERBOSE */ +#endif + + if (old_fs_info->fat_type == FAT_TYPE_FAT32 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_fs_info->root_cluster + = fat_op_context_map_cluster (ctx, + old_fs_info->root_cluster); + } + + if (old_fs_info->fat_type == FAT_TYPE_FAT16 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + for (i=0; ctx->new_root_dir[i+1]; i++) { + fat_table_set (new_fs_info->fat, + ctx->new_root_dir[i], + ctx->new_root_dir[i+1]); + } + fat_table_set_eof (new_fs_info->fat, ctx->new_root_dir[i]); + } + + return 1; +} + +static int +ask_type (PedFileSystem* fs, int fat16_ok, int fat32_ok, FatType* out_fat_type) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedExceptionOption status; + char* fat16_msg; + char* fat32_msg; + + if (fs_info->fat_type == FAT_TYPE_FAT16) + fat16_msg = _("If you leave your file system as FAT16, " + "then you will have no problems."); + else + fat16_msg = _("If you convert to FAT16, and MS Windows " + "is installed on this partition, then " + "you must re-install the MS Windows boot " + "loader. If you want to do this, you " + "should consult the Parted manual (or " + "your distribution's manual)."); + + if (fs_info->fat_type == FAT_TYPE_FAT32) + fat32_msg = _("If you leave your file system as FAT32, " + "then you will not introduce any new " + "problems."); + else + fat32_msg = _("If you convert to FAT32, and MS Windows " + "is installed on this partition, then " + "you must re-install the MS Windows boot " + "loader. If you want to do this, you " + "should consult the Parted manual (or " + "your distribution's manual). Also, " + "converting to FAT32 will make the file " + "system unreadable by MS DOS, MS Windows " + "95a, and MS Windows NT."); + + if (fat16_ok && fat32_ok) { + status = ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_YES_NO_CANCEL, + _("%s %s %s"), + _("Would you like to use FAT32?"), + fat16_msg, + fat32_msg); + + switch (status) { + case PED_EXCEPTION_YES: + *out_fat_type = FAT_TYPE_FAT32; + return 1; + + case PED_EXCEPTION_NO: + *out_fat_type = FAT_TYPE_FAT16; + return 1; + + case PED_EXCEPTION_UNHANDLED: + *out_fat_type = fs_info->fat_type; + return 1; + + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + if (fat16_ok) { + if (fs_info->fat_type != FAT_TYPE_FAT16) { + status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK_CANCEL, + _("%s %s"), + _("The file system can only be resized to this " + "size by converting to FAT16."), + fat16_msg); + if (status == PED_EXCEPTION_CANCEL) + return 0; + } + *out_fat_type = FAT_TYPE_FAT16; + return 1; + } + + if (fat32_ok) { + if (fs_info->fat_type != FAT_TYPE_FAT32) { + status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK_CANCEL, + _("%s %s"), + _("The file system can only be resized to this " + "size by converting to FAT32."), + fat32_msg); + if (status == PED_EXCEPTION_CANCEL) + return 0; + } + *out_fat_type = FAT_TYPE_FAT32; + return 1; + } + + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("GNU Parted cannot resize this partition to this size. " + "We're working on it!")); + + return 0; +} + +/* For resize operations: determine if the file system must be FAT16 or FAT32, + * or either. If the new file system must be FAT32, then query for + * confirmation. If either file system can be used, query for which one. + */ +static int +get_fat_type (PedFileSystem* fs, const PedGeometry* new_geom, + FatType* out_fat_type) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedExceptionOption status; + PedSector fat16_cluster_sectors; + PedSector fat32_cluster_sectors; + FatCluster dummy_cluster_count; + PedSector dummy_fat_sectors; + int fat16_ok; + int fat32_ok; + + fat16_ok = fat_calc_resize_sizes ( + new_geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT16, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &fat16_cluster_sectors, + &dummy_cluster_count, + &dummy_fat_sectors); + + fat32_ok = fat_calc_resize_sizes ( + new_geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT32, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &fat32_cluster_sectors, + &dummy_cluster_count, + &dummy_fat_sectors); + + return ask_type (fs, fat16_ok, fat32_ok, out_fat_type); +} + +/* Creates the PedFileSystem struct for the new resized file system, and + sticks it in a FatOpContext. At the end of the process, the original + (ctx->old_fs) is destroyed, and replaced with the new one (ctx->new_fs). + */ +static FatOpContext* +create_resize_context (PedFileSystem* fs, const PedGeometry* new_geom) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatSpecific* new_fs_info; + PedFileSystem* new_fs; + PedSector new_cluster_sectors; + FatCluster new_cluster_count; + PedSector new_fat_sectors; + FatType new_fat_type; + PedSector root_dir_sector_count; + FatOpContext* context; + + /* hypothetical number of root dir sectors, if we end up using + * FAT16 + */ + if (fs_info->root_dir_sector_count) + root_dir_sector_count = fs_info->root_dir_sector_count; + else + root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT + * sizeof (FatDirEntry) / 512; + + if (!get_fat_type (fs, new_geom, &new_fat_type)) + return 0; + + fat_calc_resize_sizes (new_geom, fs_info->cluster_sectors, new_fat_type, + root_dir_sector_count, fs_info->cluster_sectors, + &new_cluster_sectors, &new_cluster_count, &new_fat_sectors); + + if (!fat_check_resize_geometry (fs, new_geom, new_cluster_sectors, + new_cluster_count)) + goto error; + + new_fs = fat_alloc (new_geom); + if (!new_fs) + goto error; + + new_fs_info = FAT_SPECIFIC (new_fs); + if (!new_fs_info) + goto error_free_new_fs; + +/* preserve boot code, etc. */ + memcpy (&new_fs_info->boot_sector, &fs_info->boot_sector, + sizeof (FatBootSector)); + memcpy (&new_fs_info->info_sector, &fs_info->info_sector, + sizeof (FatInfoSector)); + + new_fs_info->logical_sector_size = fs_info->logical_sector_size; + new_fs_info->sector_count = new_geom->length; + + new_fs_info->sectors_per_track = fs_info->sectors_per_track; + new_fs_info->heads = fs_info->heads; + + new_fs_info->cluster_size = new_cluster_sectors * 512; + new_fs_info->cluster_sectors = new_cluster_sectors; + new_fs_info->cluster_count = new_cluster_count; + new_fs_info->dir_entries_per_cluster = fs_info->dir_entries_per_cluster; + + new_fs_info->fat_type = new_fat_type; + new_fs_info->fat_table_count = 2; + new_fs_info->fat_sectors = new_fat_sectors; + + /* what about copying? */ + new_fs_info->serial_number = fs_info->serial_number; + + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_fs_info->info_sector_offset = 1; + new_fs_info->boot_sector_backup_offset = 6; + + new_fs_info->root_dir_offset = 0; + new_fs_info->root_dir_entry_count = 0; + new_fs_info->root_dir_sector_count = 0; + + /* we add calc_align_sectors to push the cluster_offset + forward, to keep the clusters aligned between the new + and old file systems + */ + new_fs_info->fat_offset + = fat_min_reserved_sector_count (FAT_TYPE_FAT32) + + fat_calc_align_sectors (new_fs, fs); + + new_fs_info->cluster_offset + = new_fs_info->fat_offset + + 2 * new_fs_info->fat_sectors; + } else { + new_fs_info->root_dir_sector_count = root_dir_sector_count; + new_fs_info->root_dir_entry_count + = root_dir_sector_count * 512 / sizeof (FatDirEntry); + + new_fs_info->fat_offset + = fat_min_reserved_sector_count (FAT_TYPE_FAT16) + + fat_calc_align_sectors (new_fs, fs); + + new_fs_info->root_dir_offset = new_fs_info->fat_offset + + 2 * new_fs_info->fat_sectors; + + new_fs_info->cluster_offset = new_fs_info->root_dir_offset + + new_fs_info->root_dir_sector_count; + } + + new_fs_info->total_dir_clusters = fs_info->total_dir_clusters; + + context = fat_op_context_new (new_fs, fs); + if (!context) + goto error_free_new_fs_info; + + if (!fat_op_context_create_initial_fat (context)) + goto error_free_context; + + if (!fat_alloc_buffers (new_fs)) + goto error_free_fat; + + return context; + +error_free_fat: + fat_table_destroy (new_fs_info->fat); +error_free_context: + ped_free (context); +error_free_new_fs_info: + ped_free (new_fs_info); +error_free_new_fs: + ped_free (new_fs); +error: + return NULL; +} + +static int +resize_context_assimilate (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + fat_free_buffers (ctx->old_fs); + fat_table_destroy (old_fs_info->fat); + ped_free (old_fs_info); + ped_geometry_destroy (ctx->old_fs->geom); + + ctx->old_fs->type_specific = ctx->new_fs->type_specific; + ctx->old_fs->geom = ctx->new_fs->geom; + ctx->old_fs->type = (new_fs_info->fat_type == FAT_TYPE_FAT16) + ? &fat16_type + : &fat32_type; + + ped_free (ctx->new_fs); + + fat_op_context_destroy (ctx); + + return 1; +} + +static int +resize_context_abort (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + fat_free_buffers (ctx->new_fs); + fat_table_destroy (new_fs_info->fat); + ped_free (new_fs_info); + ped_geometry_destroy (ctx->new_fs->geom); + ped_free (ctx->new_fs); + + fat_op_context_destroy (ctx); + + return 1; +} + +/* copies the "hidden" sectors, between the boot sector and the FAT. Required, + * for the Windows 98 FAT32 boot loader + */ +int +_copy_hidden_sectors (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + PedSector first = 1; + PedSector last; + PedSector count; + + /* nothing to copy for FAT16 */ + if (old_fs_info->fat_type == FAT_TYPE_FAT16 + || new_fs_info->fat_type == FAT_TYPE_FAT16) + return 1; + + last = PED_MIN (old_fs_info->fat_offset, new_fs_info->fat_offset) - 1; + count = last - first + 1; + + PED_ASSERT (count < BUFFER_SIZE, return 0); + + if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer, + first, count)) + return 0; + if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer, + first, count)) + return 0; + return 1; +} + +int +fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatSpecific* new_fs_info; + FatOpContext* ctx; + PedFileSystem* new_fs; + + ctx = create_resize_context (fs, geom); + if (!ctx) + goto error; + new_fs = ctx->new_fs; + new_fs_info = FAT_SPECIFIC (new_fs); + + if (!fat_duplicate_clusters (ctx, timer)) + goto error_abort_ctx; + if (fs_info->fat_type == FAT_TYPE_FAT16 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + if (!alloc_root_dir (ctx)) + goto error_abort_ctx; + } + if (!fat_construct_new_fat (ctx)) + goto error_abort_ctx; + if (fs_info->fat_type == FAT_TYPE_FAT32 + && new_fs_info->fat_type == FAT_TYPE_FAT16) { + if (!free_root_dir (ctx)) + goto error_abort_ctx; + } + if (!fat_construct_dir_tree (ctx)) + goto error_abort_ctx; + if (!fat_table_write_all (new_fs_info->fat, new_fs)) + goto error_abort_ctx; + + _copy_hidden_sectors (ctx); + fat_boot_sector_generate (&new_fs_info->boot_sector, new_fs); + fat_boot_sector_write (&new_fs_info->boot_sector, new_fs); + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + fat_info_sector_generate (&new_fs_info->info_sector, new_fs); + fat_info_sector_write (&new_fs_info->info_sector, new_fs); + } + + if (!resize_context_assimilate (ctx)) + goto error; + + return 1; + +error_abort_ctx: + resize_context_abort (ctx); +error: + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/table.c b/libparted/fs/fat/table.c new file mode 100644 index 0000000..920e269 --- /dev/null +++ b/libparted/fs/fat/table.c @@ -0,0 +1,466 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <string.h> + +#include <parted/endian.h> +#include "fat.h" + +#ifndef DISCOVER_ONLY + +FatTable* +fat_table_new (FatType fat_type, FatCluster size) +{ + FatTable* ft; + int entry_size = fat_table_entry_size (fat_type); + + ft = (FatTable*) ped_malloc (sizeof (FatTable)); + if (!ft) return NULL; + + ft->cluster_count = ft->free_cluster_count = size - 2; + +/* ensure there's some free room on the end, to finish off the sector */ + ft->size = ped_div_round_up (size * entry_size, 512) * 512 / entry_size; + ft->fat_type = fat_type; + ft->raw_size = ft->size * entry_size; + + ft->table = ped_malloc (ft->raw_size); + if (!ft->table) { + ped_free (ft); + return NULL; + } + + fat_table_clear (ft); + return ft; +} + +void +fat_table_destroy (FatTable* ft) +{ + ped_free (ft->table); + ped_free (ft); +} + +FatTable* +fat_table_duplicate (const FatTable* ft) +{ + FatTable* dup; + + dup = fat_table_new (ft->fat_type, ft->size); + if (!dup) return NULL; + + dup->cluster_count = ft->cluster_count; + dup->free_cluster_count = ft->free_cluster_count; + dup->bad_cluster_count = ft->bad_cluster_count; + dup->last_alloc = ft->last_alloc; + + memcpy (dup->table, ft->table, ft->raw_size); + + return dup; +} + +void +fat_table_clear (FatTable* ft) +{ + memset (ft->table, 0, ft->raw_size); + + fat_table_set (ft, 0, 0x0ffffff8); + fat_table_set (ft, 1, 0x0fffffff); + + ft->free_cluster_count = ft->cluster_count; + ft->bad_cluster_count = 0; + ft->last_alloc = 1; +} + +int +fat_table_set_cluster_count (FatTable* ft, FatCluster new_cluster_count) +{ + PED_ASSERT (new_cluster_count + 2 <= ft->size, return 0); + + ft->cluster_count = new_cluster_count; + return fat_table_count_stats (ft); +} + +int +fat_table_count_stats (FatTable* ft) +{ + FatCluster i; + + PED_ASSERT (ft->cluster_count + 2 <= ft->size, return 0); + + ft->free_cluster_count = 0; + ft->bad_cluster_count = 0; + + for (i=2; i < ft->cluster_count + 2; i++) { + if (fat_table_is_available (ft, i)) + ft->free_cluster_count++; + if (fat_table_is_bad (ft, i)) + ft->bad_cluster_count++; + } + return 1; +} + +int +fat_table_read (FatTable* ft, const PedFileSystem* fs, int table_num) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512, return 0); + + memset (ft->table, 0, ft->raw_size); + + if (!ped_geometry_read (fs->geom, (void *) ft->table, + fs_info->fat_offset + + table_num * fs_info->fat_sectors, + fs_info->fat_sectors)) + return 0; + + if ( *((unsigned char*) ft->table) != fs_info->boot_sector.media) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("FAT %d media %x doesn't match the boot sector's " + "media %x. You should probably run scandisk."), + (int) table_num + 1, + (int) *((unsigned char*) ft->table), + (int) fs_info->boot_sector.media) + != PED_EXCEPTION_IGNORE) + return 0; + } + + ft->cluster_count = fs_info->cluster_count; + + fat_table_count_stats (ft); + + return 1; +} + +int +fat_table_write (const FatTable* ft, PedFileSystem* fs, int table_num) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512, return 0); + + if (!ped_geometry_write (fs->geom, (void *) ft->table, + fs_info->fat_offset + + table_num * fs_info->fat_sectors, + fs_info->fat_sectors)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + + return 1; +} + +int +fat_table_write_all (const FatTable* ft, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int i; + + for (i = 0; i < fs_info->fat_table_count; i++) { + if (!fat_table_write (ft, fs, i)) + return 0; + } + + return 1; +} + +int +fat_table_compare (const FatTable* a, const FatTable* b) +{ + FatCluster i; + + if (a->cluster_count != b->cluster_count) + return 0; + + for (i = 0; i < a->cluster_count + 2; i++) { + if (fat_table_get (a, i) != fat_table_get (b, i)) + return 0; + } + + return 1; +} + +static int +_test_code_available (const FatTable* ft, FatCluster code) +{ + return code == 0; +} + +static int +_test_code_bad (const FatTable* ft, FatCluster code) +{ + switch (ft->fat_type) { + case FAT_TYPE_FAT16: + if (code == 0xfff7) return 1; + break; + + case FAT_TYPE_FAT32: + if (code == 0x0ffffff7) return 1; + break; + } + return 0; +} + +static int +_test_code_active (const FatTable* ft, FatCluster code) +{ + return code && !_test_code_bad (ft, code); +} + +static int +_test_code_eof (const FatTable* ft, FatCluster code) +{ + switch (ft->fat_type) { + case FAT_TYPE_FAT16: + if (code >= 0xfff7) return 1; + break; + + case FAT_TYPE_FAT32: + if (code >= 0x0ffffff7) return 1; + break; + } + return 0; +} + +void +_update_stats (FatTable* ft, FatCluster cluster, FatCluster value) +{ + if (_test_code_available (ft, value) + && !fat_table_is_available (ft, cluster)) { + ft->free_cluster_count++; + if (fat_table_is_bad (ft, cluster)) + ft->bad_cluster_count--; + } + + if (!_test_code_available (ft, value) + && fat_table_is_available (ft, cluster)) { + ft->free_cluster_count--; + if (_test_code_bad (ft, cluster)) + ft->bad_cluster_count--; + } +} + +int +fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value) +{ + if (cluster >= ft->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("fat_table_set: cluster %ld outside " + "file system"), + (long) cluster); + return 0; + } + + _update_stats (ft, cluster, value); + + switch (ft->fat_type) { + case FAT_TYPE_FAT16: + ((unsigned short *) ft->table) [cluster] + = PED_CPU_TO_LE16 (value); + break; + + case FAT_TYPE_FAT32: + ((unsigned int *) ft->table) [cluster] + = PED_CPU_TO_LE32 (value); + break; + } + return 1; +} + +FatCluster +fat_table_get (const FatTable* ft, FatCluster cluster) +{ + if (cluster >= ft->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("fat_table_get: cluster %ld outside " + "file system"), + (long) cluster); + exit (1); /* FIXME */ + } + + switch (ft->fat_type) { + case FAT_TYPE_FAT16: + return PED_LE16_TO_CPU + (((unsigned short *) ft->table) [cluster]); + + case FAT_TYPE_FAT32: + return PED_LE32_TO_CPU + (((unsigned int *) ft->table) [cluster]); + } + + return 0; +} + +FatCluster +fat_table_alloc_cluster (FatTable* ft) +{ + FatCluster i; + FatCluster cluster; + +/* hack: assumes the first two FAT entries are marked as used (which they + * always should be) + */ + for (i=1; i < ft->cluster_count + 1; i++) { + cluster = (i + ft->last_alloc) % ft->cluster_count; + if (fat_table_is_available (ft, cluster)) { + ft->last_alloc = cluster; + return cluster; + } + } + + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("fat_table_alloc_cluster: no free clusters")); + return 0; +} + +FatCluster +fat_table_alloc_check_cluster (FatTable* ft, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster result; + + while (1) { + result = fat_table_alloc_cluster (ft); + if (!result) + return 0; + if (fat_read_cluster (fs, fs_info->buffer, result)) + return result; + fat_table_set_bad (ft, result); + } +} + +/* + returns true if <cluster> is marked as bad +*/ +int +fat_table_is_bad (const FatTable* ft, FatCluster cluster) +{ + return _test_code_bad (ft, fat_table_get (ft, cluster)); +} + +/* + returns true if <cluster> represents an EOF marker +*/ +int +fat_table_is_eof (const FatTable* ft, FatCluster cluster) +{ + return _test_code_eof (ft, cluster); +} + +/* + returns true if <cluster> is available. +*/ +int +fat_table_is_available (const FatTable* ft, FatCluster cluster) +{ + return _test_code_available (ft, fat_table_get (ft, cluster)); +} + +/* + returns true if <cluster> is empty. Note that this includes bad clusters. +*/ +int +fat_table_is_empty (const FatTable* ft, FatCluster cluster) +{ + return fat_table_is_available (ft, cluster) + || fat_table_is_bad (ft, cluster); +} + +/* + returns true if <cluster> is being used for something constructive. +*/ +int +fat_table_is_active (const FatTable* ft, FatCluster cluster) +{ + return !fat_table_is_bad (ft, cluster) + && !fat_table_is_available (ft, cluster); +} + +/* + marks <cluster> as the last cluster in the chain +*/ +int +fat_table_set_eof (FatTable* ft, FatCluster cluster) +{ + + switch (ft->fat_type) { + case FAT_TYPE_FAT16: + return fat_table_set (ft, cluster, 0xfff8); + + case FAT_TYPE_FAT32: + return fat_table_set (ft, cluster, 0x0fffffff); + } + + return 0; +} + +/* + Marks a clusters as unusable, due to physical disk damage. +*/ +int +fat_table_set_bad (FatTable* ft, FatCluster cluster) +{ + if (!fat_table_is_bad (ft, cluster)) + ft->bad_cluster_count++; + + switch (ft->fat_type) { + case FAT_TYPE_FAT16: + return fat_table_set (ft, cluster, 0xfff7); + + case FAT_TYPE_FAT32: + return fat_table_set (ft, cluster, 0x0ffffff7); + } + + return 0; +} + +/* + marks <cluster> as unused/free/available +*/ +int +fat_table_set_avail (FatTable* ft, FatCluster cluster) +{ + return fat_table_set (ft, cluster, 0); +} + +#endif /* !DISCOVER_ONLY */ + +int +fat_table_entry_size (FatType fat_type) +{ + switch (fat_type) { + case FAT_TYPE_FAT12: + return 2; /* FIXME: how? */ + + case FAT_TYPE_FAT16: + return 2; + + case FAT_TYPE_FAT32: + return 4; + } + + return 0; +} + diff --git a/libparted/fs/fat/table.h b/libparted/fs/fat/table.h new file mode 100644 index 0000000..9d2501b --- /dev/null +++ b/libparted/fs/fat/table.h @@ -0,0 +1,75 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef PED_FAT_TABLE_H_INCLUDED +#define PED_FAT_TABLE_H_INCLUDED + +typedef struct _FatTable FatTable; + +#include "fat.h" + +struct _FatTable { + void* table; + FatCluster size; + int raw_size; + + FatType fat_type; + FatCluster cluster_count; + FatCluster free_cluster_count; + FatCluster bad_cluster_count; + + FatCluster last_alloc; +}; + +extern FatTable* fat_table_new (FatType fat_type, FatCluster size); +extern FatTable* fat_table_duplicate (const FatTable* ft); +extern void fat_table_destroy (FatTable* ft); +extern void fat_table_clear (FatTable* ft); +extern int fat_table_set_cluster_count (FatTable* ft, + FatCluster new_cluster_count); + +extern int fat_table_read (FatTable* ft, const PedFileSystem* fs, + int table_num); +extern int fat_table_write (const FatTable* ft, PedFileSystem* fs, + int table_num); +extern int fat_table_write_all (const FatTable* ft, PedFileSystem* fs); +extern int fat_table_compare (const FatTable* a, const FatTable* b); +extern int fat_table_count_stats (FatTable* ft); + +extern FatCluster fat_table_get (const FatTable* ft, FatCluster cluster); +extern int fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value); + +extern FatCluster fat_table_alloc_cluster (FatTable* ft); +extern FatCluster fat_table_alloc_check_cluster (FatTable* ft, + PedFileSystem* fs); + +extern int fat_table_is_bad (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_eof (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_empty (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_available (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_active (const FatTable* ft, FatCluster cluster); + +extern int fat_table_set_eof (FatTable* ft, FatCluster cluster); +extern int fat_table_set_avail (FatTable* ft, FatCluster cluster); +extern int fat_table_set_bad (FatTable* ft, FatCluster cluster); + +extern int fat_table_entry_size (FatType fat_type); + +#endif /* PED_FAT_TABLE_H_INCLUDED */ + diff --git a/libparted/fs/fat/traverse.c b/libparted/fs/fat/traverse.c new file mode 100644 index 0000000..8c3e530 --- /dev/null +++ b/libparted/fs/fat/traverse.c @@ -0,0 +1,369 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + I can also be contacted at: + + Andrew Clausen + 18 Shaw St + Ashwood, 3147 + Victoria, Australia + +*/ + +#include "fat.h" +#include "traverse.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +#define NO_CLUSTER -1 + +static char tmp_buffer [4096]; + +int +fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info) +{ + return trav_info->buffer_size / sizeof (FatDirEntry); +} + +/* returns 1 if there are no more directory entries in the directory being + * traversed, 0 otherwise. + */ +static int +is_last_buffer (FatTraverseInfo* trav_info) { + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + if (trav_info->is_legacy_root_dir) + return 1; + else + return fat_table_is_eof (fs_info->fat, trav_info->next_buffer); +} + +static int +write_root_dir (FatTraverseInfo* trav_info) +{ + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count)) + return 0; + if (!ped_geometry_sync (trav_info->fs->geom)) + return 0; + trav_info->dirty = 0; + return 1; +} + +static int +write_dir_cluster (FatTraverseInfo* trav_info) +{ + if (!fat_write_sync_cluster (trav_info->fs, + (void*) trav_info->dir_entries, + trav_info->this_buffer)) + return 0; + trav_info->dirty = 0; + return 1; +} + +static int +write_dir_buffer (FatTraverseInfo* trav_info) +{ + if (trav_info->is_legacy_root_dir) + return write_root_dir (trav_info); + else + return write_dir_cluster (trav_info); +} + +static int +read_next_dir_buffer (FatTraverseInfo* trav_info) +{ + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + PED_ASSERT (!trav_info->is_legacy_root_dir, return 0); + + trav_info->this_buffer = trav_info->next_buffer; + + if (trav_info->this_buffer < 2 + || trav_info->this_buffer >= fs_info->cluster_count + 2) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "Cluster %ld in directory %s is outside file system!", + (long) trav_info->this_buffer, + trav_info->dir_name); + return 0; + } + + trav_info->next_buffer + = fat_table_get (fs_info->fat, trav_info->this_buffer); + + return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries, + trav_info->this_buffer); +} + +/* FIXME: put into fat_dir_entry_* operations */ +void +fat_traverse_mark_dirty (FatTraverseInfo* trav_info) +{ + trav_info->dirty = 1; +} + +FatTraverseInfo* +fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster, + char* dir_name) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTraverseInfo* trav_info; + + trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo)); + if (!trav_info) + goto error; + + trav_info->dir_name = strdup (dir_name); + if (!trav_info->dir_name) + goto error_free_trav_info; + + trav_info->fs = fs; + trav_info->is_legacy_root_dir + = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0); + trav_info->dirty = 0; + trav_info->eof = 0; + trav_info->current_entry = -1; + + if (trav_info->is_legacy_root_dir) { + trav_info->buffer_size = 512 * fs_info->root_dir_sector_count; + } else { + trav_info->next_buffer = start_cluster; + trav_info->buffer_size = fs_info->cluster_size; + } + + trav_info->dir_entries + = (FatDirEntry*) ped_malloc (trav_info->buffer_size); + if (!trav_info->dir_entries) + goto error_free_dir_name; + + if (trav_info->is_legacy_root_dir) { + if (!ped_geometry_read (fs->geom, trav_info->dir_entries, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count)) + goto error_free_dir_entries; + } else { + if (!read_next_dir_buffer (trav_info)) + goto error_free_dir_entries; + } + + return trav_info; + +error_free_dir_entries: + ped_free (trav_info->dir_entries); +error_free_dir_name: + ped_free (trav_info->dir_name); +error_free_trav_info: + ped_free (trav_info); +error: + return NULL; +} + +int +fat_traverse_complete (FatTraverseInfo* trav_info) +{ + if (trav_info->dirty) { + if (!write_dir_buffer (trav_info)) + return 0; + } + ped_free (trav_info->dir_entries); + ped_free (trav_info->dir_name); + ped_free (trav_info); + return 1; +} + +FatTraverseInfo* +fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent) +{ + strcpy (tmp_buffer, trav_info->dir_name); + fat_dir_entry_get_name (parent, + tmp_buffer + strlen (trav_info->dir_name)); + strcat (tmp_buffer, "\\"); + + return fat_traverse_begin (trav_info->fs, + fat_dir_entry_get_first_cluster (parent, trav_info->fs), + tmp_buffer); +} + +FatDirEntry* +fat_traverse_next_dir_entry (FatTraverseInfo *trav_info) +{ + if (trav_info->eof) + return NULL; + + trav_info->current_entry++; + if (trav_info->current_entry + >= fat_traverse_entries_per_buffer (trav_info)) { + if (trav_info->dirty) { + if (!write_dir_buffer (trav_info)) + return NULL; + } + + trav_info->current_entry = 0; + if (is_last_buffer (trav_info)) { + trav_info->eof = 1; + return NULL; + } + if (!read_next_dir_buffer (trav_info)) + return NULL; + } + return trav_info->dir_entries + trav_info->current_entry; +} + +FatCluster +fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + switch (fs_info->fat_type) { + case FAT_TYPE_FAT12: + case FAT_TYPE_FAT16: + return PED_LE16_TO_CPU (dir_entry->first_cluster); + + case FAT_TYPE_FAT32: + return PED_LE16_TO_CPU (dir_entry->first_cluster_high) + * 65536L + + PED_LE16_TO_CPU (dir_entry->first_cluster); + } + + return 0; +} + +void +fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs, + FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + switch (fs_info->fat_type) { + case FAT_TYPE_FAT16: + dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster); + break; + + case FAT_TYPE_FAT32: + dir_entry->first_cluster + = PED_CPU_TO_LE16 (cluster & 0xffff); + dir_entry->first_cluster_high + = PED_CPU_TO_LE16 (cluster / 0x10000); + break; + } +} + +uint32_t +fat_dir_entry_get_length (FatDirEntry* dir_entry) +{ + return PED_LE32_TO_CPU (dir_entry->length); +} + +int +fat_dir_entry_is_null_term (const FatDirEntry* dir_entry) +{ + FatDirEntry null_entry; + + memset (&null_entry, 0, sizeof (null_entry)); + return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0; +} + +int +fat_dir_entry_is_active (FatDirEntry* dir_entry) +{ + if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0; + if ((unsigned char) dir_entry->name[0] == 0) return 0; + if ((unsigned char) dir_entry->name[0] == 0xF6) return 0; + return 1; +} + +int +fat_dir_entry_is_file (FatDirEntry* dir_entry) { + if (dir_entry->attributes == VFAT_ATTR) return 0; + if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; + if (!fat_dir_entry_is_active (dir_entry)) return 0; + if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0; + return 1; +} + +int +fat_dir_entry_is_system_file (FatDirEntry* dir_entry) +{ + if (!fat_dir_entry_is_file (dir_entry)) return 0; + return (dir_entry->attributes & SYSTEM_ATTR) + || (dir_entry->attributes & HIDDEN_ATTR); +} + +int +fat_dir_entry_is_directory (FatDirEntry* dir_entry) +{ + if (dir_entry->attributes == VFAT_ATTR) return 0; + if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; + if (!fat_dir_entry_is_active (dir_entry)) return 0; + return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR; +} + +int +fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster first_cluster; + + if (!fat_dir_entry_is_file (dir_entry) + && !fat_dir_entry_is_directory (dir_entry)) + return 0; + + first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs); + if (first_cluster == 0 + || fat_table_is_eof (fs_info->fat, first_cluster)) + return 0; + + return 1; +} + +/* + decrypts silly DOS names to FILENAME.EXT +*/ +void +fat_dir_entry_get_name (FatDirEntry*dir_entry, char *result) { + int i; + char *src; + + src = dir_entry->name; + + for (i=0; i<8; i++) { + if (src[i] == ' ' || src[i] == 0) break; + *result++ = src[i]; + } + + if (src[8] != ' ' && src[8] != 0) { + *result++ = '.'; + for (i=8; i<11; i++) { + if (src[i] == ' ' || src[i] == 0) break; + *result++ = src[i]; + } + } + + *result = 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/traverse.h b/libparted/fs/fat/traverse.h new file mode 100644 index 0000000..b88302c --- /dev/null +++ b/libparted/fs/fat/traverse.h @@ -0,0 +1,81 @@ +/* + libparted + Copyright (C) 1998, 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + I can also be contacted at: + + Andrew Clausen + 18 Shaw St + Ashwood, 3147 + Victoria, Australia +*/ + +#ifndef TRAVERSE_H_INCLUDED +#define TRAVERSE_H_INCLUDED + +#include "fatio.h" + +typedef struct _FatTraverseInfo FatTraverseInfo; + +struct _FatTraverseInfo { + PedFileSystem* fs; + char* dir_name; + + int is_legacy_root_dir; + int dirty; + int eof; + + FatDirEntry* dir_entries; + int current_entry; + FatCluster this_buffer, next_buffer; + int buffer_size; +}; + +extern int fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info); + +/* starts traversal at an arbitary cluster. if start_cluster==0, then uses + root directory */ +extern FatTraverseInfo* fat_traverse_begin (PedFileSystem* fs, + FatCluster start_cluster, char* dir_name); + +extern int fat_traverse_complete (FatTraverseInfo* trav_info); + +extern FatTraverseInfo* fat_traverse_directory (FatTraverseInfo* trav_info, + FatDirEntry* parent); + +extern void fat_traverse_mark_dirty (FatTraverseInfo* trav_info); + +extern FatDirEntry* fat_traverse_next_dir_entry (FatTraverseInfo* trav_info); + +extern FatCluster fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs); + +extern void fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs, FatCluster cluster); + +extern uint32_t fat_dir_entry_get_length (FatDirEntry* dir_entry); + +extern int fat_dir_entry_is_null_term (const FatDirEntry* dir_entry); +extern int fat_dir_entry_is_file (FatDirEntry* dir_entry); +extern int fat_dir_entry_is_system_file (FatDirEntry* dir_entry); +extern int fat_dir_entry_is_directory (FatDirEntry* dir_entry); +extern void fat_dir_entry_get_name (FatDirEntry* dir_entry, char* result); +extern int fat_dir_entry_is_active (FatDirEntry* dir_entry); +extern int fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs); + +#endif /* TRAVERSE_H_INCLUDED */ diff --git a/libparted/fs/hfs/DOC b/libparted/fs/hfs/DOC new file mode 100644 index 0000000..d39b10b --- /dev/null +++ b/libparted/fs/hfs/DOC @@ -0,0 +1,92 @@ +WARNING : Both HFS and HFS+ implementations of Linux 2.4 are buggy, at +least when used before or after this implementation. Some workarounds +are used in this implementation, but there can still be incompatibilities. +Try Linux 2.6 if you want to play with HFS(+) resizing (though some bugs +might also be there in 2.6, there is of course no warranty) + +--- + + Technical doc about Apple HFS and HFS+ file systems is available at : + * For HFS, section "Data Organization on Volumes", + "Chapter 2 - File Manager" + of the book "Inside Macintosh: Files" + http://developer.apple.com/documentation/mac/Files/Files-99.html + * For HFS+, "Technical Note TN1150", "HFS Plus Volume Format" + http://developer.apple.com/technotes/tn/tn1150.html + + Some useful HFS precisions concerning alignement, bit ordering, and + order of fields for extent key comparaisons are only in the HFS+ TN + + These Apple Creator Codes are reserved for us : + Shnk traP GP16 GnuP PH+x Xpnd Resz GP17 GP18 GP19 GP20 + +--- + +* Cache design * + +Versions before HFS Patch 15 were very slow when data relocation was needed, +because every extent to relocate involved scanning the whole file system, +looking for a reference to its physical position on the volume (this was a +dummy algorithm, I know :) + +HFS Patch 16 introduced a cache that allows to efficiently retrieve the place +of the reference in the file system given the physical position of an extent. +The cache is designed for : - efficiency + - scaling + - simplicity + - avoiding memory allocation while resizing + +This cache involves quite big worst case memory consumption, but without it +the time needed to complete the operation in the worst case would be huge +anyway (maybe several years...) so this isn't really an issue. The cache size +is nearly proportional to the number of files you have, or if you have very few +files, to the size of your volume, so worst cases situations occure when you +fill a drive with millions of < 4 ko files :p For this very special usage you +will just need a very special amount of RAM (on typical systems about +(FS size) / 256 )... On a more "normal" volume it's about +(# of files) * 20 bytes. With very few files it's about (FS Size) / 1024 / 256. + +At the beginning of the resize process, the cache is filed by scanning the FS. +The position of each extent is cut into 2 parts : high order is used as +an index into a table of pointer to a linked list which contains : +- the next ptr (sizeof struct *) +- the extent start (4 bytes) +- the extent size (4 bytes) +- number of BTree block or 0 if in prim (4 bytes) +- offset of the extent start reference + from the block beginning (2 bytes) +- sectors by BTree block, or + 1 for VH/MDB (1 byte) +- FS special file / primary structure + where the extent reference is stored (1 byte) + (3 bits for the extent index, 5 for + the actual ref) + + 0 : dont exists --- reserved + 1 : mdb / vh : catalog --- + 2 : mdb / vh : extent --- + 3 : vh : attributes X+- + 4 : vh : allocation X+- + 5 : vh : startup X+- + 6 : catalog --- + 7 : attributes X+- + 8 : extent (nothing to update) --- + 9 : extent (update catalog) --- + 10 : extent (update extent !?!) --- should not exist + 11 : extent (update attributes) X+- + 12 : extent (update allocation) X+- + 13 : extent (update startup) X+- reserved + 14 : vh : journal info block X+J + 15 : jib: journal X+J + 16 - 31 : --- + +mdb : Master Directory Block +vh : Volume Header +X+ : HFSX or HFS+ only +J : Journaled only + +Large amount of memory is allocated at once (first enough memory to fit +every files if there isn't any fragmentation +6.25%, then this value / 4, +if this wasn't enough). On a typical FS, the first allocation should be enough. + +--- diff --git a/libparted/fs/hfs/HISTORY b/libparted/fs/hfs/HISTORY new file mode 100644 index 0000000..e4c4b52 --- /dev/null +++ b/libparted/fs/hfs/HISTORY @@ -0,0 +1,115 @@ +## modifications dd-mm-yyyy +---------------------- PATCH FOR PARTED 1.6.5 ---------------------------- + 1 initial revision 07-04-2003 + 2 one pass resizing, removal of debug info 08-04-2003 + 3 safe abort if resize failed, code cleanups, timer, 10-04-2003 + source file split, won't resize if not unmounted, + only relocate data if needed, minimize disk operations + 4 memory leaks removal, code cleanups, resize hfs+ code, 17-04-2003 + more checks, minor hfs resize bugfix, probe code + returns real geometry + 5 hfs+ resize bugfixes : 19-04-2003 + * fragmented fs could be corrupted + * VH wasn't written on error during alloc map writing + * attributes file could be corrupted + 6 Use PedSector to be able to use 2To+ HD 23-04-2003 + Minor probe bugfix, Cleanups, HFS+ Timer tuning, + 7 80 columns indentation 23-04-2003 + 8 Bugfix free blocks calculation in wrapper + (makes Mac OS boot !) 28-04-2003 +---------------------- PATCH FOR PARTED 1.6.6 ---------------------------- + 9 Don't destroy the file being worked on in case of + interruption of Parted 28-10-2003 +---------------------- PATCH FOR PARTED 1.6.10 --------------------------- +10 Regression tests, URL correction, In effect_move_extent : + corrected memory leak & corrected a bug in precondition checks + Added error messages, Check ped_alloc results + Use macro for test / set / clear in the allocation bitmap + Light probe correction, Check return value of get_empty_end + Moved dynamic memory allocation out of effect_move_extent + Check HFS+ version, Set implementation creator code + Check journal absence, Corrected a bug in HFS+ block number + calculation 24-04-2004 +--------------------- PATCH FOR PARTED 1.6.11 ---------------------------- +11-Some pointer dereference moved after non nul assertion + -Error messages for HFS(+) file IO + -Memory leak correction in hfs(plus)_read_bad_blocks + -Mark out of volume blocks as used + (improve compatibility with broken HFS+ Linux + implementation) + WARNING : this fix is not 100% tn1150 compatible : + "The allocation file may be larger than the minimum + number of bits required for the given volume size. + Any unused bits in the bitmap must be set to _zero_." + Anyway neither is the Linux implementation, nor was my + previous implementations + Maybe I should ask Apple to change the specifications + -HISTORY, DOC and TODO files 29-04-2004 +12 Corrected a bug in hfsplus_volume_resize : size of alloc + bitmap could be miscalculated 29-04-2004 +--------------------- PATCH FOR PARTED 1.6.12 ---------------------------- +13-Finally partial rewrite of *_search_move_* + Easier to maintain and prepare for extent search and + relocation algorithm changes for better ones. + -"An extent has not been relocated!" message now only when + relocation requested + -Slightly better and simpler relocation algorithm + -Update of Makefile.in and Makefile.am in fs_hfs + -Sign correction for some 8bits HFS integers + -Added the option --enable-hfs-extract-fs in 'configure' + -Added every ped_geometry_sync where needed + -Bugfix : "A root node does not need to exist + (if the tree is empty)." + - now handled correctly in btree_search + -Bugfix : failure wasn't detected in some cases + during 2 pass relocation (*_search_move_*) + -Bugfix : The extent key comparaison was done in a wrong order + and a pad field was used during the comparaison + -Bugfix : in hfs_file_find_sector and hfsplus_file_find_sector + the absolute position of a file sector could be + miscalculated in case of fragmentation, resulting + in potential data corruption, or various errors + -Bugfix : The end of the HFS bitmap compatibility block was + miscalculated ( (1<<16)/8 instead of (1<<16) ) + in hfs_resize + 07-09-2004 +--------------------- PATCH FOR PARTED 1.6.14 ---------------------------- +14 Port of Patch 13 for Parted 1.6.14 (update timestamps) + 08-09-2004 +--------------------- PATCH FOR PARTED 1.6.15 ---------------------------- +15-hfsplus_open : added a warning message if the "attributes" + special file exists + -hfsplus_open : added a test to check if the "allocation" + special file has been correctly opened + -optimisation of hfs+ block access : don't recalculate + the address of each sector, and avoid checking the cache if + obviously not useful + ( hfsplus_file_read && hfsplus_file_write + && hfsplus_file_find_extent && hfs_file_find_sector) + -cut the "hfs.c" file in several parts + -Bugfix: in hfsplus_do_move_primary, hfs_effect_move_extent + was called instead of hfsplus_effect_move_extent !!! + This could not produce data corruption, because of a welcome + ASSERT in *_effect_move_extent that would detect the bug :) + -Bugfix: in hfs_effect_move_extent, do + PED_ASSERT(*ptr_to_fblock <= *ptr_fblock, return -1); + instead of + PED_ASSERT(*ptr_to_fblock < *ptr_fblock, return -1); + and added that assertion to hfsplus_effect_move_extent + -Bugfix: bugs introduced in rewrite of hfsplus_file_read + && hfsplus_file_write : last sector was incorrectly detected + as out of file. + -Cache the extent references (speed improvement ?) + 23-09-2004 +16-Bugfix: in hfsplus_do_move (reloc_plus.c), case CR_BTREE_EXT_ATTR + incorrectly updated the cached part of priv_data->catalog_file + instead of priv_data->attributes_file + -Bugfix: in hfs_read_bad_blocks && hfsplus_read_bad_blocks, + now generate an error if file_ID or type mismatch after the + first pass + Also check return value of ped_malloc + -Bugfix: in hfsplus_btree_search, check return value of ped_malloc + 29-09-2004 +---------------- INTEGRATION IN PARTED 1.6.22 (cvs) ---------------------- +Futur changes will be described in ../../ChangeLog + 02-02-2005 diff --git a/libparted/fs/hfs/Makefile.am b/libparted/fs/hfs/Makefile.am new file mode 100644 index 0000000..f70513a --- /dev/null +++ b/libparted/fs/hfs/Makefile.am @@ -0,0 +1,15 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libhfs.la +libhfs_la_SOURCES = hfs.c hfs.h \ + probe.c probe.h \ + cache.c cache.h \ + advfs.c advfs.h \ + file.c file.h \ + reloc.c reloc.h \ + advfs_plus.c advfs_plus.h \ + file_plus.c file_plus.h \ + reloc_plus.c reloc_plus.h \ + journal.c journal.h + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/hfs/TODO b/libparted/fs/hfs/TODO new file mode 100644 index 0000000..13bca9c --- /dev/null +++ b/libparted/fs/hfs/TODO @@ -0,0 +1,27 @@ +--- TODO --- + + * Continue to write regressions tests and try on 2.6 kernel -- (high) + * Fix timer progression calculation, according to the new + caching code -- (high) + * write doc, website, ... -- (high) + * Check block allocation in linux 2.4 and remove + compatibility code if possible -- (high) + + * In hfs(plus)_btree_search , use a static variable to detect + illegal recursion and abort in that case. (find the right + number of recursion before reporting bug) -- easy -- (medium) + * Finish the HFS Extractor -- (medium) + (add mdb & vh extraction, and maybe journal) + + * Write code to allow enlarging and moving HFS[+x] -- (low) + * Use a bitmap to internaly store the bad blocks -- (low) + * Less bitmap saves ? -- (low) + * Continue to change the relocation algorithm + for a better one :) -- (low) + +--- NOT todo --- + + * debug HFS(+) Linux implementation (block allocation for HFS+, + hard and sym links for HFS+, filename length for HFS, ...) -- (dont) + /// Linux 2.6 contains HFS(+) implementations with less bugs + /// Linux 2.4 should not be used anymore to access HFS(+) diff --git a/libparted/fs/hfs/advfs.c b/libparted/fs/hfs/advfs.c new file mode 100644 index 0000000..b19fe5e --- /dev/null +++ b/libparted/fs/hfs/advfs.c @@ -0,0 +1,331 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef DISCOVER_ONLY + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file.h" + +#include "advfs.h" + +/* - if a < b, 0 if a == b, + if a > b */ +/* Comparaison is done in the following order : */ +/* CNID, then fork type, then start block */ +/* Note that HFS implementation in linux has a bug */ +/* in this function */ +static int +hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b) +{ + HfsExtentKey* key1 = (HfsExtentKey*) a; + HfsExtentKey* key2 = (HfsExtentKey*) b; + + /* do NOT use a substraction, because */ + /* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */ + /* would return -2, despite the fact */ + /* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */ + if (key1->file_ID != key2->file_ID) + return PED_BE32_TO_CPU(key1->file_ID) < + PED_BE32_TO_CPU(key2->file_ID) ? + -1 : +1; + + if (key1->type != key2->type) + return (int)(key1->type - key2->type); + + if (key1->start == key2->start) + return 0; + /* the whole thing wont work with 16 bits ints */ + /* anyway */ + return (int)( PED_BE16_TO_CPU(key1->start) - + PED_BE16_TO_CPU(key2->start) ); +} + +/* do a B-Tree lookup */ +/* read the first record immediatly inferior or egal to the given key */ +/* return 0 on error */ +/* record_out _must_ be large enough to receive record_size bytes */ +/* WARNING : the compare function called only handle Extents BTree */ +/* so modify this function if you want to do lookup in */ +/* other BTrees has well */ +int +hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsPrivateGenericKey* record_key = NULL; + unsigned int node_number, record_number; + int i; + + /* Read the header node */ + if (!hfs_file_read_sector(b_tree_file, node, 0)) + return 0; + header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE_DEFAULT-2)))))); + + /* Get the node number of the root */ + node_number = PED_BE32_TO_CPU(header->root_node); + if (!node_number) + return 0; + + /* Read the root node */ + if (!hfs_file_read_sector(b_tree_file, node, node_number)) + return 0; + + /* Follow the white rabbit */ + while (1) { + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = record_number; i; i--) { + record_key = (HfsPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE_DEFAULT - 2*i))))); + /* check for obvious error in FS */ + if (((uint8_t*)record_key - node < HFS_FIRST_REC) + || ((uint8_t*)record_key - node + >= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + if (hfs_extent_key_cmp(record_key, key) <= 0) + break; + } + if (!i) return 0; + if (desc->type == HFS_IDX_NODE) { + unsigned int skip; + + skip = (1 + record_key->key_length + 1) & ~1; + node_number = PED_BE32_TO_CPU (*((uint32_t *) + (((uint8_t *) record_key) + skip))); + if (!hfs_file_read_sector(b_tree_file, node, + node_number)) + return 0; + } else + break; + } + + /* copy the result if needed */ + if (record_size) + memcpy (record_out, record_key, record_size); + + /* send record reference if needed */ + if (record_ref) { + record_ref->node_size = 1; /* in sectors */ + record_ref->node_number = node_number; + record_ref->record_pos = (uint8_t*)record_key - node; + record_ref->record_number = i; + } + + /* success */ + return 1; +} + +/* free the bad blocks linked list */ +void +hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first) +{ + HfsPrivateLinkExtent* next; + + while (first) { + next = first->next; + ped_free (first); + first = next; + } +} + +/* This function reads bad blocks extents in the extents file + and store it in f.s. specific data of fs */ +int +hfs_read_bad_blocks (const PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + return 1; + + { + uint8_t record[sizeof (HfsExtentKey) + + sizeof (HfsExtDataRec)]; + HfsExtentKey search; + HfsExtentKey* ret_key = (HfsExtentKey*) record; + HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) + (record + sizeof (HfsExtentKey)); + unsigned int block, last_start, first_pass = 1; + + search.key_length = sizeof (HfsExtentKey) - 1; + search.type = HFS_DATA_FORK; + search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + + last_start = -1; block = 0; + while (1) { + int i; + + search.start = PED_CPU_TO_BE16 (block); + if (!hfs_btree_search (priv_data->extent_file, + (HfsPrivateGenericKey*) &search, + record, sizeof (record), NULL) + || ret_key->file_ID != search.file_ID + || ret_key->type != search.type) { + if (first_pass) + break; + else + goto errbb; + } + if (PED_BE16_TO_CPU (ret_key->start) == last_start) + break; + + last_start = PED_BE16_TO_CPU (ret_key->start); + for (i = 0; i < HFS_EXT_NB; i++) { + if (ret_cache[i].block_count) { + HfsPrivateLinkExtent* new_xt = + (HfsPrivateLinkExtent*) ped_malloc ( + sizeof (HfsPrivateLinkExtent)); + if (!new_xt) + goto errbb; + new_xt->next = priv_data->bad_blocks_xtent_list; + memcpy(&(new_xt->extent), ret_cache+i, + sizeof (HfsExtDescriptor)); + priv_data->bad_blocks_xtent_list = new_xt; + priv_data->bad_blocks_xtent_nb++; + block += PED_BE16_TO_CPU ( + ret_cache[i].block_count); + } + } + first_pass = 0; + } + + priv_data->bad_blocks_loaded = 1; + return 1;} + +errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + priv_data->bad_blocks_xtent_list=NULL; + priv_data->bad_blocks_xtent_nb=0; + return 0; +} + +/* This function check if fblock is a bad block */ +int +hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsPrivateLinkExtent* walk; + + for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { + /* Won't compile without the strange cast ! gcc bug ? */ + /* or maybe C subtilties... */ + if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) && + (fblock < (unsigned int) (PED_BE16_TO_CPU ( + walk->extent.start_block) + + PED_BE16_TO_CPU ( + walk->extent.block_count)))) + return 1; + } + + return 0; +} + +/* This function returns the first sector of the last free block of an + HFS volume we can get after a hfs_pack_free_space_from_block call */ +/* On error this function returns 0 */ +PedSector +hfs_get_empty_end (const PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + HfsPrivateLinkExtent* link; + unsigned int block, last_bad, end_free_blocks; + + /* find the next block to the last bad block of the volume */ + if (!hfs_read_bad_blocks (fs)) + return 0; + + last_bad = 0; + for (link = priv_data->bad_blocks_xtent_list; link; link = link->next) { + if ((unsigned int) PED_BE16_TO_CPU (link->extent.start_block) + + PED_BE16_TO_CPU (link->extent.block_count) > last_bad) + last_bad = PED_BE16_TO_CPU (link->extent.start_block) + + PED_BE16_TO_CPU (link->extent.block_count); + } + + /* Count the free blocks from last_bad to the end of the volume */ + end_free_blocks = 0; + for (block = last_bad; + block < PED_BE16_TO_CPU (mdb->total_blocks); + block++) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + end_free_blocks++; + } + + /* Calculate the block that will by the first free at the + end of the volume */ + block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks; + + return (PedSector) PED_BE16_TO_CPU (mdb->start_block) + + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT); +} + +/* return the block which should be used to pack data to have at + least free fblock blocks at the end of the volume */ +unsigned int +hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int block; + + for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1; + block && fblock; + block--) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + fblock--; + } + + while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block--; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block++; + + return block; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/advfs.h b/libparted/fs/hfs/advfs.h new file mode 100644 index 0000000..8e23fb6 --- /dev/null +++ b/libparted/fs/hfs/advfs.h @@ -0,0 +1,49 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _ADVFS_H +#define _ADVFS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref); + +void +hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first); + +int +hfs_read_bad_blocks (const PedFileSystem *fs); + +int +hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock); + +PedSector +hfs_get_empty_end (const PedFileSystem *fs); + +unsigned int +hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock); + +#endif /* _ADVFS_H */ diff --git a/libparted/fs/hfs/advfs_plus.c b/libparted/fs/hfs/advfs_plus.c new file mode 100644 index 0000000..c260995 --- /dev/null +++ b/libparted/fs/hfs/advfs_plus.c @@ -0,0 +1,387 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef DISCOVER_ONLY + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs.h" +#include "file_plus.h" + +#include "advfs_plus.h" + +/* - if a < b, 0 if a == b, + if a > b */ +/* Comparaison is done in the following order : */ +/* CNID, then fork type, then start block */ +static int +hfsplus_extent_key_cmp(HfsPPrivateGenericKey* a, HfsPPrivateGenericKey* b) +{ + HfsPExtentKey* key1 = (HfsPExtentKey*) a; + HfsPExtentKey* key2 = (HfsPExtentKey*) b; + + if (key1->file_ID != key2->file_ID) + return PED_BE32_TO_CPU(key1->file_ID) < + PED_BE32_TO_CPU(key2->file_ID) ? + -1 : +1; + + if (key1->type != key2->type) + return (int)(key1->type - key2->type); + + if (key1->start == key2->start) + return 0; + return PED_BE32_TO_CPU(key1->start) < + PED_BE32_TO_CPU(key2->start) ? + -1 : +1; +} + +/* do a B-Tree lookup */ +/* read the first record immediatly inferior or egal to the given key */ +/* return 0 on error */ +/* record_out _must_ be large enough to receive the whole record (key + data) */ +/* WARNING : the search function called only handle Extents BTree */ +/* so modify this function if you want to do lookup in */ +/* other BTrees has well */ +int +hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref) +{ + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1; + HfsPPrivateGenericKey* record_key = NULL; + unsigned int node_number, record_number, size, bsize; + int i; + + /* Read the header node */ + if (!hfsplus_file_read_sector(b_tree_file, node_1, 0)) + return 0; + header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); + + /* Get the node number of the root */ + node_number = PED_BE32_TO_CPU (header->root_node); + if (!node_number) + return 0; + + /* Get the size of a node in sectors and allocate buffer */ + size = (bsize = PED_BE16_TO_CPU (header->node_size)) / PED_SECTOR_SIZE_DEFAULT; + node = (uint8_t*) ped_malloc (bsize); + if (!node) + return 0; + desc = (HfsPNodeDescriptor*) node; + + /* Read the root node */ + if (!hfsplus_file_read (b_tree_file, node, + (PedSector) node_number * size, size)) + return 0; + + /* Follow the white rabbit */ + while (1) { + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = record_number; i; i--) { + record_key = (HfsPPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + /* check for obvious error in FS */ + if (((uint8_t*)record_key - node < HFS_FIRST_REC) + || ((uint8_t*)record_key - node + >= (signed)bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + ped_free (node); + return 0; + } + if (hfsplus_extent_key_cmp(record_key, key) <= 0) + break; + } + if (!i) { ped_free (node); return 0; } + if (desc->type == HFS_IDX_NODE) { + unsigned int skip; + + skip = ( 2 + PED_BE16_TO_CPU (record_key->key_length) + + 1 ) & ~1; + node_number = PED_BE32_TO_CPU (*((uint32_t *) + (((uint8_t *) record_key) + skip))); + if (!hfsplus_file_read(b_tree_file, node, + (PedSector) node_number * size, + size)) { + ped_free (node); + return 0; + } + } else + break; + } + + /* copy the result if needed */ + if (record_size) + memcpy (record_out, record_key, record_size); + + /* send record reference if needed */ + if (record_ref) { + record_ref->node_size = size; /* in sectors */ + record_ref->node_number = node_number; + record_ref->record_pos = (uint8_t*)record_key - node; + record_ref->record_number = i; + } + + /* success */ + ped_free (node); + return 1; +} + +/* free the bad blocks linked list */ +void +hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first) +{ + HfsPPrivateLinkExtent* next; + + while (first) { + next = first->next; + ped_free (first); + first = next; + } +} + +/* This function reads bad blocks extents in the extents file + and store it in f.s. specific data of fs */ +int +hfsplus_read_bad_blocks (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + return 1; + + { + uint8_t record[sizeof (HfsPExtentKey) + + sizeof (HfsPExtDataRec)]; + HfsPExtentKey search; + HfsPExtentKey* ret_key = (HfsPExtentKey*) record; + HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) + (record + sizeof (HfsPExtentKey)); + int block, first_pass = 1; + unsigned int last_start; + + search.key_length = sizeof (HfsExtentKey) - 2; + search.type = HFS_DATA_FORK; + search.pad = 0; + search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + + last_start = -1; block = 0; + while (1) { + int i; + + search.start = PED_CPU_TO_BE32 (block); + if (!hfsplus_btree_search (priv_data->extents_file, + (HfsPPrivateGenericKey*) &search, + record, sizeof (record), NULL) + || ret_key->file_ID != search.file_ID + || ret_key->type != search.type) { + if (first_pass) + break; + else + goto errbbp; + } + if (PED_BE32_TO_CPU (ret_key->start) == last_start) + break; + + last_start = PED_BE32_TO_CPU (ret_key->start); + for (i = 0; i < HFSP_EXT_NB; i++) { + if (ret_cache[i].block_count) { + HfsPPrivateLinkExtent* new_xt = + (HfsPPrivateLinkExtent*) ped_malloc ( + sizeof (HfsPPrivateLinkExtent)); + if (!new_xt) + goto errbbp; + new_xt->next = priv_data->bad_blocks_xtent_list; + memcpy (&(new_xt->extent), ret_cache+i, + sizeof (HfsPExtDescriptor)); + priv_data->bad_blocks_xtent_list = new_xt; + priv_data->bad_blocks_xtent_nb++; + block += PED_BE32_TO_CPU ( + ret_cache[i].block_count); + } + } + first_pass = 0; + } + + priv_data->bad_blocks_loaded = 1; + return 1;} + +errbbp: hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + priv_data->bad_blocks_xtent_list=NULL; + priv_data->bad_blocks_xtent_nb=0; + return 0; +} + +/* This function check if fblock is a bad block */ +int +hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPPrivateLinkExtent* walk; + + for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { + /* Won't compile without the strange cast ! gcc bug ? */ + /* or maybe C subtilties... */ + if ((fblock >= PED_BE32_TO_CPU (walk->extent.start_block)) && + (fblock < (unsigned int)(PED_BE32_TO_CPU ( + walk->extent.start_block) + + PED_BE32_TO_CPU (walk->extent.block_count)))) + return 1; + } + + return 0; +} + +/* This function returns the first sector of the last free block of + an HFS+ volume we can get after a hfsplus_pack_free_space_from_block call */ +PedSector +hfsplus_get_empty_end (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + HfsPPrivateLinkExtent* link; + unsigned int block, last_bad, end_free_blocks; + + /* find the next block to the last bad block of the volume */ + if (!hfsplus_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks could not be read.")); + return 0; + } + + last_bad = 0; + for (link = priv_data->bad_blocks_xtent_list; link; link = link->next) { + if ((unsigned int) PED_BE32_TO_CPU (link->extent.start_block) + + PED_BE32_TO_CPU (link->extent.block_count) > last_bad) + last_bad = PED_BE32_TO_CPU (link->extent.start_block) + + PED_BE32_TO_CPU (link->extent.block_count); + } + + /* Count the free blocks from last_bad to the end of the volume */ + end_free_blocks = 0; + for (block = last_bad; + block < PED_BE32_TO_CPU (vh->total_blocks); + block++) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + end_free_blocks++; + } + + /* Calculate the block that will by the first free at + the end of the volume */ + block = PED_BE32_TO_CPU (vh->total_blocks) - end_free_blocks; + + return (PedSector) block * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); +} + +/* On error, returns 0 */ +PedSector +hfsplus_get_min_size (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + PedSector min_size; + + /* don't need to add anything because every sector + can be part of allocation blocks in HFS+, and + the last block _must_ be reserved */ + min_size = hfsplus_get_empty_end(fs); + if (!min_size) return 0; + + if (priv_data->wrapper) { + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block; + PedSector hgee; + hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + /* + * if hfs+ is embedded in an hfs wrapper then the new size is : + * the new size of the hfs+ volume rounded up to the size + * of hfs blocks + * + the minimum size of the hfs wrapper without any hfs+ + * modification + * - the current size of the hfs+ volume in the hfs wrapper + */ + hgee = hfs_get_empty_end(priv_data->wrapper); + if (!hgee) return 0; + min_size = ((min_size + hfs_sect_block - 1) / hfs_sect_block) + * hfs_sect_block + + hgee + 2 + - (PedSector) PED_BE16_TO_CPU ( hfs_priv_data->mdb + ->old_new.embedded + .location.block_count ) + * hfs_sect_block; + } + + return min_size; +} + +/* return the block which should be used to pack data to have + at least free fblock blocks at the end of the volume */ +unsigned int +hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int block; + + for (block = PED_BE32_TO_CPU (priv_data->vh->total_blocks) - 1; + block && fblock; + block--) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + fblock--; + } + + while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block--; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block++; + + return block; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/advfs_plus.h b/libparted/fs/hfs/advfs_plus.h new file mode 100644 index 0000000..3d5ece4 --- /dev/null +++ b/libparted/fs/hfs/advfs_plus.h @@ -0,0 +1,52 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _ADVFS_PLUS_H +#define _ADVFS_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref); + +void +hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first); + +int +hfsplus_read_bad_blocks (const PedFileSystem *fs); + +int +hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock); + +PedSector +hfsplus_get_empty_end (const PedFileSystem *fs); + +PedSector +hfsplus_get_min_size (const PedFileSystem *fs); + +unsigned int +hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock); + +#endif /* _ADVFS_PLUS_H */ diff --git a/libparted/fs/hfs/cache.c b/libparted/fs/hfs/cache.c new file mode 100644 index 0000000..e3825d1 --- /dev/null +++ b/libparted/fs/hfs/cache.c @@ -0,0 +1,241 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef DISCOVER_ONLY + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" + +#include "cache.h" + +static HfsCPrivateCacheTable* +hfsc_new_cachetable(unsigned int size) +{ + HfsCPrivateCacheTable* ret; + + ret = (HfsCPrivateCacheTable*) ped_malloc(sizeof(*ret)); + if (!ret) return NULL; + + ret->next_cache = NULL; + ret->table_size = size; + ret->table_first_free = 0; + + ret->table = ped_malloc(sizeof(*ret->table)*size); + if (!ret->table) { ped_free(ret); return NULL; } + memset(ret->table, 0, sizeof(*ret->table)*size); + + return ret; +} + +HfsCPrivateCache* +hfsc_new_cache(unsigned int block_number, unsigned int file_number) +{ + unsigned int cachetable_size, i; + HfsCPrivateCache* ret; + + ret = (HfsCPrivateCache*) ped_malloc(sizeof(*ret)); + if (!ret) return NULL; + ret->block_number = block_number; + /* following code avoid integer overflow */ + ret->linked_ref_size = block_number > block_number + ((1<<CR_SHIFT)-1) ? + ( block_number >> CR_SHIFT ) + 1 : + ( block_number + ((1<<CR_SHIFT)-1) ) >> CR_SHIFT + ; + + ret->linked_ref = (HfsCPrivateExtent**) + ped_malloc( sizeof(*ret->linked_ref) + * ret->linked_ref_size ); + if (!ret->linked_ref) { ped_free(ret); return NULL; } + + cachetable_size = file_number + file_number / CR_OVER_DIV + CR_ADD_CST; + if (cachetable_size < file_number) cachetable_size = (unsigned) -1; + ret->first_cachetable_size = cachetable_size; + ret->table_list = hfsc_new_cachetable(cachetable_size); + if (!ret->table_list) { + ped_free(ret->linked_ref); + ped_free(ret); + return NULL; + } + ret->last_table = ret->table_list; + + for (i = 0; i < ret->linked_ref_size; ++i) + ret->linked_ref[i] = NULL; + + ret->needed_alloc_size = 0; + + return ret; +} + +static void +hfsc_delete_cachetable(HfsCPrivateCacheTable* list) +{ + HfsCPrivateCacheTable* next; + + while (list) { + ped_free (list->table); + next = list->next_cache; + ped_free (list); + list = next; + } +} + +void +hfsc_delete_cache(HfsCPrivateCache* cache) +{ + hfsc_delete_cachetable(cache->table_list); + ped_free(cache->linked_ref); + ped_free(cache); +} + +HfsCPrivateExtent* +hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length, + uint32_t block, uint16_t offset, uint8_t sbb, + uint8_t where, uint8_t index) +{ + HfsCPrivateExtent* ext; + unsigned int idx = start >> CR_SHIFT; + + PED_ASSERT(idx < cache->linked_ref_size, return NULL); + + for (ext = cache->linked_ref[idx]; + ext && start != ext->ext_start; + ext = ext->next); + + if (ext) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to register an extent starting at block " + "0x%X, but another one already exists at this " + "position. You should check the file system!"), + start); + return NULL; + } + + if ( cache->last_table->table_first_free + == cache->last_table->table_size ) { + cache->last_table->next_cache = + hfsc_new_cachetable( ( cache->first_cachetable_size + / CR_NEW_ALLOC_DIV ) + + CR_ADD_CST ); + if (!cache->last_table->next_cache) + return NULL; + cache->last_table = cache->last_table->next_cache; + } + + ext = cache->last_table->table+(cache->last_table->table_first_free++); + + ext->ext_start = start; + ext->ext_length = length; + ext->ref_block = block; + ext->ref_offset = offset; + ext->sect_by_block = sbb; + ext->where = where; + ext->ref_index = index; + + ext->next = cache->linked_ref[idx]; + cache->linked_ref[idx] = ext; + + cache->needed_alloc_size = cache->needed_alloc_size > + (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb ? + cache->needed_alloc_size : + (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb; + + return ext; +} + +HfsCPrivateExtent* +hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start) +{ + HfsCPrivateExtent* ret; + unsigned int idx = start >> CR_SHIFT; + + PED_ASSERT(idx < cache->linked_ref_size, return NULL); + + for (ret = cache->linked_ref[idx]; + ret && start != ret->ext_start; + ret = ret->next); + + return ret; +} + +/* Can't fail if extent begining at old_start exists */ +/* Returns 0 if no such extent, or on error */ +HfsCPrivateExtent* +hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start, + uint32_t new_start) +{ + HfsCPrivateExtent** ppext; + HfsCPrivateExtent* pext; + + unsigned int idx1 = old_start >> CR_SHIFT; + unsigned int idx2 = new_start >> CR_SHIFT; + + PED_ASSERT(idx1 < cache->linked_ref_size, return NULL); + PED_ASSERT(idx2 < cache->linked_ref_size, return NULL); + + for (pext = cache->linked_ref[idx2]; + pext && new_start != pext->ext_start; + pext = pext->next); + + if (pext) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Trying to move an extent from block Ox%X to block " + "Ox%X, but another one already exists at this " + "position. This should not happen!"), + old_start, new_start); + return NULL; + } + + for (ppext = &(cache->linked_ref[idx1]); + (*ppext) && old_start != (*ppext)->ext_start; + ppext = &((*ppext)->next)); + + if (!(*ppext)) return NULL; + + /* removing the extent from the cache */ + pext = *ppext; + (*ppext) = pext->next; + + /* change ext_start and insert the extent again */ + pext->ext_start = new_start; + pext->next = cache->linked_ref[idx2]; + cache->linked_ref[idx2] = pext; + + return pext; +} + +#endif /* DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/cache.h b/libparted/fs/hfs/cache.h new file mode 100644 index 0000000..9e49ef8 --- /dev/null +++ b/libparted/fs/hfs/cache.h @@ -0,0 +1,118 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _CACHE_H +#define _CACHE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +/* CR => CACHE REF */ +#define CR_NULL 0 /* reserved */ +#define CR_PRIM_CAT 1 +#define CR_PRIM_EXT 2 +#define CR_PRIM_ATTR 3 +#define CR_PRIM_ALLOC 4 +#define CR_PRIM_START 5 +#define CR_BTREE_CAT 6 +#define CR_BTREE_ATTR 7 +#define CR_BTREE_EXT_0 8 +#define CR_BTREE_EXT_CAT 9 +#define CR_BTREE_EXT_EXT 10 /* should not happen ! */ +#define CR_BTREE_EXT_ATTR 11 +#define CR_BTREE_EXT_ALLOC 12 +#define CR_BTREE_EXT_START 13 /* unneeded in current code */ +#define CR_BTREE_CAT_JIB 14 /* journal info block */ +#define CR_BTREE_CAT_JL 15 /* journal */ +/* 16 -> 31 || high order bit */ /* reserved */ + +/* tuning */ +#define CR_SHIFT 8 /* number of bits to shift start_block by */ + /* to get the index of the linked list */ +#define CR_OVER_DIV 16 /* alloc a table for (1+1/CR_OVER_DIV) * + file_number + CR_ADD_CST */ +#define CR_ADD_CST 16 +#define CR_NEW_ALLOC_DIV 4 /* divide the size of the first alloc table + by this value to allocate next tables */ + +/* See DOC for an explaination of this structure */ +/* Access read only from outside cache.c */ +struct _HfsCPrivateExtent { + struct _HfsCPrivateExtent* next; + uint32_t ext_start; + uint32_t ext_length; + uint32_t ref_block; + uint16_t ref_offset; + uint8_t sect_by_block; + unsigned where : 5; + unsigned ref_index : 3; /* 0 -> 7 */ +}; +typedef struct _HfsCPrivateExtent HfsCPrivateExtent; + +/* Internaly used by cache.c for custom memory managment only */ +struct _HfsCPrivateCacheTable { + struct _HfsCPrivateCacheTable* next_cache; + HfsCPrivateExtent* table; + unsigned int table_size; + unsigned int table_first_free; + /* first_elemt ? */ +}; +typedef struct _HfsCPrivateCacheTable HfsCPrivateCacheTable; + +/* Internaly used by cache.c for custom memory managment + and cache handling only */ +struct _HfsCPrivateCache { + HfsCPrivateCacheTable* table_list; + HfsCPrivateCacheTable* last_table; + HfsCPrivateExtent** linked_ref; + unsigned int linked_ref_size; + unsigned int block_number; + unsigned int first_cachetable_size; + unsigned int needed_alloc_size; +}; +typedef struct _HfsCPrivateCache HfsCPrivateCache; + +HfsCPrivateCache* +hfsc_new_cache(unsigned int block_number, unsigned int file_number); + +void +hfsc_delete_cache(HfsCPrivateCache* cache); + +HfsCPrivateExtent* +hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length, + uint32_t block, uint16_t offset, uint8_t sbb, + uint8_t where, uint8_t index); + +HfsCPrivateExtent* +hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start); + +HfsCPrivateExtent* +hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start, + uint32_t new_start); + +static __inline__ unsigned int +hfsc_cache_needed_buffer(HfsCPrivateCache* cache) +{ + return cache->needed_alloc_size; +} + +#endif /* _CACHE_H */ diff --git a/libparted/fs/hfs/file.c b/libparted/fs/hfs/file.c new file mode 100644 index 0000000..fe3616e --- /dev/null +++ b/libparted/fs/hfs/file.c @@ -0,0 +1,231 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef DISCOVER_ONLY + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs.h" + +#include "file.h" + +/* Open the data fork of a file with its first three extents and its CNID */ +HfsPrivateFile* +hfs_file_open (PedFileSystem *fs, uint32_t CNID, + HfsExtDataRec ext_desc, PedSector sect_nb) +{ + HfsPrivateFile* file; + + file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile)); + if (!file) return NULL; + + file->fs = fs; + file->sect_nb = sect_nb; + file->CNID = CNID; + memcpy(file->first, ext_desc, sizeof (HfsExtDataRec)); + file->start_cache = 0; + + return file; +} + +/* Close an HFS file */ +void +hfs_file_close (HfsPrivateFile* file) +{ + ped_free (file); +} + +/* warning : only works on data forks */ +static int +hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block, + HfsExtDataRec cache, uint16_t* ptr_start_cache) +{ + uint8_t record[sizeof (HfsExtentKey) + + sizeof (HfsExtDataRec)]; + HfsExtentKey search; + HfsExtentKey* ret_key = (HfsExtentKey*) record; + HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) + (record + sizeof (HfsExtentKey)); + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + file->fs->type_specific; + + search.key_length = sizeof (HfsExtentKey) - 1; + search.type = HFS_DATA_FORK; + search.file_ID = file->CNID; + search.start = PED_CPU_TO_BE16 (block); + + if (!hfs_btree_search (priv_data->extent_file, + (HfsPrivateGenericKey*) &search, + record, sizeof (record), NULL)) + return 0; + + if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) + return 0; + + memcpy (cache, ret_cache, sizeof(HfsExtDataRec)); + *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start); + + return 1; +} + +/* find and return the nth sector of a file */ +/* return 0 on error */ +static PedSector +hfs_file_find_sector (HfsPrivateFile* file, PedSector sector) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + file->fs->type_specific; + unsigned int sect_by_block = PED_BE32_TO_CPU ( + priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + unsigned int i, s, vol_block; + unsigned int block = sector / sect_by_block; + unsigned int offset = sector % sect_by_block; + + /* in the three first extent */ + for (s = 0, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && ( block < s + PED_BE16_TO_CPU ( + file->first[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->first[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->first[i].block_count); + } + + /* in the three cached extent */ + if (file->start_cache && block >= file->start_cache) + for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE16_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->cache[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->cache[i].block_count); + } + + /* update cache */ + if (!hfs_get_extent_containing (file, block, file->cache, + &(file->start_cache))) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_CANCEL, + _("Could not update the extent cache for HFS file with " + "CNID %X."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + /* in the three cached extent */ + PED_ASSERT(file->start_cache && block >= file->start_cache, return 0); + for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE16_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->cache[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->cache[i].block_count); + } + + return 0; + + sector_found: + return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block) + + (PedSector) vol_block * sect_by_block + + offset; +} + +/* Read the nth sector of a file */ +/* return 0 on error */ +int +hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector) +{ + PedSector abs_sector; + + if (sector >= file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to read HFS file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + abs_sector = hfs_file_find_sector (file, sector); + if (!abs_sector) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS file with " + "CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + return ped_geometry_read (file->fs->geom, buf, abs_sector, 1); +} + +/* Write the nth sector of a file */ +/* return 0 on error */ +int +hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector) +{ + PedSector abs_sector; + + if (sector >= file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to write HFS file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + abs_sector = hfs_file_find_sector (file, sector); + if (!abs_sector) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS file with " + "CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + return ped_geometry_write (file->fs->geom, buf, abs_sector, 1); +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/file.h b/libparted/fs/hfs/file.h new file mode 100644 index 0000000..7fcd21c --- /dev/null +++ b/libparted/fs/hfs/file.h @@ -0,0 +1,42 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _FILE_H +#define _FILE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +HfsPrivateFile* +hfs_file_open (PedFileSystem *fs, uint32_t CNID, + HfsExtDataRec ext_desc, PedSector sect_nb); + +void +hfs_file_close (HfsPrivateFile* file); + +int +hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector); + +int +hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector); + +#endif /* _FILE_H */ diff --git a/libparted/fs/hfs/file_plus.c b/libparted/fs/hfs/file_plus.c new file mode 100644 index 0000000..8b8fbfb --- /dev/null +++ b/libparted/fs/hfs/file_plus.c @@ -0,0 +1,274 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef DISCOVER_ONLY + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs_plus.h" + +#include "file_plus.h" + +/* Open the data fork of a file with its first eight extents and its CNID */ +/* CNID and ext_desc must be in disc order, sect_nb in CPU order */ +/* return null on failure */ +HfsPPrivateFile* +hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID, + HfsPExtDataRec ext_desc, PedSector sect_nb) +{ + HfsPPrivateFile* file; + + file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile)); + if (!file) return NULL; + + file->fs = fs; + file->sect_nb = sect_nb; + file->CNID = CNID; + memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec)); + file->start_cache = 0; + + return file; +} + +/* Close an HFS+ file */ +void +hfsplus_file_close (HfsPPrivateFile* file) +{ + ped_free (file); +} + +/* warning : only works on data forks */ +static int +hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block, + HfsPExtDataRec cache, uint32_t* ptr_start_cache) +{ + uint8_t record[sizeof (HfsPExtentKey) + + sizeof (HfsPExtDataRec)]; + HfsPExtentKey search; + HfsPExtentKey* ret_key = (HfsPExtentKey*) record; + HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) + (record + sizeof (HfsPExtentKey)); + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + + search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2); + search.type = HFS_DATA_FORK; + search.pad = 0; + search.file_ID = file->CNID; + search.start = PED_CPU_TO_BE32 (block); + + if (!hfsplus_btree_search (priv_data->extents_file, + (HfsPPrivateGenericKey*) &search, + record, sizeof (record), NULL)) + return 0; + + if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) + return 0; + + memcpy (cache, ret_cache, sizeof(HfsPExtDataRec)); + *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start); + + return 1; +} + +/* find a sub extent contained in the desired area */ +/* and with the same starting point */ +/* return 0 in sector_count on error, or the physical area */ +/* on the volume corresponding to the logical area in the file */ +static HfsPPrivateExtent +hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent ret = {0,0}; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + unsigned int sect_by_block = PED_BE32_TO_CPU ( + priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT; + unsigned int i, s, vol_block, size; + PedSector sect_size; + unsigned int block = sector / sect_by_block; + unsigned int offset = sector % sect_by_block; + + /* in the 8 first extent */ + for (s = 0, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->first[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->first[i] + .start_block); + size = PED_BE32_TO_CPU (file->first[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->first[i].block_count); + } + + /* in the 8 cached extent */ + if (file->start_cache && block >= file->start_cache) + for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->cache[i] + .start_block); + size = PED_BE32_TO_CPU (file->cache[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->cache[i].block_count); + } + + /* update cache */ + if (!hfsplus_get_extent_containing (file, block, file->cache, + &(file->start_cache))) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_CANCEL, + _("Could not update the extent cache for HFS+ file " + "with CNID %X."), + PED_BE32_TO_CPU(file->CNID)); + return ret; /* ret == {0,0} */ + } + + /* ret == {0,0} */ + PED_ASSERT(file->start_cache && block >= file->start_cache, return ret); + + for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->cache[i] + .start_block); + size = PED_BE32_TO_CPU (file->cache[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->cache[i].block_count); + } + + return ret; + +plus_sector_found: + sect_size = (PedSector) size * sect_by_block - offset; + ret.start_sector = vol_block * sect_by_block + offset; + ret.sector_count = (sect_size < nb) ? sect_size : nb; + return ret; +} + +int +hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent phy_area; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + + if (sector+nb < sector /* detect overflow */ + || sector+nb > file->sect_nb) /* out of file */ { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to read HFS+ file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + while (nb) { + phy_area = hfsplus_file_find_extent(file, sector, nb); + if (phy_area.sector_count == 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS+ file " + "with CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + if (!ped_geometry_read(priv_data->plus_geom, buf, + phy_area.start_sector, + phy_area.sector_count)) + return 0; + + nb -= phy_area.sector_count; /* < nb anyway ... */ + sector += phy_area.sector_count; + buf += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT; + } + + return 1; +} + +int +hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent phy_area; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + + if (sector+nb < sector /* detect overflow */ + || sector+nb > file->sect_nb) /* out of file */ { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to write HFS+ file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + while (nb) { + phy_area = hfsplus_file_find_extent(file, sector, nb); + if (phy_area.sector_count == 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS+ file " + "with CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + if (!ped_geometry_write(priv_data->plus_geom, buf, + phy_area.start_sector, + phy_area.sector_count)) + return 0; + + nb -= phy_area.sector_count; /* < nb anyway ... */ + sector += phy_area.sector_count; + buf += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT; + } + + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/file_plus.h b/libparted/fs/hfs/file_plus.h new file mode 100644 index 0000000..5f48554 --- /dev/null +++ b/libparted/fs/hfs/file_plus.h @@ -0,0 +1,61 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _FILE_PLUS_H +#define _FILE_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +HfsPPrivateFile* +hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID, + HfsPExtDataRec ext_desc, PedSector sect_nb); + +void +hfsplus_file_close (HfsPPrivateFile* file); + +int +hfsplus_file_read(HfsPPrivateFile* file, void *buf, + PedSector sector, unsigned int nb); + +int +hfsplus_file_write(HfsPPrivateFile* file, void *buf, + PedSector sector, unsigned int nb); + +/* Read the nth sector of a file */ +/* return 0 on error */ +static __inline__ int +hfsplus_file_read_sector (HfsPPrivateFile* file, void *buf, PedSector sector) +{ + return hfsplus_file_read(file, buf, sector, 1); +} + +/* Write the nth sector of a file */ +/* return 0 on error */ +static __inline__ int +hfsplus_file_write_sector (HfsPPrivateFile* file, void *buf, PedSector sector) +{ + return hfsplus_file_write(file, buf, sector, 1); +} + + +#endif /* _FILE_PLUS_H */ diff --git a/libparted/fs/hfs/hfs.c b/libparted/fs/hfs/hfs.c new file mode 100644 index 0000000..ee09342 --- /dev/null +++ b/libparted/fs/hfs/hfs.c @@ -0,0 +1,1355 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2003, 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/* + Author : Guillaume Knispel <k_guillaume@libertysurf.fr> + Report bug to <bug-parted@gnu.org> +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "probe.h" + +uint8_t* hfs_block = NULL; +uint8_t* hfsp_block = NULL; +unsigned hfs_block_count; +unsigned hfsp_block_count; + +#define HFS_BLOCK_SIZES ((int[2]){512, 0}) +#define HFSP_BLOCK_SIZES ((int[2]){512, 0}) +#define HFSX_BLOCK_SIZES ((int[2]){512, 0}) + +#ifndef DISCOVER_ONLY +#include "file.h" +#include "reloc.h" +#include "advfs.h" + + +/* ----- HFS ----- */ + +/* This is a very unundoable operation */ +/* Maybe I shouldn't touch the alternate MDB ? */ +/* Anyway clobber is call before other fs creation */ +/* So this is a non-issue */ +static int +hfs_clobber (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + + memset (buf, 0, PED_SECTOR_SIZE_DEFAULT); + + /* destroy boot blocks, mdb, alternate mdb ... */ + return (!!ped_geometry_write (geom, buf, 0, 1)) & + (!!ped_geometry_write (geom, buf, 1, 1)) & + (!!ped_geometry_write (geom, buf, 2, 1)) & + (!!ped_geometry_write (geom, buf, geom->length - 2, 1)) & + (!!ped_geometry_write (geom, buf, geom->length - 1, 1)) & + (!!ped_geometry_sync (geom)); +} + +static PedFileSystem* +hfs_open (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedFileSystem* fs; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFSData* priv_data; + + if (!hfsc_can_use_geom (geom)) + return NULL; + + /* Read MDB */ + if (!ped_geometry_read (geom, buf, 2, 1)) + return NULL; + + /* Allocate memory */ + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) goto ho; + mdb = (HfsMasterDirectoryBlock*) + ped_malloc (sizeof (HfsMasterDirectoryBlock)); + if (!mdb) goto ho_fs; + priv_data = (HfsPrivateFSData*) + ped_malloc (sizeof (HfsPrivateFSData)); + if (!priv_data) goto ho_mdb; + + memcpy (mdb, buf, sizeof (HfsMasterDirectoryBlock)); + + /* init structures */ + priv_data->mdb = mdb; + priv_data->bad_blocks_loaded = 0; + priv_data->bad_blocks_xtent_nb = 0; + priv_data->bad_blocks_xtent_list = NULL; + priv_data->extent_file = + hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID), + mdb->extents_file_rec, + PED_CPU_TO_BE32 (mdb->extents_file_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->extent_file) goto ho_pd; + priv_data->catalog_file = + hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID), + mdb->catalog_file_rec, + PED_CPU_TO_BE32 (mdb->catalog_file_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->catalog_file) goto ho_ce; + /* Read allocation blocks */ + if (!ped_geometry_read(geom, priv_data->alloc_map, + PED_BE16_TO_CPU (mdb->volume_bitmap_block), + ( PED_BE16_TO_CPU (mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8) ) ) + goto ho_cf; + + fs->type = &hfs_type; + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) goto ho_cf; + fs->type_specific = (void*) priv_data; + fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes) + >> HFS_UNMOUNTED ) & 1; + + return fs; + +/*--- clean error handling ---*/ +ho_cf: hfs_file_close(priv_data->catalog_file); +ho_ce: hfs_file_close(priv_data->extent_file); +ho_pd: ped_free(priv_data); +ho_mdb: ped_free(mdb); +ho_fs: ped_free(fs); +ho: return NULL; +} + +static int +hfs_close (PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific; + + hfs_file_close (priv_data->extent_file); + hfs_file_close (priv_data->catalog_file); + if (priv_data->bad_blocks_loaded) + hfs_free_bad_blocks_list (priv_data->bad_blocks_xtent_list); + ped_free (priv_data->mdb); + ped_free (priv_data); + ped_geometry_destroy (fs->geom); + ped_free (fs); + + return 1; +} + +static PedConstraint* +hfs_get_resize_constraint (const PedFileSystem *fs) +{ + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + /* 2 = last two sectors (alternate MDB and unused sector) */ + min_size = hfs_get_empty_end(fs) + 2; + if (min_size == 2) return NULL; + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + fs->geom->length); +} + +static int +hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + unsigned int nblock, nfree; + unsigned int block, to_free; + HfsPrivateFSData* priv_data; + HfsMasterDirectoryBlock* mdb; + int resize = 1; + unsigned int hfs_sect_block; + PedSector hgee; + + /* check preconditions */ + PED_ASSERT (fs != NULL, return 0); + PED_ASSERT (fs->geom != NULL, return 0); + PED_ASSERT (geom != NULL, return 0); +#ifdef DEBUG + PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0); +#else + if ((hgee = hfs_get_empty_end(fs)) == 0) + return 0; +#endif + + PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0); + + if (ped_geometry_test_equal(fs->geom, geom)) + return 1; + + priv_data = (HfsPrivateFSData*) fs->type_specific; + mdb = priv_data->mdb; + hfs_sect_block = PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + + if (fs->geom->start != geom->start + || geom->length > fs->geom->length + || geom->length < hgee + 2) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, HFS cannot be resized that way yet.")); + return 0; + } + + /* Flush caches */ + if (!ped_geometry_sync(fs->geom)) + return 0; + + /* Clear the unmounted bit */ + mdb->volume_attributes &= PED_CPU_TO_BE16 (~( 1 << HFS_UNMOUNTED )); + if (!ped_geometry_read (fs->geom, buf, 2, 1)) + return 0; + memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock)); + if ( !ped_geometry_write (fs->geom, buf, 2, 1) + || !ped_geometry_sync (fs->geom)) + return 0; + + ped_timer_reset (timer); + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + /* relocate data */ + to_free = ( fs->geom->length - geom->length + + hfs_sect_block - 1 ) + / hfs_sect_block ; + block = hfs_find_start_pack (fs, to_free); + if (!hfs_pack_free_space_from_block (fs, block, timer, to_free)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation has failed.")); + goto write_MDB; + } + + /* Calculate new block number and other MDB field */ + nblock = ( geom->length - (PED_BE16_TO_CPU (mdb->start_block) + 2) ) + / hfs_sect_block; + nfree = PED_BE16_TO_CPU (mdb->free_blocks) + - ( PED_BE16_TO_CPU (mdb->total_blocks) - nblock ); + + /* Check that all block after future end are really free */ + for (block = nblock; + block < PED_BE16_TO_CPU (mdb->total_blocks); + block++) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation left some data in the end " + "of the volume.")); + goto write_MDB; + } + } + + /* Mark out of volume blocks as used + (broken implementations compatibility) */ + for ( block = nblock; block < (1 << 16); ++block) + SET_BLOC_OCCUPATION(priv_data->alloc_map,block); + + /* save the allocation map + I do not write until start of allocation blocks + but only until pre-resize end of bitmap blocks + because the specifications do _not_ assert that everything + until allocation blocks is boot, mdb and alloc */ + ped_geometry_write(fs->geom, priv_data->alloc_map, + PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block), + ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1) + / (PED_SECTOR_SIZE_DEFAULT * 8)); + + /* Update geometry */ + if (resize) { + /* update in fs structure */ + if (PED_BE16_TO_CPU (mdb->next_allocation) >= nblock) + mdb->next_allocation = PED_CPU_TO_BE16 (0); + mdb->total_blocks = PED_CPU_TO_BE16 (nblock); + mdb->free_blocks = PED_CPU_TO_BE16 (nfree); + /* update parted structure */ + fs->geom->length = geom->length; + fs->geom->end = fs->geom->start + geom->length - 1; + } + + /* Set the unmounted bit */ + mdb->volume_attributes |= PED_CPU_TO_BE16 ( 1 << HFS_UNMOUNTED ); + + /* Effective write */ + write_MDB: + ped_timer_set_state_name(timer,_("writing HFS Master Directory Block")); + + if (!hfs_update_mdb(fs)) { + ped_geometry_sync(geom); + return 0; + } + + if (!ped_geometry_sync(geom)) + return 0; + + ped_timer_update(timer, 1.0); + + return (resize); +} + +/* ----- HFS+ ----- */ + +#include "file_plus.h" +#include "advfs_plus.h" +#include "reloc_plus.h" +#include "journal.h" + +static int +hfsplus_clobber (PedGeometry* geom) +{ + unsigned int i = 1; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + HfsMasterDirectoryBlock *mdb; + + mdb = (HfsMasterDirectoryBlock *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1)) + return 0; + + if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) { + /* embedded hfs+ */ + PedGeometry *embedded; + + i = PED_BE32_TO_CPU(mdb->block_size) / PED_SECTOR_SIZE_DEFAULT; + embedded = ped_geometry_new ( + geom->dev, + (PedSector) geom->start + + PED_BE16_TO_CPU (mdb->start_block) + + (PedSector) PED_BE16_TO_CPU ( + mdb->old_new.embedded.location.start_block ) * i, + (PedSector) PED_BE16_TO_CPU ( + mdb->old_new.embedded.location.block_count ) * i ); + if (!embedded) i = 0; + else { + i = hfs_clobber (embedded); + ped_geometry_destroy (embedded); + } + } + + /* non-embedded or envelop destroy as hfs */ + return ( hfs_clobber (geom) && i ); +} + +static int +hfsplus_close (PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + ped_free(priv_data->alloc_map); + ped_free(priv_data->dirty_alloc_map); + hfsplus_file_close (priv_data->allocation_file); + hfsplus_file_close (priv_data->attributes_file); + hfsplus_file_close (priv_data->catalog_file); + hfsplus_file_close (priv_data->extents_file); + if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); + if (priv_data->wrapper) hfs_close(priv_data->wrapper); + ped_geometry_destroy (fs->geom); + ped_free(priv_data->vh); + ped_free(priv_data); + ped_free(fs); + + return 1; +} + +static PedFileSystem* +hfsplus_open (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedFileSystem* fs; + HfsPVolumeHeader* vh; + HfsPPrivateFSData* priv_data; + PedGeometry* wrapper_geom; + unsigned int map_sectors; + + if (!hfsc_can_use_geom (geom)) + return NULL; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) goto hpo; + vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader)); + if (!vh) goto hpo_fs; + priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData)); + if (!priv_data) goto hpo_vh; + + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) goto hpo_pd; + fs->type_specific = (void*) priv_data; + + if ((wrapper_geom = hfs_and_wrapper_probe (geom))) { + HfsPrivateFSData* hfs_priv_data; + PedSector abs_sect, length; + unsigned int bs; + + ped_geometry_destroy (wrapper_geom); + priv_data->wrapper = hfs_open(geom); + if (!priv_data->wrapper) goto hpo_gm; + hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + abs_sect = (PedSector) geom->start + + (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->start_block) + + (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.start_block ) + * bs; + length = (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.block_count) + * bs; + priv_data->plus_geom = ped_geometry_new (geom->dev, abs_sect, + length); + if (!priv_data->plus_geom) goto hpo_wr; + priv_data->free_geom = 1; + } else { + priv_data->wrapper = NULL; + priv_data->plus_geom = fs->geom; + priv_data->free_geom = 0; + } + + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) goto hpo_pg; + memcpy (vh, buf, sizeof (HfsPVolumeHeader)); + priv_data->vh = vh; + + if (vh->signature != PED_CPU_TO_BE16(HFSP_SIGNATURE) + && vh->signature != PED_CPU_TO_BE16(HFSX_SIGNATURE)) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("No valid HFS[+X] signature has been found while " + "opening.")); + goto hpo_pg; + } + + if (vh->signature == PED_CPU_TO_BE16(HFSP_SIGNATURE) + && vh->version != PED_CPU_TO_BE16(HFSP_VERSION)) { + if (ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_IGNORE_CANCEL, + _("Version %d of HFS+ isn't supported."), + PED_BE16_TO_CPU(vh->version)) + != PED_EXCEPTION_IGNORE) + goto hpo_pg; + } + + if (vh->signature == PED_CPU_TO_BE16(HFSX_SIGNATURE) + && vh->version != PED_CPU_TO_BE16(HFSX_VERSION)) { + if (ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_IGNORE_CANCEL, + _("Version %d of HFSX isn't supported."), + PED_BE16_TO_CPU(vh->version)) + != PED_EXCEPTION_IGNORE) + goto hpo_pg; + } + + priv_data->jib_start_block = 0; + priv_data->jl_start_block = 0; + if (vh->attributes & PED_CPU_TO_BE32(1<<HFSP_JOURNALED)) { + if (!hfsj_replay_journal(fs)) + goto hpo_pg; + } + + priv_data->bad_blocks_loaded = 0; + priv_data->bad_blocks_xtent_nb = 0; + priv_data->bad_blocks_xtent_list = NULL; + priv_data->extents_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID), + vh->extents_file.extents, + PED_BE64_TO_CPU ( + vh->extents_file.logical_size ) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->extents_file) goto hpo_pg; + priv_data->catalog_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID), + vh->catalog_file.extents, + PED_BE64_TO_CPU ( + vh->catalog_file.logical_size ) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->catalog_file) goto hpo_ce; + priv_data->attributes_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ATTRIB_ID), + vh->attributes_file.extents, + PED_BE64_TO_CPU ( + vh->attributes_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->attributes_file) goto hpo_cc; + + map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + priv_data->dirty_alloc_map = (uint8_t*) + ped_malloc ((map_sectors + 7) / 8); + if (!priv_data->dirty_alloc_map) goto hpo_cl; + memset(priv_data->dirty_alloc_map, 0, (map_sectors + 7) / 8); + priv_data->alloc_map = (uint8_t*) + ped_malloc (map_sectors * PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->alloc_map) goto hpo_dm; + + priv_data->allocation_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID), + vh->allocation_file.extents, + PED_BE64_TO_CPU ( + vh->allocation_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->allocation_file) goto hpo_am; + if (!hfsplus_file_read (priv_data->allocation_file, + priv_data->alloc_map, 0, map_sectors)) { + hfsplus_close(fs); + return NULL; + } + + fs->type = &hfsplus_type; + fs->checked = ((PED_BE32_TO_CPU (vh->attributes) >> HFS_UNMOUNTED) & 1) + && !((PED_BE32_TO_CPU (vh->attributes) >> HFSP_INCONSISTENT) & 1); + + return fs; + +/*--- clean error handling ---*/ +hpo_am: ped_free(priv_data->alloc_map); +hpo_dm: ped_free(priv_data->dirty_alloc_map); +hpo_cl: hfsplus_file_close (priv_data->attributes_file); +hpo_cc: hfsplus_file_close (priv_data->catalog_file); +hpo_ce: hfsplus_file_close (priv_data->extents_file); +hpo_pg: if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); +hpo_wr: if (priv_data->wrapper) hfs_close(priv_data->wrapper); +hpo_gm: ped_geometry_destroy (fs->geom); +hpo_pd: ped_free(priv_data); +hpo_vh: ped_free(vh); +hpo_fs: ped_free(fs); +hpo: return NULL; +} + +static PedConstraint* +hfsplus_get_resize_constraint (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + min_size = hfsplus_get_min_size (fs); + if (!min_size) return NULL; + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + fs->geom->length); +} + +static int +hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + unsigned int nblock, nfree, mblock; + unsigned int block, to_free, old_blocks; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + int resize = 1; + unsigned int hfsp_sect_block = + ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); + unsigned int map_sectors; + + old_blocks = PED_BE32_TO_CPU (vh->total_blocks); + + /* Flush caches */ + if (!ped_geometry_sync(priv_data->plus_geom)) + return 0; + + /* Clear the unmounted bit */ + /* and set the implementation code (Apple Creator Code) */ + vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED )); + vh->last_mounted_version = PED_CPU_TO_BE32(HFSP_IMPL_Shnk); + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) + return 0; + memcpy (buf, vh, sizeof (HfsPVolumeHeader)); + if ( !ped_geometry_write (priv_data->plus_geom, buf, 2, 1) + || !ped_geometry_sync (priv_data->plus_geom)) + return 0; + + ped_timer_reset (timer); + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + /* relocate data */ + to_free = ( priv_data->plus_geom->length + - geom->length + hfsp_sect_block + - 1 ) / hfsp_sect_block; + block = hfsplus_find_start_pack (fs, to_free); + if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation has failed.")); + goto write_VH; + } + + /* Calculate new block number and other VH field */ + /* nblock must be rounded _down_ */ + nblock = geom->length / hfsp_sect_block; + nfree = PED_BE32_TO_CPU (vh->free_blocks) + - (old_blocks - nblock); + /* free block readjustement is only needed when incorrect nblock + was used by my previous implementation, so detect the case */ + if (priv_data->plus_geom->length < old_blocks + * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT) ) { + if (priv_data->plus_geom->length % hfsp_sect_block == 1) + nfree++; + } + + /* Check that all block after future end are really free */ + mblock = ( priv_data->plus_geom->length - 2 ) + / hfsp_sect_block; + if (mblock > old_blocks - 1) + mblock = old_blocks - 1; + for ( block = nblock; + block < mblock; + block++ ) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation left some data at the end " + "of the volume.")); + goto write_VH; + } + } + + /* Mark out of volume blocks as used */ + map_sectors = ( ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8) ) + * (PED_SECTOR_SIZE_DEFAULT * 8); + for ( block = nblock; block < map_sectors; ++block) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + + /* Update geometry */ + if (resize) { + /* update in fs structure */ + if (PED_BE32_TO_CPU (vh->next_allocation) >= nblock) + vh->next_allocation = PED_CPU_TO_BE32 (0); + vh->total_blocks = PED_CPU_TO_BE32 (nblock); + vh->free_blocks = PED_CPU_TO_BE32 (nfree); + /* update parted structure */ + priv_data->plus_geom->length = geom->length; + priv_data->plus_geom->end = priv_data->plus_geom->start + + geom->length - 1; + } + + /* Effective write */ + write_VH: + /* lasts two sectors are allocated by the alternate VH + and a reserved sector, and last block is always reserved */ + block = (priv_data->plus_geom->length - 1) / hfsp_sect_block; + if (block < PED_BE32_TO_CPU (vh->total_blocks)) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + block = (priv_data->plus_geom->length - 2) / hfsp_sect_block; + if (block < PED_BE32_TO_CPU (vh->total_blocks)) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + SET_BLOC_OCCUPATION(priv_data->alloc_map, + PED_BE32_TO_CPU (vh->total_blocks) - 1); + + /* Write the _old_ area to set out of volume blocks as used */ + map_sectors = ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + if (!hfsplus_file_write (priv_data->allocation_file, + priv_data->alloc_map, 0, map_sectors)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Error while writing the allocation file.")); + } else { + /* Write remaining part of allocation bitmap */ + /* This is necessary to handle pre patch-11 and third party */ + /* implementations */ + memset(buf, 0xFF, PED_SECTOR_SIZE_DEFAULT); + for (block = map_sectors; + block < priv_data->allocation_file->sect_nb; + ++block) { + if (!hfsplus_file_write_sector ( + priv_data->allocation_file, + buf, block)) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("Error while writing the " + "compatibility part of the " + "allocation file.")); + break; + } + } + } + ped_geometry_sync (priv_data->plus_geom); + + if (resize) { + /* Set the unmounted bit and clear the inconsistent bit */ + vh->attributes |= PED_CPU_TO_BE32 ( 1 << HFS_UNMOUNTED ); + vh->attributes &= ~ PED_CPU_TO_BE32 ( 1 << HFSP_INCONSISTENT ); + } + + ped_timer_set_state_name(timer, _("writing HFS+ Volume Header")); + if (!hfsplus_update_vh(fs)) { + ped_geometry_sync(priv_data->plus_geom); + return 0; + } + + if (!ped_geometry_sync(priv_data->plus_geom)) + return 0; + + ped_timer_update(timer, 1.0); + + return (resize); +} + +/* Update the HFS wrapper mdb and bad blocks file to reflect + the new geometry of the embedded HFS+ volume */ +static int +hfsplus_wrapper_update (PedFileSystem* fs) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsCPrivateLeafRec ref; + HfsExtentKey key; + HfsNodeDescriptor* node_desc = (HfsNodeDescriptor*) node; + HfsExtentKey* ret_key; + HfsExtDescriptor* ret_data; + unsigned int i, j; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT ; + PedSector hfsplus_sect = (PedSector) + PED_BE32_TO_CPU (priv_data->vh->total_blocks) + * ( PED_BE32_TO_CPU (priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); + unsigned int hfs_blocks_embedded = + (hfsplus_sect + hfs_sect_block - 1) + / hfs_sect_block; + unsigned int hfs_blocks_embedded_old; + + /* update HFS wrapper MDB */ + hfs_blocks_embedded_old = PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.block_count ); + hfs_priv_data->mdb->old_new.embedded.location.block_count = + PED_CPU_TO_BE16 (hfs_blocks_embedded); + /* maybe macOS will boot with this */ + /* update : yes it does \o/ :) */ + hfs_priv_data->mdb->free_blocks = + PED_CPU_TO_BE16 ( PED_BE16_TO_CPU (hfs_priv_data->mdb->free_blocks) + + hfs_blocks_embedded_old + - hfs_blocks_embedded ); + + if (!hfs_update_mdb(priv_data->wrapper)) + return 0; + + /* force reload bad block list */ + if (hfs_priv_data->bad_blocks_loaded) { + hfs_free_bad_blocks_list (hfs_priv_data->bad_blocks_xtent_list); + hfs_priv_data->bad_blocks_xtent_list = NULL; + hfs_priv_data->bad_blocks_xtent_nb = 0; + hfs_priv_data->bad_blocks_loaded = 0; + } + + /* clean HFS wrapper allocation map */ + for (i = PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new.embedded + .location.start_block ) + + hfs_blocks_embedded; + i < PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new.embedded + .location.start_block ) + + hfs_blocks_embedded_old; + i++ ) { + CLR_BLOC_OCCUPATION(hfs_priv_data->alloc_map, i); + } + /* and save it */ + if (!ped_geometry_write (fs->geom, hfs_priv_data->alloc_map, + PED_BE16_TO_CPU ( + hfs_priv_data->mdb->volume_bitmap_block ), + ( PED_BE16_TO_CPU ( + hfs_priv_data->mdb->total_blocks ) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8))) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + + /* search and update the bad blocks file */ + key.key_length = sizeof(key) - 1; + key.type = HFS_DATA_FORK; + key.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + key.start = 0; + if (!hfs_btree_search (hfs_priv_data->extent_file, + (HfsPrivateGenericKey*) &key, NULL, 0, &ref)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred while looking for the mandatory " + "bad blocks file.")); + return 0; + } + if (!hfs_file_read_sector (hfs_priv_data->extent_file, node, + ref.node_number)) + return 0; + ret_key = (HfsExtentKey*) (node + ref.record_pos); + ret_data = (HfsExtDescriptor*) ( node + ref.record_pos + + sizeof (HfsExtentKey) ); + + while (ret_key->type == key.type && ret_key->file_ID == key.file_ID) { + for (i = 0; i < HFS_EXT_NB; i++) { + if ( ret_data[i].start_block + == hfs_priv_data->mdb->old_new + .embedded.location.start_block) { + ret_data[i].block_count = + hfs_priv_data->mdb->old_new + .embedded.location.block_count; + /* found ! : update */ + if (!hfs_file_write_sector ( + hfs_priv_data->extent_file, + node, ref.node_number) + || !ped_geometry_sync(fs->geom)) + return 0; + return 1; + } + } + + if (ref.record_number < PED_BE16_TO_CPU (node_desc->rec_nb)) { + ref.record_number++; + } else { + ref.node_number = PED_BE32_TO_CPU (node_desc->next); + if (!ref.node_number + || !hfs_file_read_sector(hfs_priv_data->extent_file, + node, ref.node_number)) + goto bb_not_found; + ref.record_number = 1; + } + + ref.record_pos = + PED_BE16_TO_CPU (*((uint16_t *) + (node + (PED_SECTOR_SIZE_DEFAULT + - 2*ref.record_number)))); + ret_key = (HfsExtentKey*) (node + ref.record_pos); + ret_data = (HfsExtDescriptor*) ( node + ref.record_pos + + sizeof (HfsExtentKey) ); + } + +bb_not_found: + /* not found : not a valid hfs+ wrapper : failure */ + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("It seems there is an error in the HFS wrapper: the bad " + "blocks file doesn't contain the embedded HFS+ volume.")); + return 0; +} + +static int +hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data; + PedTimer* timer_plus; + PedGeometry* embedded_geom; + PedSector hgms; + + /* check preconditions */ + PED_ASSERT (fs != NULL, return 0); + PED_ASSERT (fs->geom != NULL, return 0); + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (fs->geom->dev == geom->dev, return 0); +#ifdef DEBUG + PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0, return 0); +#else + if ((hgms = hfsplus_get_min_size (fs)) == 0) + return 0; +#endif + + if (ped_geometry_test_equal(fs->geom, geom)) + return 1; + + priv_data = (HfsPPrivateFSData*) fs->type_specific; + + if (fs->geom->start != geom->start + || geom->length > fs->geom->length + || geom->length < hgms) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, HFS+ cannot be resized that way yet.")); + return 0; + } + + if (priv_data->wrapper) { + PedSector red, hgee; + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + + /* There is a wrapper so we must calculate the new geometry + of the embedded HFS+ volume */ + red = ( (fs->geom->length - geom->length + hfs_sect_block - 1) + / hfs_sect_block ) * hfs_sect_block; + /* Can't we shrink the hfs+ volume by the desired size ? */ + hgee = hfsplus_get_empty_end (fs); + if (!hgee) return 0; + if (red > priv_data->plus_geom->length - hgee) { + /* No, shrink hfs+ by the greatest possible value */ + hgee = ((hgee + hfs_sect_block - 1) / hfs_sect_block) + * hfs_sect_block; + red = priv_data->plus_geom->length - hgee; + } + embedded_geom = ped_geometry_new (geom->dev, + priv_data->plus_geom->start, + priv_data->plus_geom->length + - red); + + /* There is a wrapper so the resize process is a two stages + process (embedded resizing then wrapper resizing) : + we create a sub timer */ + ped_timer_reset (timer); + ped_timer_set_state_name (timer, + _("shrinking embedded HFS+ volume")); + ped_timer_update(timer, 0.0); + timer_plus = ped_timer_new_nested (timer, 0.98); + } else { + /* No wrapper : the desired geometry is the desired + HFS+ volume geometry */ + embedded_geom = geom; + timer_plus = timer; + } + + /* Resize the HFS+ volume */ + if (!hfsplus_volume_resize (fs, embedded_geom, timer_plus)) { + if (timer_plus != timer) ped_timer_destroy_nested (timer_plus); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Resizing the HFS+ volume has failed.")); + return 0; + } + + if (priv_data->wrapper) { + ped_geometry_destroy (embedded_geom); + ped_timer_destroy_nested (timer_plus); + ped_timer_set_state_name(timer, _("shrinking HFS wrapper")); + timer_plus = ped_timer_new_nested (timer, 0.02); + /* There's a wrapper : second stage = resizing it */ + if (!hfsplus_wrapper_update (fs) + || !hfs_resize (priv_data->wrapper, geom, timer_plus)) { + ped_timer_destroy_nested (timer_plus); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Updating the HFS wrapper has failed.")); + return 0; + } + ped_timer_destroy_nested (timer_plus); + } + ped_timer_update(timer, 1.0); + + return 1; +} + +#ifdef HFS_EXTRACT_FS +/* The following is for debugging purpose only, NOT for packaging */ + +#include <stdio.h> + +uint8_t* extract_buffer = NULL; + +static int +hfs_extract_file(const char* filename, HfsPrivateFile* hfs_file) +{ + FILE* fout; + PedSector sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (sect = 0; sect < hfs_file->sect_nb; ++sect) { + if (!hfs_file_read_sector(hfs_file, extract_buffer, sect)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + } + + fclose(fout); + return 1; +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract_bitmap(const char* filename, PedFileSystem* fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + unsigned int count; + FILE* fout; + PedSector sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (sect = PED_BE16_TO_CPU(mdb->volume_bitmap_block); + sect < PED_BE16_TO_CPU(mdb->start_block); + sect += count) { + uint16_t st_block = PED_BE16_TO_CPU(mdb->start_block); + count = (st_block-sect) < BLOCK_MAX_BUFF ? + (st_block-sect) : BLOCK_MAX_BUFF; + if (!ped_geometry_read(fs->geom, extract_buffer, sect, count)) + goto err_close; + if (!fwrite (extract_buffer, count * PED_SECTOR_SIZE_DEFAULT, + 1, fout)) + goto err_close; + } + + fclose(fout); + return 1; +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract_mdb (const char* filename, PedFileSystem* fs) +{ + FILE* fout; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + if (!ped_geometry_read(fs->geom, extract_buffer, 2, 1)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + + fclose(fout); + return 1; +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract (PedFileSystem* fs, PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This is not a real %s check. This is going to extract " + "special low level files for debugging purposes."), + "HFS"); + + extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT); + if (!extract_buffer) return 0; + + hfs_extract_mdb(HFS_MDB_FILENAME, fs); + hfs_extract_file(HFS_CATALOG_FILENAME, priv_data->catalog_file); + hfs_extract_file(HFS_EXTENTS_FILENAME, priv_data->extent_file); + hfs_extract_bitmap(HFS_BITMAP_FILENAME, fs); + + ped_free(extract_buffer); extract_buffer = NULL; + return 0; /* nothing has been fixed by us ! */ +} + +static int +hfsplus_extract_file(const char* filename, HfsPPrivateFile* hfsp_file) +{ + FILE* fout; + unsigned int cp_sect; + PedSector rem_sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (rem_sect = hfsp_file->sect_nb; rem_sect; rem_sect -= cp_sect) { + cp_sect = rem_sect < BLOCK_MAX_BUFF ? rem_sect : BLOCK_MAX_BUFF; + if (!hfsplus_file_read(hfsp_file, extract_buffer, + hfsp_file->sect_nb - rem_sect, cp_sect)) + goto err_close; + if (!fwrite (extract_buffer, cp_sect * PED_SECTOR_SIZE_DEFAULT, + 1, fout)) + goto err_close; + } + + fclose(fout); + return 1; +err_close: + fclose(fout); + return 0; +} + +static int +hfsplus_extract_vh (const char* filename, PedFileSystem* fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + FILE* fout; + PedGeometry* geom = priv_data->plus_geom; + + + fout = fopen(filename, "w"); + if (!fout) return 0; + + if (!ped_geometry_read(geom, extract_buffer, 2, 1)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + + fclose(fout); + return 1; +err_close: + fclose(fout); + return 0; +} + +/* TODO : use the timer to report what is happening */ +/* TODO : use exceptions to report errors */ +static int +hfsplus_extract (PedFileSystem* fs, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + HfsPPrivateFile* startup_file; + + if (priv_data->wrapper) { + /* TODO : create nested timer */ + hfs_extract (priv_data->wrapper, timer); + } + + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This is not a real %s check. This is going to extract " + "special low level files for debugging purposes."), + "HFS+"); + + extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT); + if (!extract_buffer) return 0; + + hfsplus_extract_vh(HFSP_VH_FILENAME, fs); + hfsplus_extract_file(HFSP_CATALOG_FILENAME, priv_data->catalog_file); + hfsplus_extract_file(HFSP_EXTENTS_FILENAME, priv_data->extents_file); + hfsplus_extract_file(HFSP_ATTRIB_FILENAME, priv_data->attributes_file); + hfsplus_extract_file(HFSP_BITMAP_FILENAME, priv_data->allocation_file); + + startup_file = hfsplus_file_open(fs, PED_CPU_TO_BE32(HFSP_STARTUP_ID), + vh->startup_file.extents, + PED_BE64_TO_CPU ( + vh->startup_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (startup_file) { + hfsplus_extract_file(HFSP_STARTUP_FILENAME, startup_file); + hfsplus_file_close(startup_file); startup_file = NULL; + } + + ped_free(extract_buffer); extract_buffer = NULL; + return 0; /* nothing has been fixed by us ! */ +} +#endif /* HFS_EXTRACT_FS */ + +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps hfs_ops = { + probe: hfs_probe, +#ifndef DISCOVER_ONLY + clobber: hfs_clobber, + open: hfs_open, + create: NULL, + close: hfs_close, +#ifndef HFS_EXTRACT_FS + check: NULL, +#else + check: hfs_extract, +#endif + copy: NULL, + resize: hfs_resize, + get_create_constraint: NULL, + get_resize_constraint: hfs_get_resize_constraint, + get_copy_constraint: NULL, +#else /* DISCOVER_ONLY */ + 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 /* DISCOVER_ONLY */ +}; + +static PedFileSystemOps hfsplus_ops = { + probe: hfsplus_probe, +#ifndef DISCOVER_ONLY + clobber: hfsplus_clobber, + open: hfsplus_open, + create: NULL, + close: hfsplus_close, +#ifndef HFS_EXTRACT_FS + check: NULL, +#else + check: hfsplus_extract, +#endif + copy: NULL, + resize: hfsplus_resize, + get_create_constraint: NULL, + get_resize_constraint: hfsplus_get_resize_constraint, + get_copy_constraint: NULL, +#else /* DISCOVER_ONLY */ + 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 /* DISCOVER_ONLY */ +}; + +static PedFileSystemOps hfsx_ops = { + probe: hfsx_probe, +#ifndef DISCOVER_ONLY + clobber: hfs_clobber, /* NOT hfsplus_clobber ! + HFSX can't be embedded */ + open: hfsplus_open, + create: NULL, + close: hfsplus_close, +#ifndef HFS_EXTRACT_FS + check: NULL, +#else + check: hfsplus_extract, +#endif + copy: NULL, + resize: hfsplus_resize, + get_create_constraint: NULL, + get_resize_constraint: hfsplus_get_resize_constraint, + get_copy_constraint: NULL, +#else /* DISCOVER_ONLY */ + 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 /* DISCOVER_ONLY */ +}; + + +static PedFileSystemType hfs_type = { + next: NULL, + ops: &hfs_ops, + name: "hfs", + block_sizes: HFS_BLOCK_SIZES +}; + +static PedFileSystemType hfsplus_type = { + next: NULL, + ops: &hfsplus_ops, + name: "hfs+", + block_sizes: HFSP_BLOCK_SIZES +}; + +static PedFileSystemType hfsx_type = { + next: NULL, + ops: &hfsx_ops, + name: "hfsx", + block_sizes: HFSX_BLOCK_SIZES +}; + +void +ped_file_system_hfs_init () +{ + ped_file_system_type_register (&hfs_type); + ped_file_system_type_register (&hfsplus_type); + ped_file_system_type_register (&hfsx_type); +} + +void +ped_file_system_hfs_done () +{ + ped_file_system_type_unregister (&hfs_type); + ped_file_system_type_unregister (&hfsplus_type); + ped_file_system_type_unregister (&hfsx_type); +} diff --git a/libparted/fs/hfs/hfs.h b/libparted/fs/hfs/hfs.h new file mode 100644 index 0000000..490c8f2 --- /dev/null +++ b/libparted/fs/hfs/hfs.h @@ -0,0 +1,651 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2003, 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _HFS_H +#define _HFS_H + +/* WARNING : bn is used 2 times in theses macro */ +/* so _never_ use side effect operators when using them */ +#define TST_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) & (1<<(7-((bn)&7)))) +#define SET_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) |= (1<<(7-((bn)&7)))) +#define CLR_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) &= ~(1<<(7-((bn)&7)))) + +/* Maximum number of blocks for the copy buffers */ +#define BLOCK_MAX_BUFF 256 +/* Maximum size of the copy buffers, in bytes */ +#define BYTES_MAX_BUFF 8388608 + +/* Apple Creator Codes follow */ +#define HFSP_IMPL_Shnk 0x53686e6b /* in use */ +#define HFSP_IMPL_Xpnd 0x58706e64 /* reserved */ +#define HFSP_IMPL_Resz 0x5265737a /* reserved */ +#define HFSP_IMPL_PHpx 0x50482b78 /* reserved */ +#define HFSP_IMPL_traP 0x74726150 /* reserved */ +#define HFSP_IMPL_GnuP 0x476e7550 /* reserved */ + +#define HFS_SIGNATURE 0x4244 /* 'BD' */ +#define HFSP_SIGNATURE 0x482B /* 'H+' */ +#define HFSX_SIGNATURE 0x4858 /* 'HX' */ + +#define HFSP_VERSION 4 +#define HFSX_VERSION 5 + +#define HFS_HARD_LOCK 7 +#define HFS_UNMOUNTED 8 +#define HFS_BAD_SPARED 9 +#define HFS_SOFT_LOCK 15 +#define HFSP_NO_CACHE 10 +#define HFSP_INCONSISTENT 11 +#define HFSP_REUSE_CNID 12 +#define HFSP_JOURNALED 13 + +#define HFS_IDX_NODE 0x00 +#define HFS_HDR_NODE 0x01 +#define HFS_MAP_NODE 0x02 +#define HFS_LEAF_NODE 0xFF + +#define HFS_FIRST_REC 0x0E +#define HFS_NSD_HD_REC 0x78 +#define HFS_MAP_REC 0xF8 + +#define HFS_DATA_FORK 0x00 +#define HFS_RES_FORK 0xFF + +#define HFS_CAT_DIR 0x01 +#define HFS_CAT_FILE 0x02 +#define HFS_CAT_DIR_TH 0x03 +#define HFS_CAT_FILE_TH 0x04 + +#define HFSP_ATTR_INLINE 0x10 +#define HFSP_ATTR_FORK 0x20 +#define HFSP_ATTR_EXTENTS 0x30 + +#define HFS_ROOT_PAR_ID 0x01 +#define HFS_ROOT_DIR_ID 0x02 +#define HFS_XTENT_ID 0x03 +#define HFS_CATALOG_ID 0x04 +#define HFS_BAD_BLOCK_ID 0x05 +#define HFSP_ALLOC_ID 0x06 +#define HFSP_STARTUP_ID 0x07 +#define HFSP_ATTRIB_ID 0x08 +#define HFSP_BOGUS_ID 0x0F +#define HFSP_FIRST_AV_ID 0x10 + +#define HFSJ_JOURN_IN_FS 0x00 +#define HFSJ_JOURN_OTHER_DEV 0x01 +#define HFSJ_JOURN_NEED_INIT 0x02 + +#define HFSJ_HEADER_MAGIC 0x4a4e4c78 +#define HFSJ_ENDIAN_MAGIC 0x12345678 + +#define HFSX_CASE_FOLDING 0xCF /* case insensitive HFSX */ +#define HFSX_BINARY_COMPARE 0xBC /* case sensitive HFSX */ + +#define HFS_EXT_NB 3 +#define HFSP_EXT_NB 8 + +/* Define the filenames used by the FS extractor */ +#ifdef HFS_EXTRACT_FS + +#define HFS_MDB_FILENAME "mdb.hfs" +#define HFS_CATALOG_FILENAME "catalog.hfs" +#define HFS_EXTENTS_FILENAME "extents.hfs" +#define HFS_BITMAP_FILENAME "bitmap.hfs" + +#define HFSP_VH_FILENAME "vh.hfsplus" +#define HFSP_CATALOG_FILENAME "catalog.hfsplus" +#define HFSP_EXTENTS_FILENAME "extents.hfsplus" +#define HFSP_BITMAP_FILENAME "bitmap.hfsplus" +#define HFSP_ATTRIB_FILENAME "attributes.hfsplus" +#define HFSP_STARTUP_FILENAME "startup.hfsplus" + +#endif /* HFS_EXTRACT_FS */ + +static PedFileSystemType hfs_type; +static PedFileSystemType hfsplus_type; + + + +/* ----------------------------------- */ +/* -- HFS DATA STRUCTURES -- */ +/* ----------------------------------- */ + +/* Extent descriptor */ +struct __attribute__ ((packed)) _HfsExtDescriptor { + uint16_t start_block; + uint16_t block_count; +}; +typedef struct _HfsExtDescriptor HfsExtDescriptor; +typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB]; + +/* Volume header */ +struct __attribute__ ((packed)) _HfsMasterDirectoryBlock { + uint16_t signature; + uint32_t create_date; + uint32_t modify_date; + uint16_t volume_attributes; + uint16_t files_in_root; + uint16_t volume_bitmap_block; /* in sectors */ + uint16_t next_allocation; + uint16_t total_blocks; + uint32_t block_size; /* in bytes */ + uint32_t def_clump_size; /* in bytes */ + uint16_t start_block; /* in sectors */ + uint32_t next_free_node; + uint16_t free_blocks; + uint8_t name_length; + char name[27]; + uint32_t backup_date; + uint16_t backup_number; + uint32_t write_count; + uint32_t extents_clump; + uint32_t catalog_clump; + uint16_t dirs_in_root; + uint32_t file_count; + uint32_t dir_count; + uint32_t finder_info[8]; + union __attribute__ ((packed)) { + struct __attribute__ ((packed)) { + uint16_t volume_cache_size; /* in blocks */ + uint16_t bitmap_cache_size; /* in blocks */ + uint16_t common_cache_size; /* in blocks */ + } legacy; + struct __attribute__ ((packed)) { + uint16_t signature; + HfsExtDescriptor location; + } embedded; + } old_new; + uint32_t extents_file_size; /* in bytes, block size multiple */ + HfsExtDataRec extents_file_rec; + uint32_t catalog_file_size; /* in bytes, block size multiple */ + HfsExtDataRec catalog_file_rec; +}; +typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock; + +/* B*-Tree Node Descriptor */ +struct __attribute__ ((packed)) _HfsNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsNodeDescriptor HfsNodeDescriptor; + +/* Header record of a whole B*-Tree */ +struct __attribute__ ((packed)) _HfsHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; + int8_t reserved[76]; +}; +typedef struct _HfsHeaderRecord HfsHeaderRecord; + +/* Catalog key for B*-Tree lookup in the catalog file */ +struct __attribute__ ((packed)) _HfsCatalogKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t reserved; + uint32_t parent_ID; + uint8_t name_length; + char name[31]; /* in fact physicaly 1 upto 31 */ +}; +typedef struct _HfsCatalogKey HfsCatalogKey; + +/* Extents overflow key for B*-Tree lookup */ +struct __attribute__ ((packed)) _HfsExtentKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t type; /* data or ressource fork */ + uint32_t file_ID; + uint16_t start; +}; +typedef struct _HfsExtentKey HfsExtentKey; + +/* Catalog subdata case directory */ +struct __attribute__ ((packed)) _HfsDir { + uint16_t flags; + uint16_t valence; /* number of files in this directory */ + uint32_t dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t reserved[4]; +}; +typedef struct _HfsDir HfsDir; + +/* Catalog subdata case file */ +struct __attribute__ ((packed)) _HfsFile { + int8_t flags; + int8_t type; /* should be 0 */ + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + uint32_t file_ID; + uint16_t data_start_block; + uint32_t data_sz_byte; + uint32_t data_sz_block; + uint16_t res_start_block; + uint32_t res_sz_byte; + uint32_t res_sz_block; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint16_t clump_size; + HfsExtDataRec extents_data; + HfsExtDataRec extents_res; + uint32_t reserved; +}; +typedef struct _HfsFile HfsFile; + +/* Catalog subdata case directory thread */ +struct __attribute__ ((packed)) _HfsDirTh { + uint32_t reserved[2]; + uint32_t parent_ID; + int8_t name_length; + char name[31]; +}; +typedef struct _HfsDirTh HfsDirTh; + +/* Catalog subdata case file thread */ +typedef struct _HfsDirTh HfsFileTh; /* same as directory thread */ + +/* Catalog data */ +struct __attribute__ ((packed)) _HfsCatalog { + int8_t type; + int8_t reserved; + union { + HfsDir dir; + HfsFile file; + HfsDirTh dir_th; + HfsFileTh file_th; + } sel; +}; +typedef struct _HfsCatalog HfsCatalog; + + + +/* ------------------------------------ */ +/* -- HFS+ DATA STRUCTURES -- */ +/* ------------------------------------ */ + +/* documented since 2004 in tn1150 */ +struct __attribute__ ((packed)) _HfsPPerms { + uint32_t owner_ID; + uint32_t group_ID; + uint32_t permissions; + uint32_t special_devices; +}; +typedef struct _HfsPPerms HfsPPerms; + +/* HFS+ extent descriptor*/ +struct __attribute__ ((packed)) _HfsPExtDescriptor { + uint32_t start_block; + uint32_t block_count; +}; +typedef struct _HfsPExtDescriptor HfsPExtDescriptor; +typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB]; + +/* HFS+ fork data structure */ +struct __attribute__ ((packed)) _HfsPForkData { + uint64_t logical_size; + uint32_t clump_size; + uint32_t total_blocks; + HfsPExtDataRec extents; +}; +typedef struct _HfsPForkData HfsPForkData; + +/* HFS+ catalog node ID */ +typedef uint32_t HfsPNodeID; + +/* HFS+ file names */ +typedef uint16_t unichar; +struct __attribute__ ((packed)) _HfsPUniStr255 { + uint16_t length; + unichar unicode[255]; /* 1 upto 255 */ +}; +typedef struct _HfsPUniStr255 HfsPUniStr255; + +/* HFS+ volume header */ +struct __attribute__ ((packed)) _HfsPVolumeHeader { + uint16_t signature; + uint16_t version; + uint32_t attributes; + uint32_t last_mounted_version; + uint32_t journal_info_block; + + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + uint32_t checked_date; + + uint32_t file_count; + uint32_t dir_count; + + uint32_t block_size; + uint32_t total_blocks; + uint32_t free_blocks; + + uint32_t next_allocation; + uint32_t res_clump_size; + uint32_t data_clump_size; + HfsPNodeID next_catalog_ID; + + uint32_t write_count; + uint64_t encodings_bitmap; + + uint8_t finder_info[32]; + + HfsPForkData allocation_file; + HfsPForkData extents_file; + HfsPForkData catalog_file; + HfsPForkData attributes_file; + HfsPForkData startup_file; +}; +typedef struct _HfsPVolumeHeader HfsPVolumeHeader; + +/* HFS+ B-Tree Node Descriptor */ /* same as HFS btree */ +struct __attribute__ ((packed)) _HfsPNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor; + +/* Header record of a whole HFS+ B-Tree */ +struct __attribute__ ((packed)) _HfsPHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; /* same as hfs btree until here */ + uint16_t reserved1; + + uint32_t clump_size; + uint8_t btree_type; /* must be 0 for HFS+ B-Tree */ + uint8_t key_compare_type; /* hfsx => 0xCF = case folding */ + /* 0xBC = binary compare */ + /* otherwise, reserved */ + uint32_t attributes; + uint32_t reserved3[16]; +}; +typedef struct _HfsPHeaderRecord HfsPHeaderRecord; + +/* Catalog key for B-Tree lookup in the HFS+ catalog file */ +struct __attribute__ ((packed)) _HfsPCatalogKey { + uint16_t key_length; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPCatalogKey HfsPCatalogKey; + +/* HFS+ catalog subdata case dir */ +struct __attribute__ ((packed)) _HfsPDir { + uint16_t flags; + uint32_t valence; + HfsPNodeID dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved; +}; +typedef struct _HfsPDir HfsPDir; + +/* HFS+ catalog subdata case file */ +struct __attribute__ ((packed)) _HfsPFile { + uint16_t flags; + uint32_t reserved1; + HfsPNodeID file_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved2; + + HfsPForkData data_fork; + HfsPForkData res_fork; +}; +typedef struct _HfsPFile HfsPFile; + +/* HFS+ catalog subdata case thread */ +struct __attribute__ ((packed)) _HfsPThread { + int16_t reserved; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPThread HfsPDirTh; +typedef struct _HfsPThread HfsPFileTh; + +/* HFS+ Catalog leaf data */ +struct __attribute__ ((packed)) _HfsPCatalog { + int16_t type; + union { + HfsPDir dir; + HfsPFile file; + HfsPDirTh dir_th; + HfsPFileTh file_th; + } sel; +}; +typedef struct _HfsPCatalog HfsPCatalog; + +/* HFS+ extents file key */ +struct __attribute__ ((packed)) _HfsPExtentKey { + uint16_t key_length; + uint8_t type; + uint8_t pad; + HfsPNodeID file_ID; + uint32_t start; +}; +typedef struct _HfsPExtentKey HfsPExtentKey; + +/* extent file data is HfsPExtDataRec */ + +/* Fork data attribute file */ +struct __attribute__ ((packed)) _HfsPForkDataAttr { + uint32_t record_type; + uint32_t reserved; + union __attribute__ ((packed)) { + HfsPForkData fork; + HfsPExtDataRec extents; + } fork_res; +}; +typedef struct _HfsPForkDataAttr HfsPForkDataAttr; + + +/* ----------- Journal data structures ----------- */ + +/* Info block : stored in a block # defined in the VH */ +struct __attribute__ ((packed)) _HfsJJournalInfoBlock { + uint32_t flags; + uint32_t device_signature[8]; + uint64_t offset; + uint64_t size; + uint32_t reserved[32]; +}; +typedef struct _HfsJJournalInfoBlock HfsJJournalInfoBlock; + +struct __attribute__ ((packed)) _HfsJJournalHeader { + uint32_t magic; + uint32_t endian; + uint64_t start; + uint64_t end; + uint64_t size; + uint32_t blhdr_size; + uint32_t checksum; + uint32_t jhdr_size; +}; +typedef struct _HfsJJournalHeader HfsJJournalHeader; + +struct __attribute__ ((packed)) _HfsJBlockInfo { + uint64_t bnum; /* sector number */ + uint32_t bsize; /* size in bytes */ + uint32_t next; +}; +typedef struct _HfsJBlockInfo HfsJBlockInfo; + +struct __attribute__ ((packed)) _HfsJBlockListHeader { + uint16_t max_blocks; /* reserved */ + uint16_t num_blocks; + uint32_t bytes_used; + uint32_t checksum; + uint32_t pad; + HfsJBlockInfo binfo[1]; +}; +typedef struct _HfsJBlockListHeader HfsJBlockListHeader; + + + +/* ---------------------------------------- */ +/* -- INTERNAL DATA STRUCTURES -- */ +/* ---------------------------------------- */ + +/* Data of an opened HFS file */ +struct _HfsPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + uint32_t CNID; /* disk order (BE) */ + HfsExtDataRec first; /* disk order (BE) */ + HfsExtDataRec cache; /* disk order (BE) */ + uint16_t start_cache; /* CPU order */ +}; +typedef struct _HfsPrivateFile HfsPrivateFile; + +/* To store bad block list */ +struct _HfsPrivateLinkExtent { + HfsExtDescriptor extent; + struct _HfsPrivateLinkExtent* next; +}; +typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent; + +/* HFS Filesystem specific data */ +struct _HfsPrivateFSData { + uint8_t alloc_map[(1<<16) / 8]; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFile* extent_file; + HfsPrivateFile* catalog_file; + HfsPrivateLinkExtent* bad_blocks_xtent_list; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; +}; +typedef struct _HfsPrivateFSData HfsPrivateFSData; + +/* Generic btree key */ +struct __attribute__ ((packed)) _HfsPrivateGenericKey { + uint8_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey; + +/* ----- HFS+ ----- */ + +/* Data of an opened HFS file */ +struct _HfsPPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + HfsPNodeID CNID; /* disk order (BE) */ + HfsPExtDataRec first; /* disk order (BE) */ + HfsPExtDataRec cache; /* disk order (BE) */ + uint32_t start_cache; /* CPU order */ +}; +typedef struct _HfsPPrivateFile HfsPPrivateFile; + +struct _HfsPPrivateExtent { + PedSector start_sector; + PedSector sector_count; +}; +typedef struct _HfsPPrivateExtent HfsPPrivateExtent; + +/* To store bad block list */ +struct _HfsPPrivateLinkExtent { + HfsPExtDescriptor extent; + struct _HfsPPrivateLinkExtent* next; +}; +typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent; + +/* HFS+ file system specific data */ +struct _HfsPPrivateFSData { + PedFileSystem* wrapper; /* NULL if hfs+ is not embedded */ + PedGeometry* plus_geom; /* Geometry of HFS+ _volume_ */ + uint8_t* alloc_map; + uint8_t* dirty_alloc_map; + HfsPVolumeHeader* vh; + HfsPPrivateFile* extents_file; + HfsPPrivateFile* catalog_file; + HfsPPrivateFile* attributes_file; + HfsPPrivateFile* allocation_file; + HfsPPrivateLinkExtent* bad_blocks_xtent_list; + uint32_t jib_start_block; + uint32_t jl_start_block; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; + char free_geom; /* 1 = plus_geom must be freed */ +}; +typedef struct _HfsPPrivateFSData HfsPPrivateFSData; + +/* Generic + btree key */ +struct __attribute__ ((packed)) _HfsPPrivateGenericKey { + uint16_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey; + +/* ---- common ---- */ + +/* node and lead record reference for a BTree search */ +struct _HfsCPrivateLeafRec { + unsigned int node_size; /* in sectors */ + unsigned int node_number; + unsigned int record_pos; + unsigned int record_number; +}; +typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec; + +extern uint8_t* hfs_block; +extern uint8_t* hfsp_block; +extern unsigned hfs_block_count; +extern unsigned hfsp_block_count; + +#endif /* _HFS_H */ diff --git a/libparted/fs/hfs/journal.c b/libparted/fs/hfs/journal.c new file mode 100644 index 0000000..883f306 --- /dev/null +++ b/libparted/fs/hfs/journal.c @@ -0,0 +1,388 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef DISCOVER_ONLY + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "reloc_plus.h" + +#include "journal.h" + +static int hfsj_vh_replayed = 0; + +static uint32_t +hfsj_calc_checksum(uint8_t *ptr, int len) +{ + int i; + uint32_t cksum=0; + + for (i=0; i < len; i++, ptr++) { + cksum = (cksum << 8) ^ (cksum + *ptr); + } + + return (~cksum); +} + +int +hfsj_update_jib(PedFileSystem* fs, uint32_t block) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + priv_data->vh->journal_info_block = PED_CPU_TO_BE32(block); + + if (!hfsplus_update_vh (fs)) + return 0; + + priv_data->jib_start_block = block; + return 1; +} + +int +hfsj_update_jl(PedFileSystem* fs, uint32_t block) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector sector; + uint64_t offset; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJJournalInfoBlock* jib; + int binsect; + + binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT; + sector = (PedSector) priv_data->jib_start_block * binsect; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jib = (HfsJJournalInfoBlock*) buf; + + offset = (uint64_t)block * PED_SECTOR_SIZE_DEFAULT * binsect; + jib->offset = PED_CPU_TO_BE64(offset); + + if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1) + || !ped_geometry_sync(priv_data->plus_geom)) + return 0; + + priv_data->jl_start_block = block; + return 1; +} + +/* Return the sector in the journal that is after the area read */ +/* or 0 on error */ +static PedSector +hfsj_journal_read(PedGeometry* geom, HfsJJournalHeader* jh, + PedSector journ_sect, PedSector journ_length, + PedSector read_sect, unsigned int nb_sect, + void* buf) +{ + int r; + + while (nb_sect--) { + r = ped_geometry_read(geom, buf, journ_sect + read_sect, 1); + if (!r) return 0; + + buf = ((uint8_t*)buf) + PED_SECTOR_SIZE_DEFAULT; + read_sect++; + if (read_sect == journ_length) + read_sect = 1; /* skip journal header + which is asserted to be + 1 sector long */ + } + + return read_sect; +} + +static int +hfsj_replay_transaction(PedFileSystem* fs, HfsJJournalHeader* jh, + PedSector jsector, PedSector jlength) +{ + PedSector start, sector; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJBlockListHeader* blhdr; + uint8_t* block; + unsigned int blhdr_nbsect; + int i, r; + uint32_t cksum, size; + + blhdr_nbsect = PED_BE32_TO_CPU(jh->blhdr_size) / PED_SECTOR_SIZE_DEFAULT; + blhdr = (HfsJBlockListHeader*) + ped_malloc (blhdr_nbsect * PED_SECTOR_SIZE_DEFAULT); + if (!blhdr) return 0; + + start = PED_BE64_TO_CPU(jh->start) / PED_SECTOR_SIZE_DEFAULT; + do { + start = hfsj_journal_read(priv_data->plus_geom, jh, jsector, + jlength, start, blhdr_nbsect, blhdr); + if (!start) goto err_replay; + + cksum = PED_BE32_TO_CPU(blhdr->checksum); + blhdr->checksum = 0; + if (cksum!=hfsj_calc_checksum((uint8_t*)blhdr, sizeof(*blhdr))){ + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad block list header checksum.")); + goto err_replay; + } + blhdr->checksum = PED_CPU_TO_BE32(cksum); + + for (i=1; i < PED_BE16_TO_CPU(blhdr->num_blocks); ++i) { + size = PED_BE32_TO_CPU(blhdr->binfo[i].bsize); + sector = PED_BE64_TO_CPU(blhdr->binfo[i].bnum); + if (!size) continue; + if (size % PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Invalid size of a transaction " + "block while replaying the journal " + "(%i bytes)."), + size); + goto err_replay; + } + block = (uint8_t*) ped_malloc(size); + if (!block) goto err_replay; + start = hfsj_journal_read(priv_data->plus_geom, jh, + jsector, jlength, start, + size / PED_SECTOR_SIZE_DEFAULT, + block); + if (!start) { + ped_free (block); + goto err_replay; + } + /* the sector stored in the journal seems to be + relative to the begin of the block device which + contains the hfs+ journaled volume */ + if (sector != ~0LL) + r = ped_geometry_write (fs->geom, block, sector, + size / PED_SECTOR_SIZE_DEFAULT); + else + r = 1; + ped_free (block); + /* check if wrapper mdb or vh with no wrapper has + changed */ + if ( (sector != ~0LL) + && (2 >= sector) + && (2 < sector + size / PED_SECTOR_SIZE_DEFAULT) ) + hfsj_vh_replayed = 1; + /* check if vh of embedded hfs+ has changed */ + if ( (sector != ~0LL) + && (priv_data->plus_geom != fs->geom) + && (sector + + fs->geom->start + - priv_data->plus_geom->start <= 2) + && (sector + + size / PED_SECTOR_SIZE_DEFAULT + + fs->geom->start + - priv_data->plus_geom->start > 2) ) + hfsj_vh_replayed = 1; + if (!r) goto err_replay; + } + } while (blhdr->binfo[0].next); + + jh->start = PED_CPU_TO_BE64(start * PED_SECTOR_SIZE_DEFAULT); + + ped_free (blhdr); + return (ped_geometry_sync (fs->geom)); + +err_replay: + ped_free (blhdr); + return 0; +} + +/* 0 => Failure, don't continue to open ! */ +/* 1 => Success, the journal has been completly replayed, or don't need to */ +int +hfsj_replay_journal(PedFileSystem* fs) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector sector, length; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJJournalInfoBlock* jib; + HfsJJournalHeader* jh; + int binsect; + uint32_t cksum; + + binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT; + priv_data->jib_start_block = + PED_BE32_TO_CPU(priv_data->vh->journal_info_block); + sector = (PedSector) priv_data->jib_start_block * binsect; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jib = (HfsJJournalInfoBlock*) buf; + + if ( (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS)) + && !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) { + priv_data->jl_start_block = PED_BE64_TO_CPU(jib->offset) + / ( PED_SECTOR_SIZE_DEFAULT * binsect ); + } + + if (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_NEED_INIT)) + return 1; + + if ( !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS)) + || (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Journal stored outside of the volume are " + "not supported. Try to desactivate the " + "journal and run Parted again.")); + return 0; + } + + if ( (PED_BE64_TO_CPU(jib->offset) % PED_SECTOR_SIZE_DEFAULT) + || (PED_BE64_TO_CPU(jib->size) % PED_SECTOR_SIZE_DEFAULT) ) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Journal offset or size is not multiple of " + "the sector size.")); + return 0; + } + + sector = PED_BE64_TO_CPU(jib->offset) / PED_SECTOR_SIZE_DEFAULT; + length = PED_BE64_TO_CPU(jib->size) / PED_SECTOR_SIZE_DEFAULT; + + jib = NULL; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jh = (HfsJJournalHeader*) buf; + + if ( (jh->magic != PED_BE32_TO_CPU(HFSJ_HEADER_MAGIC)) + || (jh->endian != PED_BE32_TO_CPU(HFSJ_ENDIAN_MAGIC)) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Incorrect magic values in the journal header.")); + return 0; + } + + if ( (PED_BE64_TO_CPU(jh->size)%PED_SECTOR_SIZE_DEFAULT) + || (PED_BE64_TO_CPU(jh->size)/PED_SECTOR_SIZE_DEFAULT + != (uint64_t)length) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Journal size mismatch between journal info block " + "and journal header.")); + return 0; + } + + if ( (PED_BE64_TO_CPU(jh->start) % PED_SECTOR_SIZE_DEFAULT) + || (PED_BE64_TO_CPU(jh->end) % PED_SECTOR_SIZE_DEFAULT) + || (PED_BE32_TO_CPU(jh->blhdr_size) % PED_SECTOR_SIZE_DEFAULT) + || (PED_BE32_TO_CPU(jh->jhdr_size) % PED_SECTOR_SIZE_DEFAULT) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Some header fields are not multiple of the sector " + "size.")); + return 0; + } + + if (PED_BE32_TO_CPU(jh->jhdr_size) != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The sector size stored in the journal is not 512 " + "bytes. Parted only supports 512 bytes length " + "sectors.")); + return 0; + } + + cksum = PED_BE32_TO_CPU(jh->checksum); + jh->checksum = 0; + if (cksum != hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad journal checksum.")); + return 0; + } + jh->checksum = PED_CPU_TO_BE32(cksum); + + /* The 2 following test are in the XNU Darwin source code */ + /* so I assume they're needed */ + if (jh->start == jh->size) + jh->start = PED_CPU_TO_BE64(PED_SECTOR_SIZE_DEFAULT); + if (jh->end == jh->size) + jh->start = PED_CPU_TO_BE64(PED_SECTOR_SIZE_DEFAULT); + + if (jh->start == jh->end) + return 1; + + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL, + _("The journal is not empty. Parted must replay the " + "transactions before opening the file system. This will " + "modify the file system.")) + != PED_EXCEPTION_FIX) + return 0; + + while (jh->start != jh->end) { + /* Replay one complete transaction */ + if (!hfsj_replay_transaction(fs, jh, sector, length)) + return 0; + + /* Recalculate cksum of the journal header */ + jh->checksum = 0; /* need to be 0 while calculating the cksum */ + cksum = hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh)); + jh->checksum = PED_CPU_TO_BE32(cksum); + + /* Update the Journal Header */ + if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1) + || !ped_geometry_sync(priv_data->plus_geom)) + return 0; + } + + if (hfsj_vh_replayed) { + /* probe could have reported incorrect info ! */ + /* is there a way to ask parted to quit ? */ + ped_exception_throw( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("The volume header or the master directory block has " + "changed while replaying the journal. You should " + "restart Parted.")); + return 0; + } + + return 1; +} + +#endif /* DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/journal.h b/libparted/fs/hfs/journal.h new file mode 100644 index 0000000..c0e2ead --- /dev/null +++ b/libparted/fs/hfs/journal.h @@ -0,0 +1,38 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _JOURNAL_H +#define _JOURNAL_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsj_replay_journal(PedFileSystem* fs); + +int +hfsj_update_jib(PedFileSystem* fs, uint32_t block); + +int +hfsj_update_jl(PedFileSystem* fs, uint32_t block); + +#endif /* _JOURNAL_H */ diff --git a/libparted/fs/hfs/probe.c b/libparted/fs/hfs/probe.c new file mode 100644 index 0000000..3fd9ef7 --- /dev/null +++ b/libparted/fs/hfs/probe.c @@ -0,0 +1,232 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" + +#include "probe.h" + +int +hfsc_can_use_geom (PedGeometry* geom) +{ + PedDevice* dev; + + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT ((dev = geom->dev) != NULL, return 0); + + if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Parted can't use HFS file systems on disks " + "with a sector size not equal to %d bytes."), + (int)PED_SECTOR_SIZE_DEFAULT ); + return 0; + } + + return 1; +} + +/* Probe an HFS volume, detecting it even if +it is in fact a wrapper to an HFS+ volume */ +/* Used by hfsplus_probe and hfs_probe */ +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + HfsMasterDirectoryBlock *mdb; + PedGeometry* geom_ret; + PedSector search, max; + + PED_ASSERT (geom != NULL, return NULL); + PED_ASSERT (hfsc_can_use_geom (geom), return NULL); + + mdb = (HfsMasterDirectoryBlock *) buf; + + /* is 5 an intelligent value ? */ + if ((geom->length < 5) + || (!ped_geometry_read (geom, buf, 2, 1)) + || (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) ) + return NULL; + + search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block) + + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks) + * (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT ))); + max = search + (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT); + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} + +PedGeometry* +hfsplus_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + + PED_ASSERT (geom != NULL, return NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom_ret = hfs_and_wrapper_probe(geom))) { + /* HFS+ is embedded in an HFS volume ? */ + HfsMasterDirectoryBlock *mdb; + mdb = (HfsMasterDirectoryBlock *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1) + || (mdb->old_new.embedded.signature + != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } else { + /* This is a standalone HFS+ volume ? */ + PedSector search, max; + HfsPVolumeHeader *vh; + vh = (HfsPVolumeHeader *) buf; + + if ((geom->length < 5) + || !ped_geometry_read (geom, buf, 2, 1) + || (vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) + return NULL; + + /* Correct range is indeed [ blocks*sz-2;(blocs+1)*sz-2 ( */ + /* But previous versions of my implementation used to */ + /* assume range is [(blocks-1)*sz-1;(blocks*sz) ( */ + /* (blocks-1)*sz-1 has to be scanned last, because */ + /* it can belong to a regular file */ + max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 2; + search = max - 2 * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ) + 2; + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (vh->signature == PED_CPU_TO_BE16 (HFSP_SIGNATURE)) + return geom_ret; + } + search = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) - 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 1; + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1) + || vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } +} + +PedGeometry* +hfs_probe (PedGeometry* geom) +{ + PedGeometry* geom_base; + PedGeometry* geom_plus = NULL; + + PED_ASSERT (geom != NULL, return NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom_base = hfs_and_wrapper_probe(geom)) + && (!(geom_plus = hfsplus_probe(geom_base)))) + return geom_base; + else { + if (geom_base) ped_geometry_destroy (geom_base); + if (geom_plus) ped_geometry_destroy (geom_plus); + return NULL; + } +} + +PedGeometry* +hfsx_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector search, max; + HfsPVolumeHeader *vh = (HfsPVolumeHeader *) buf; + + PED_ASSERT (geom != NULL, return NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom->length < 5) + || !ped_geometry_read (geom, buf, 2, 1) + || (vh->signature != PED_CPU_TO_BE16 (HFSX_SIGNATURE))) + return NULL; + + /* unlike the hfs+ code, which should be kept compatible + with my old previous implementations, we only care here + about legal alternate VH positions, like TN1150 describes them */ + max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 2; + search = max - ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ); + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (vh->signature == PED_CPU_TO_BE16 (HFSX_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} diff --git a/libparted/fs/hfs/probe.h b/libparted/fs/hfs/probe.h new file mode 100644 index 0000000..3001006 --- /dev/null +++ b/libparted/fs/hfs/probe.h @@ -0,0 +1,44 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _PROBE_H +#define _PROBE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsc_can_use_geom (PedGeometry* geom); + +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom); + +PedGeometry* +hfsplus_probe (PedGeometry* geom); + +PedGeometry* +hfs_probe (PedGeometry* geom); + +PedGeometry* +hfsx_probe (PedGeometry* geom); + +#endif /* _PROBE_H */ diff --git a/libparted/fs/hfs/reloc.c b/libparted/fs/hfs/reloc.c new file mode 100644 index 0000000..29c2f32 --- /dev/null +++ b/libparted/fs/hfs/reloc.c @@ -0,0 +1,673 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef DISCOVER_ONLY + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file.h" +#include "advfs.h" +#include "cache.h" + +#include "reloc.h" + +/* This function moves data of size blocks starting + at block *ptr_fblock to block *ptr_to_fblock */ +/* return new start or -1 on failure */ +static int +hfs_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, unsigned int size) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int i, ok = 0; + unsigned int next_to_fblock; + unsigned int start, stop; + + PED_ASSERT (hfs_block != NULL, return -1); + PED_ASSERT (*ptr_to_fblock <= *ptr_fblock, return -1); + /* quiet gcc */ + next_to_fblock = start = stop = 0; + +/* + Try to fit the extent AT or _BEFORE_ the wanted place, + or then in the gap between dest and source. + If failed try to fit the extent after source, for 2 pass relocation + The extent is always copied in a non overlapping way +*/ + + /* Backward search */ + /* 1 pass relocation AT or BEFORE *ptr_to_fblock */ + if (*ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_fblock < *ptr_to_fblock+size ? + *ptr_fblock : *ptr_to_fblock+size; + while (start && stop-start != size) { + --start; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start)) + stop = start; + } + ok = (stop-start == size); + } + + /* Forward search */ + /* 1 pass relocation in the gap merged with 2 pass reloc after source */ + if (!ok && *ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_to_fblock+1; + while (stop < PED_BE16_TO_CPU(priv_data->mdb->total_blocks) + && stop-start != size) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop)) + start = stop + 1; + ++stop; + } + ok = (stop-start == size); + } + + /* new non overlapping room has been found ? */ + if (ok) { + /* enough room */ + unsigned int j; + unsigned int start_block = + PED_BE16_TO_CPU (priv_data->mdb->start_block ); + unsigned int block_sz = + (PED_BE32_TO_CPU (priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT); + + if (stop > *ptr_to_fblock && stop <= *ptr_fblock) + /* Fit in the gap */ + next_to_fblock = stop; + else + /* Before or after the gap */ + next_to_fblock = *ptr_to_fblock; + + /* move blocks */ + for (i = 0; i < size; /*i+=j*/) { + PedSector abs_sector; + unsigned int ai; + + j = size - i; j = (j < hfs_block_count) ? + j : hfs_block_count ; + + abs_sector = start_block + + (PedSector) (*ptr_fblock + i) * block_sz; + if (!ped_geometry_read (fs->geom, hfs_block, abs_sector, + block_sz * j)) + return -1; + + abs_sector = start_block + + (PedSector) (start + i) * block_sz; + if (!ped_geometry_write (fs->geom,hfs_block,abs_sector, + block_sz * j)) + return -1; + + for (ai = i+j; i < ai; i++) { + /* free source block */ + CLR_BLOC_OCCUPATION(priv_data->alloc_map, + *ptr_fblock + i); + + /* set dest block */ + SET_BLOC_OCCUPATION(priv_data->alloc_map, + start + i); + } + } + if (!ped_geometry_sync_fast (fs->geom)) + return -1; + + *ptr_fblock += size; + *ptr_to_fblock = next_to_fblock; + } else { + if (*ptr_fblock != *ptr_to_fblock) + /* not enough room, but try to continue */ + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("An extent has not been relocated.")); + start = *ptr_fblock; + *ptr_fblock = *ptr_to_fblock = start + size; + } + + return start; +} + +/* Update MDB */ +/* Return 0 if an error occurred */ +/* Return 1 if everything ok */ +int +hfs_update_mdb (PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + + if (!ped_geometry_read (fs->geom, node, 2, 1)) + return 0; + memcpy (node, priv_data->mdb, sizeof (HfsMasterDirectoryBlock)); + if ( !ped_geometry_write (fs->geom, node, 2, 1) + || !ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1) + || !ped_geometry_sync_fast (fs->geom)) + return 0; + return 1; +} + +/* Generic relocator */ +/* replace previous hfs_do_move_* */ +static int +hfs_do_move (PedFileSystem* fs, unsigned int *ptr_src, + unsigned int *ptr_dest, HfsCPrivateCache* cache, + HfsCPrivateExtent* ref) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsPrivateFile* file; + HfsExtDescriptor* extent; + HfsCPrivateExtent* move; + int new_start; + + new_start = hfs_effect_move_extent (fs, ptr_src, ptr_dest, + ref->ext_length); + if (new_start == -1) return -1; + + if (ref->ext_start != (unsigned) new_start) { + /* Load, modify & save */ + switch (ref->where) { + /******** MDB *********/ + case CR_PRIM_CAT : + priv_data->catalog_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + goto CR_PRIM; + case CR_PRIM_EXT : + priv_data->extent_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + CR_PRIM : + extent = ( HfsExtDescriptor* ) + ( (uint8_t*)priv_data->mdb + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + if (!hfs_update_mdb(fs)) return -1; + break; + + /********* BTREE *******/ + case CR_BTREE_EXT_CAT : + if (priv_data->catalog_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE16(ref->ext_start)) + priv_data->catalog_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + case CR_BTREE_EXT_0 : + file = priv_data->extent_file; + goto CR_BTREE; + case CR_BTREE_CAT : + file = priv_data->catalog_file; + CR_BTREE: + PED_ASSERT(ref->sect_by_block == 1 + && ref->ref_offset < PED_SECTOR_SIZE_DEFAULT, + return -1); + if (!hfs_file_read_sector(file, node, ref->ref_block)) + return -1; + extent = ( HfsExtDescriptor* ) (node + ref->ref_offset); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + if (!hfs_file_write_sector(file, node, ref->ref_block) + || !ped_geometry_sync_fast (fs->geom)) + return -1; + break; + + /********** BUG ********/ + default : + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("A reference to an extent comes from a place " + "it should not. You should check the file " + "system!")); + return -1; + break; + } + + /* Update the cache */ + move = hfsc_cache_move_extent(cache, ref->ext_start, new_start); + if (!move) return -1; /* "cleanly" fail */ + PED_ASSERT(move == ref, return -1); /* generate a bug */ + } + + return new_start; +} + +/* 0 error, 1 ok */ +static int +hfs_save_allocation(PedFileSystem* fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int map_sectors; + + map_sectors = ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + return ( ped_geometry_write (fs->geom, priv_data->alloc_map, + PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block), + map_sectors) ); +} + +/* This function moves an extent starting at block fblock to block to_fblock + if there's enough room */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occurred */ +/* Return 0 if no extent was found */ +/* Generic search thanks to the file system cache */ +static int +hfs_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, + HfsCPrivateCache* cache) +{ + HfsCPrivateExtent* ref; + unsigned int old_start, new_start; + + /* Reference search powered by the cache... */ + /* This is the optimisation secret :) */ + ref = hfsc_cache_search_extent(cache, *ptr_fblock); + if (!ref) return 0; /* not found */ + + old_start = *ptr_fblock; + new_start = hfs_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref); + if (new_start == (unsigned int) -1) return -1; + if (new_start > old_start) { /* detect 2 pass reloc */ + new_start = hfs_do_move(fs,&new_start,ptr_to_fblock,cache,ref); + if (new_start == (unsigned int) -1 || new_start > old_start) + return -1; + } + + /* allocation bitmap save is not atomic with data relocation */ + /* so we only do it a few times, and without syncing */ + /* The unmounted bit protect us anyway */ + hfs_save_allocation(fs); + return 1; +} + +static int +hfs_cache_from_mdb(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsExtDescriptor* extent; + unsigned int j; + + extent = priv_data->mdb->extents_file_rec; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + 0, /* unused for mdb */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb), + 1, /* load/save only 1 sector */ + CR_PRIM_EXT, + j ) + ) + return 0; + } + + extent = priv_data->mdb->catalog_file_rec; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + 0, + ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb), + 1, + CR_PRIM_CAT, + j ) + ) + return 0; + } + + return 1; +} + +static int +hfs_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsCatalogKey* catalog_key; + HfsCatalog* catalog_data; + HfsExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j; + + if (!priv_data->catalog_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS volume has no catalog file. " + "This is very unusual!")); + return 1; + } + + if (!hfs_file_read_sector (priv_data->catalog_file, node, 0)) + return 0; + header = (HfsHeaderRecord*)(node + PED_BE16_TO_CPU(*((uint16_t*) + (node+(PED_SECTOR_SIZE_DEFAULT-2))))); + + for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + leaf_node; + leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfs_file_read_sector (priv_data->catalog_file, + node, leaf_node)) + return 0; + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; ++i) { + /* undocumented alignement */ + unsigned int skip; + catalog_key = (HfsCatalogKey*) (node + PED_BE16_TO_CPU( + *((uint16_t*)(node+(PED_SECTOR_SIZE_DEFAULT - 2*i))))); + skip = (1 + catalog_key->key_length + 1) & ~1; + catalog_data = (HfsCatalog*)( ((uint8_t*)catalog_key) + + skip ); + /* check for obvious error in FS */ + if (((uint8_t*)catalog_key - node < HFS_FIRST_REC) + || ((uint8_t*)catalog_data - node + >= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + + if (catalog_data->type != HFS_CAT_FILE) continue; + + extent = catalog_data->sel.file.extents_data; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + CR_BTREE_CAT, + j ) + ) + return 0; + } + + extent = catalog_data->sel.file.extents_res; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + CR_BTREE_CAT, + j ) + ) + return 0; + } + } + } + + return 1; +} + +static int +hfs_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsExtentKey* extent_key; + HfsExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j; + + if (!priv_data->extent_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS volume has no extents overflow " + "file. This is quite unusual!")); + return 1; + } + + if (!hfs_file_read_sector (priv_data->extent_file, node, 0)) + return 0; + header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE_DEFAULT-2)))))); + + for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + leaf_node; + leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfs_file_read_sector (priv_data->extent_file, node, + leaf_node)) + return 0; + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + uint8_t where; + extent_key = (HfsExtentKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE_DEFAULT - 2*i))))); + /* size is cst */ + extent = (HfsExtDescriptor*)(((uint8_t*)extent_key) + + sizeof (HfsExtentKey)); + /* check for obvious error in FS */ + if (((uint8_t*)extent_key - node < HFS_FIRST_REC) + || ((uint8_t*)extent - node + >= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + + switch (extent_key->file_ID) { + case PED_CPU_TO_BE32 (HFS_XTENT_ID) : + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The extents overflow file should not" + " contain its own extents! You " + "should check the file system.")) + != PED_EXCEPTION_IGNORE) + return 0; + where = CR_BTREE_EXT_EXT; + break; + case PED_CPU_TO_BE32 (HFS_CATALOG_ID) : + where = CR_BTREE_EXT_CAT; + break; + default : + where = CR_BTREE_EXT_0; + break; + } + + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + where, + j ) + ) + return 0; + } + } + } + + return 1; +} + +/* This function cache every extents start and length stored in any + fs structure into the adt defined in cache.[ch] + Returns NULL on failure */ +static HfsCPrivateCache* +hfs_cache_extents(PedFileSystem *fs, PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsCPrivateCache* ret; + unsigned int file_number, block_number; + + file_number = PED_BE32_TO_CPU(priv_data->mdb->file_count); + block_number = PED_BE16_TO_CPU(priv_data->mdb->total_blocks); + ret = hfsc_new_cache(block_number, file_number); + if (!ret) return NULL; + + if (!hfs_cache_from_mdb(ret, fs, timer) || + !hfs_cache_from_catalog(ret, fs, timer) || + !hfs_cache_from_extent(ret, fs, timer)) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not cache the file system in memory.")); + hfsc_delete_cache(ret); + return NULL; + } + + return ret; +} + +/* This function moves file's data to compact used and free space, + starting at fblock block */ +/* return 0 on error */ +int +hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free) +{ + PedSector bytes_buff; + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + HfsCPrivateCache* cache; + unsigned int to_fblock = fblock; + unsigned int start = fblock; + unsigned int div = PED_BE16_TO_CPU (mdb->total_blocks) + + 1 - start - to_free; + int ret; + + PED_ASSERT (!hfs_block, return 0); + + cache = hfs_cache_extents (fs, timer); + if (!cache) + return 0; + + /* Calculate the size of the copy buffer : + * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF + * takes the maximum number of HFS blocks so that the buffer + * will remain smaller than or equal to BYTES_MAX_BUFF, with + * a minimum of 1 HFS block */ + bytes_buff = PED_BE32_TO_CPU (priv_data->mdb->block_size) + * (PedSector) BLOCK_MAX_BUFF; + if (bytes_buff > BYTES_MAX_BUFF) { + hfs_block_count = BYTES_MAX_BUFF + / PED_BE32_TO_CPU (priv_data->mdb->block_size); + if (!hfs_block_count) + hfs_block_count = 1; + bytes_buff = (PedSector) hfs_block_count + * PED_BE32_TO_CPU (priv_data->mdb->block_size); + } else + hfs_block_count = BLOCK_MAX_BUFF; + + /* If the cache code requests more space, give it to him */ + if (bytes_buff < hfsc_cache_needed_buffer (cache)) + bytes_buff = hfsc_cache_needed_buffer (cache); + + hfs_block = (uint8_t*) ped_malloc (bytes_buff); + if (!hfs_block) + goto error_cache; + + if (!hfs_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks list could not be loaded.")); + goto error_alloc; + } + + while (fblock < PED_BE16_TO_CPU (mdb->total_blocks)) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,fblock) + && (!hfs_is_bad_block (fs, fblock))) { + if (!(ret = hfs_move_extent_starting_at (fs, &fblock, + &to_fblock, cache))) + to_fblock = ++fblock; + else if (ret == -1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred during extent " + "relocation.")); + goto error_alloc; + } + } else { + fblock++; + } + + ped_timer_update(timer, (float)(to_fblock - start)/div); + } + + ped_free (hfs_block); hfs_block = NULL; hfs_block_count = 0; + hfsc_delete_cache (cache); + return 1; + +error_alloc: + ped_free (hfs_block); hfs_block = NULL; hfs_block_count = 0; +error_cache: + hfsc_delete_cache (cache); + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/reloc.h b/libparted/fs/hfs/reloc.h new file mode 100644 index 0000000..627dd0c --- /dev/null +++ b/libparted/fs/hfs/reloc.h @@ -0,0 +1,36 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _RELOC_H +#define _RELOC_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfs_update_mdb (PedFileSystem *fs); + +int +hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free); + +#endif /* _RELOC_H */ diff --git a/libparted/fs/hfs/reloc_plus.c b/libparted/fs/hfs/reloc_plus.c new file mode 100644 index 0000000..cb618bd --- /dev/null +++ b/libparted/fs/hfs/reloc_plus.c @@ -0,0 +1,948 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef DISCOVER_ONLY + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file_plus.h" +#include "advfs_plus.h" +#include "cache.h" +#include "journal.h" + +#include "reloc_plus.h" + +/* This function moves data of size blocks starting at block *ptr_fblock + to block *ptr_to_fblock */ +/* return new start or -1 on failure */ +/* -1 is ok because there can only be 2^32-1 blocks, so the max possible + last one is 2^32-2 (and anyway it contains Alternate VH), so + -1 (== 2^32-1[2^32]) never represent a valid block */ +static int +hfsplus_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, unsigned int size) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int i, ok = 0; + unsigned int next_to_fblock; + unsigned int start, stop; + + PED_ASSERT (hfsp_block != NULL, return -1); + PED_ASSERT (*ptr_to_fblock <= *ptr_fblock, return -1); + /* quiet GCC */ + next_to_fblock = start = stop = 0; + +/* + Try to fit the extent AT or _BEFORE_ the wanted place, + or then in the gap between dest and source. + If failed try to fit the extent after source, for 2 pass relocation + The extent is always copied in a non overlapping way +*/ + + /* Backward search */ + /* 1 pass relocation AT or BEFORE *ptr_to_fblock */ + if (*ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_fblock < *ptr_to_fblock+size ? + *ptr_fblock : *ptr_to_fblock+size; + while (start && stop-start != size) { + --start; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start)) + stop = start; + } + ok = (stop-start == size); + } + + /* Forward search */ + /* 1 pass relocation in the gap merged with 2 pass reloc after source */ + if (!ok && *ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_to_fblock+1; + while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks) + && stop-start != size) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop)) + start = stop + 1; + ++stop; + } + ok = (stop-start == size); + } + + /* new non overlapping room has been found ? */ + if (ok) { + /* enough room */ + PedSector abs_sector; + unsigned int ai, j, block; + unsigned int block_sz = (PED_BE32_TO_CPU ( + priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT); + + if (stop > *ptr_to_fblock && stop <= *ptr_fblock) + /* Fit in the gap */ + next_to_fblock = stop; + else + /* Before or after the gap */ + next_to_fblock = *ptr_to_fblock; + + /* move blocks */ + for (i = 0; i < size; /*i++*/) { + j = size - i; j = (j < hfsp_block_count) ? + j : hfsp_block_count ; + + abs_sector = (PedSector) (*ptr_fblock + i) * block_sz; + if (!ped_geometry_read (priv_data->plus_geom, + hfsp_block, abs_sector, + block_sz * j)) + return -1; + + abs_sector = (PedSector) (start + i) * block_sz; + if (!ped_geometry_write (priv_data->plus_geom, + hfsp_block, abs_sector, + block_sz * j)) + return -1; + + for (ai = i+j; i < ai; i++) { + /* free source block */ + block = *ptr_fblock + i; + CLR_BLOC_OCCUPATION(priv_data->alloc_map,block); + SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, + block/(PED_SECTOR_SIZE_DEFAULT*8)); + + /* set dest block */ + block = start + i; + SET_BLOC_OCCUPATION(priv_data->alloc_map,block); + SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, + block/(PED_SECTOR_SIZE_DEFAULT*8)); + } + } + if (!ped_geometry_sync_fast (priv_data->plus_geom)) + return -1; + + *ptr_fblock += size; + *ptr_to_fblock = next_to_fblock; + } else { + if (*ptr_fblock != *ptr_to_fblock) + /* not enough room */ + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("An extent has not been relocated.")); + start = *ptr_fblock; + *ptr_fblock = *ptr_to_fblock = start + size; + } + + return start; +} + +/* Returns 0 on error */ +/* 1 on succes */ +int +hfsplus_update_vh (PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + + if (!ped_geometry_read (priv_data->plus_geom, node, 2, 1)) + return 0; + memcpy (node, priv_data->vh, sizeof (HfsPVolumeHeader)); + if (!ped_geometry_write (priv_data->plus_geom, node, 2, 1) + || !ped_geometry_write (priv_data->plus_geom, node, + priv_data->plus_geom->length - 2, 1) + || !ped_geometry_sync_fast (priv_data->plus_geom)) + return 0; + return 1; +} + +static int +hfsplus_do_move (PedFileSystem* fs, unsigned int *ptr_src, + unsigned int *ptr_dest, HfsCPrivateCache* cache, + HfsCPrivateExtent* ref) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPPrivateFile* file; + HfsPExtDescriptor* extent; + HfsCPrivateExtent* move; + int new_start; + + new_start = hfsplus_effect_move_extent (fs, ptr_src, ptr_dest, + ref->ext_length); + + if (new_start == -1) return -1; + + if (ref->ext_start != (unsigned) new_start) { + switch (ref->where) { + /************ VH ************/ + case CR_PRIM_CAT : + priv_data->catalog_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_EXT : + priv_data->extents_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_ATTR : + priv_data->attributes_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_ALLOC : + priv_data->allocation_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_START : + /* No startup file opened */ + CR_PRIM : + extent = ( HfsPExtDescriptor* ) + ( (uint8_t*)priv_data->vh + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + if (!hfsplus_update_vh(fs)) + return -1; + break; + + /************** BTREE *************/ + case CR_BTREE_CAT_JIB : + if (!hfsj_update_jib(fs, new_start)) + return -1; + goto BTREE_CAT; + + case CR_BTREE_CAT_JL : + if (!hfsj_update_jl(fs, new_start)) + return -1; + goto BTREE_CAT; + + BTREE_CAT: + case CR_BTREE_CAT : + file = priv_data->catalog_file; + goto CR_BTREE; + + case CR_BTREE_ATTR : + file = priv_data->attributes_file; + goto CR_BTREE; + + case CR_BTREE_EXT_ATTR : + if (priv_data->attributes_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->attributes_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_CAT : + if (priv_data->catalog_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->catalog_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_ALLOC : + if (priv_data->allocation_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->allocation_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_START : + /* No startup file opened */ + CR_BTREE_EXT : + case CR_BTREE_EXT_0 : + file = priv_data->extents_file; + + CR_BTREE : + PED_ASSERT(PED_SECTOR_SIZE_DEFAULT * ref->sect_by_block + > ref->ref_offset, return -1 ); + if (!hfsplus_file_read(file, hfsp_block, + (PedSector)ref->ref_block * ref->sect_by_block, + ref->sect_by_block)) + return -1; + extent = ( HfsPExtDescriptor* ) + ( hfsp_block + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + if (!hfsplus_file_write(file, hfsp_block, + (PedSector)ref->ref_block * ref->sect_by_block, + ref->sect_by_block) + || !ped_geometry_sync_fast (priv_data->plus_geom)) + return -1; + break; + + /********** BUG *********/ + default : + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("A reference to an extent comes from a place " + "it should not. You should check the file " + "system!")); + return -1; + break; + } + + move = hfsc_cache_move_extent(cache, ref->ext_start, new_start); + if (!move) return -1; + PED_ASSERT(move == ref, return -1); + } + + return new_start; +} + +/* save any dirty sector of the allocation bitmap file */ +static int +hfsplus_save_allocation(PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int map_sectors, i, j; + int ret = 1; + + map_sectors = ( PED_BE32_TO_CPU (priv_data->vh->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8); + + for (i = 0; i < map_sectors;) { + for (j = i; + (TST_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j)); + ++j) + CLR_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j); + if (j-i) { + ret = hfsplus_file_write(priv_data->allocation_file, + priv_data->alloc_map + i * PED_SECTOR_SIZE_DEFAULT, + i, j-i) && ret; + i = j; + } else + ++i; + } + + return ret; +} + +/* This function moves an extent starting at block fblock + to block to_fblock if there's enough room */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occurred */ +/* Return 0 if no extent was found */ +static int +hfsplus_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, + HfsCPrivateCache* cache) +{ + HfsCPrivateExtent* ref; + unsigned int old_start, new_start; + + ref = hfsc_cache_search_extent(cache, *ptr_fblock); + if (!ref) return 0; + + old_start = *ptr_fblock; + new_start = hfsplus_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref); + if (new_start == (unsigned)-1) return -1; + if (new_start > old_start) { + new_start = hfsplus_do_move(fs, &new_start, ptr_to_fblock, + cache, ref); + if (new_start == (unsigned)-1 || new_start > old_start) + return -1; + } + + hfsplus_save_allocation(fs); + return 1; +} + +static int +hfsplus_cache_from_vh(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPExtDescriptor* extent; + unsigned int j; + + extent = priv_data->vh->allocation_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_ALLOC, + j ) + ) + return 0; + } + + extent = priv_data->vh->extents_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_EXT, + j ) + ) + return 0; + } + + extent = priv_data->vh->catalog_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_CAT, + j ) + ) + return 0; + } + + extent = priv_data->vh->attributes_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_ATTR, + j ) + ) + return 0; + } + + extent = priv_data->vh->startup_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_START, + j ) + ) + return 0; + } + + return 1; +} + +static int +hfsplus_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1; + HfsPCatalogKey* catalog_key; + HfsPCatalog* catalog_data; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + uint32_t jib = priv_data->jib_start_block, + jl = priv_data->jl_start_block; + + if (!priv_data->catalog_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS+ volume has no catalog file. " + "This is very unusual!")); + return 1; + } + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfsplus_file_read_sector (priv_data->catalog_file, node_1, 0)) + return 0; + header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256, return 0); + + node = (uint8_t*) ped_malloc(bsize); + if (!node) return 0; + desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->catalog_file, node, + (PedSector) leaf_node * size, size)) { + ped_free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + unsigned int skip; + uint8_t where; + + catalog_key = (HfsPCatalogKey*) + ( node + PED_BE16_TO_CPU (*((uint16_t *) + (node+(bsize - 2*i)))) ); + skip = ( 2 + PED_BE16_TO_CPU (catalog_key->key_length) + + 1) & ~1; + catalog_data = (HfsPCatalog*) + (((uint8_t*)catalog_key) + skip); + /* check for obvious error in FS */ + if (((uint8_t*)catalog_key - node < HFS_FIRST_REC) + || ((uint8_t*)catalog_data - node + >= (signed) bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + ped_free (node); + return 0; + } + + if (PED_BE16_TO_CPU(catalog_data->type)!=HFS_CAT_FILE) + continue; + + extent = catalog_data->sel.file.data_fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + where = CR_BTREE_CAT; + if ( PED_BE32_TO_CPU(extent[j].start_block) + == jib ) { + jib = 0; + where = CR_BTREE_CAT_JIB; + } else + if ( PED_BE32_TO_CPU(extent[j].start_block) + == jl ) { + jl = 0; + where = CR_BTREE_CAT_JL; + } + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + where, + j ) + ) { + ped_free (node); + return 0; + } + } + + extent = catalog_data->sel.file.res_fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + CR_BTREE_CAT, + j ) + ) { + ped_free (node); + return 0; + } + } + } + } + + ped_free (node); + return 1; +} + +static int +hfsplus_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1; + HfsPExtentKey* extent_key; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + + if (!priv_data->extents_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS+ volume has no extents overflow " + "file. This is quite unusual!")); + return 1; + } + + if (!hfsplus_file_read_sector (priv_data->extents_file, node_1, 0)) + return 0; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256, return 0); + + node = (uint8_t*) ped_malloc (bsize); + if (!node) return -1; + desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->extents_file, node, + (PedSector) leaf_node * size, size)) { + ped_free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + uint8_t where; + extent_key = (HfsPExtentKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + extent = (HfsPExtDescriptor*) + (((uint8_t*)extent_key) + sizeof (HfsPExtentKey)); + /* check for obvious error in FS */ + if (((uint8_t*)extent_key - node < HFS_FIRST_REC) + || ((uint8_t*)extent - node + >= (signed)bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + ped_free (node); + return -1; + } + + switch (extent_key->file_ID) { + case PED_CPU_TO_BE32 (HFS_XTENT_ID) : + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The extents overflow file should not" + " contain its own extents! You should " + "check the file system.")) + != PED_EXCEPTION_IGNORE) + return 0; + where = CR_BTREE_EXT_EXT; + break; + case PED_CPU_TO_BE32 (HFS_CATALOG_ID) : + where = CR_BTREE_EXT_CAT; + break; + case PED_CPU_TO_BE32 (HFSP_ALLOC_ID) : + where = CR_BTREE_EXT_ALLOC; + break; + case PED_CPU_TO_BE32 (HFSP_STARTUP_ID) : + where = CR_BTREE_EXT_START; + break; + case PED_CPU_TO_BE32 (HFSP_ATTRIB_ID) : + where = CR_BTREE_EXT_ATTR; + break; + default : + where = CR_BTREE_EXT_0; + break; + } + + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + where, + j ) + ) { + ped_free (node); + return 0; + } + } + } + } + + ped_free (node); + return 1; +} + +static int +hfsplus_cache_from_attributes(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1; + HfsPPrivateGenericKey* generic_key; + HfsPForkDataAttr* fork_ext_data; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + + /* attributes file is facultative */ + if (!priv_data->attributes_file->sect_nb) + return 1; + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfsplus_file_read_sector (priv_data->attributes_file, node_1, 0)) + return 0; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256, return 0); + + node = (uint8_t*) ped_malloc(bsize); + if (!node) return 0; + desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->attributes_file, node, + (PedSector) leaf_node * size, size)) { + ped_free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + unsigned int skip; + generic_key = (HfsPPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + skip = ( 2 + PED_BE16_TO_CPU (generic_key->key_length) + + 1 ) & ~1; + fork_ext_data = (HfsPForkDataAttr*) + (((uint8_t*)generic_key) + skip); + /* check for obvious error in FS */ + if (((uint8_t*)generic_key - node < HFS_FIRST_REC) + || ((uint8_t*)fork_ext_data - node + >= (signed) bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + ped_free (node); + return 0; + } + + if (fork_ext_data->record_type + == PED_CPU_TO_BE32 ( HFSP_ATTR_FORK ) ) { + extent = fork_ext_data->fork_res.fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU ( + extent[j].start_block ), + PED_BE32_TO_CPU ( + extent[j].block_count ), + leaf_node, + (uint8_t*)extent-node, + size, + CR_BTREE_ATTR, + j ) + ) { + ped_free(node); + return 0; + } + } + } else if (fork_ext_data->record_type + == PED_CPU_TO_BE32 ( HFSP_ATTR_EXTENTS ) ) { + extent = fork_ext_data->fork_res.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU ( + extent[j].start_block ), + PED_BE32_TO_CPU ( + extent[j].block_count ), + leaf_node, + (uint8_t*)extent-node, + size, + CR_BTREE_ATTR, + j ) + ) { + ped_free(node); + return 0; + } + } + } else continue; + } + } + + ped_free (node); + return 1; +} + +static HfsCPrivateCache* +hfsplus_cache_extents(PedFileSystem* fs, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsCPrivateCache* ret; + unsigned int file_number, block_number; + + file_number = PED_BE32_TO_CPU(priv_data->vh->file_count); + block_number = PED_BE32_TO_CPU(priv_data->vh->total_blocks); + ret = hfsc_new_cache(block_number, file_number); + if (!ret) return NULL; + + if (!hfsplus_cache_from_vh(ret, fs, timer) || + !hfsplus_cache_from_catalog(ret, fs, timer) || + !hfsplus_cache_from_extent(ret, fs, timer) || + !hfsplus_cache_from_attributes(ret, fs, timer)) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not cache the file system in memory.")); + hfsc_delete_cache(ret); + return NULL; + } + + return ret; +} + +/* This function moves file's data to compact used and free space, + starting at fblock block */ +/* return 0 on error */ +int +hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free) +{ + PedSector bytes_buff; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + HfsCPrivateCache* cache; + unsigned int to_fblock = fblock; + unsigned int start = fblock; + unsigned int div = PED_BE32_TO_CPU (vh->total_blocks) + + 1 - start - to_free; + int ret; + + PED_ASSERT (!hfsp_block, return 0); + + cache = hfsplus_cache_extents (fs, timer); + if (!cache) + return 0; + + /* Calculate the size of the copy buffer : + * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF + * takes the maximum number of HFS blocks so that the buffer + * will remain smaller than or equal to BYTES_MAX_BUFF, with + * a minimum of 1 HFS block */ + bytes_buff = PED_BE32_TO_CPU (priv_data->vh->block_size) + * (PedSector) BLOCK_MAX_BUFF; + if (bytes_buff > BYTES_MAX_BUFF) { + hfsp_block_count = BYTES_MAX_BUFF + / PED_BE32_TO_CPU (priv_data->vh->block_size); + if (!hfsp_block_count) + hfsp_block_count = 1; + bytes_buff = (PedSector) hfsp_block_count + * PED_BE32_TO_CPU (priv_data->vh->block_size); + } else + hfsp_block_count = BLOCK_MAX_BUFF; + + /* If the cache code requests more space, give it to him */ + if (bytes_buff < hfsc_cache_needed_buffer (cache)) + bytes_buff = hfsc_cache_needed_buffer (cache); + + hfsp_block = (uint8_t*) ped_malloc (bytes_buff); + if (!hfsp_block) + goto error_cache; + + if (!hfsplus_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks list could not be loaded.")); + goto error_alloc; + } + + while ( fblock < ( priv_data->plus_geom->length - 2 ) + / ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ) ) { + if (TST_BLOC_OCCUPATION (priv_data->alloc_map, fblock) + && (!hfsplus_is_bad_block (fs, fblock))) { + if (!(ret = hfsplus_move_extent_starting_at (fs, + &fblock, &to_fblock, cache))) + to_fblock = ++fblock; + else if (ret == -1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred during extent " + "relocation.")); + goto error_alloc; + } + } else { + fblock++; + } + + ped_timer_update(timer, (float)(to_fblock - start) / div); + } + + ped_free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0; + hfsc_delete_cache (cache); + return 1; + +error_alloc: + ped_free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0; +error_cache: + hfsc_delete_cache (cache); + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/reloc_plus.h b/libparted/fs/hfs/reloc_plus.h new file mode 100644 index 0000000..07db0aa --- /dev/null +++ b/libparted/fs/hfs/reloc_plus.h @@ -0,0 +1,37 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _RELOC_PLUS_H +#define _RELOC_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsplus_update_vh (PedFileSystem *fs); + +int +hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free); + + +#endif /* _RELOC_PLUS_H */ diff --git a/libparted/fs/jfs/Makefile.am b/libparted/fs/jfs/Makefile.am new file mode 100644 index 0000000..48f270a --- /dev/null +++ b/libparted/fs/jfs/Makefile.am @@ -0,0 +1,6 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libjfs.la +libjfs_la_SOURCES = jfs.c jfs_superblock.h jfs_types.h + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/jfs/jfs.c b/libparted/fs/jfs/jfs.c new file mode 100644 index 0000000..97ac14d --- /dev/null +++ b/libparted/fs/jfs/jfs.c @@ -0,0 +1,112 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/endian.h> + +#define _JFS_UTILITY +#include "jfs_types.h" +#include "jfs_superblock.h" + +#define JFS_SUPER_SECTOR 64 + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <string.h> + +#define JFS_BLOCK_SIZES ((int[2]){512, 0}) + +static PedGeometry* +jfs_probe (PedGeometry* geom) +{ + union { + struct superblock sb; + char bytes[512]; + } buf; + + if (geom->length < JFS_SUPER_SECTOR + 1) + return NULL; + if (!ped_geometry_read (geom, &buf, JFS_SUPER_SECTOR, 1)) + return NULL; + + if (strncmp (buf.sb.s_magic, JFS_MAGIC, 4) == 0) { + PedSector block_size = PED_LE32_TO_CPU (buf.sb.s_pbsize) / 512; + PedSector block_count = PED_LE64_TO_CPU (buf.sb.s_size); + + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } else { + return NULL; + } +} + +#ifndef DISCOVER_ONLY +static int +jfs_clobber (PedGeometry* geom) +{ + char buf[512]; + + memset (buf, 0, 512); + return ped_geometry_write (geom, buf, JFS_SUPER_SECTOR, 1); +} +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps jfs_ops = { + probe: jfs_probe, +#ifndef DISCOVER_ONLY + clobber: jfs_clobber, +#else + clobber: NULL, +#endif + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL +}; + +static PedFileSystemType jfs_type = { + next: NULL, + ops: &jfs_ops, + name: "jfs", + block_sizes: JFS_BLOCK_SIZES +}; + +void +ped_file_system_jfs_init () +{ + ped_file_system_type_register (&jfs_type); +} + +void +ped_file_system_jfs_done () +{ + ped_file_system_type_unregister (&jfs_type); +} diff --git a/libparted/fs/jfs/jfs_superblock.h b/libparted/fs/jfs/jfs_superblock.h new file mode 100644 index 0000000..c61dc24 --- /dev/null +++ b/libparted/fs/jfs/jfs_superblock.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ +#ifndef _H_JFS_SUPERBLOCK +#define _H_JFS_SUPERBLOCK +/* + * jfs_superblock.h + */ + +/* + * make the magic number something a human could read + */ +#define JFS_MAGIC "JFS1" /* Magic word: Version 1 */ + +#define JFS_VERSION 1 /* Version number: Version 1 */ + +#define LV_NAME_SIZE 11 /* MUST BE 11 for OS/2 boot sector */ + +/* + * aggregate superblock + * + * The name superblock is too close to super_block, so the name has been + * changed to jfs_superblock. The utilities are still using the old name. + */ +#ifdef _JFS_UTILITY +struct superblock +#else +struct jfs_superblock +#endif +{ + char s_magic[4]; /* 4: magic number */ + u32 s_version; /* 4: version number */ + + s64 s_size; /* 8: aggregate size in hardware/LVM blocks; + * VFS: number of blocks + */ + s32 s_bsize; /* 4: aggregate block size in bytes; + * VFS: fragment size + */ + s16 s_l2bsize; /* 2: log2 of s_bsize */ + s16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ + s32 s_pbsize; /* 4: hardware/LVM block size in bytes */ + s16 s_l2pbsize; /* 2: log2 of s_pbsize */ + s16 pad; /* 2: padding necessary for alignment */ + + u32 s_agsize; /* 4: allocation group size in aggr. blocks */ + + u32 s_flag; /* 4: aggregate attributes: + * see jfs_filsys.h + */ + u32 s_state; /* 4: mount/unmount/recovery state: + * see jfs_filsys.h + */ + s32 s_compress; /* 4: > 0 if data compression */ + + pxd_t s_ait2; /* 8: first extent of secondary + * aggregate inode table + */ + + pxd_t s_aim2; /* 8: first extent of secondary + * aggregate inode map + */ + u32 s_logdev; /* 4: device address of log */ + s32 s_logserial; /* 4: log serial number at aggregate mount */ + pxd_t s_logpxd; /* 8: inline log extent */ + + pxd_t s_fsckpxd; /* 8: inline fsck work space extent */ + + struct timestruc_t s_time; /* 8: time last updated */ + + s32 s_fsckloglen; /* 4: Number of file system blocks reserved for + * the fsck service log. + * N.B. These blocks are divided among the + * versions kept. This is not a per + * version size. + * N.B. These blocks are included in the + * length field of s_fsckpxd. + */ + s8 s_fscklog; /* 1: which fsck service log is most recent + * 0 => no service log data yet + * 1 => the first one + * 2 => the 2nd one + */ + char s_fpack[11]; /* 11: file system volume name + * N.B. This must be 11 bytes to + * conform with the OS/2 BootSector + * requirements + */ + + /* extendfs() parameter under s_state & FM_EXTENDFS */ + s64 s_xsize; /* 8: extendfs s_size */ + pxd_t s_xfsckpxd; /* 8: extendfs fsckpxd */ + pxd_t s_xlogpxd; /* 8: extendfs logpxd */ + /* - 128 byte boundary - */ + + /* + * DFS VFS support (preliminary) + */ + char s_attach; /* 1: VFS: flag: set when aggregate is attached + */ + u8 rsrvd4[7]; /* 7: reserved - set to 0 */ + + u64 totalUsable; /* 8: VFS: total of 1K blocks which are + * available to "normal" (non-root) users. + */ + u64 minFree; /* 8: VFS: # of 1K blocks held in reserve for + * exclusive use of root. This value can be 0, + * and if it is then totalUsable will be equal + * to # of blocks in aggregate. I believe this + * means that minFree + totalUsable = # blocks. + * In that case, we don't need to store both + * totalUsable and minFree since we can compute + * one from the other. I would guess minFree + * would be the one we should store, and + * totalUsable would be the one we should + * compute. (Just a guess...) + */ + + u64 realFree; /* 8: VFS: # of free 1K blocks can be used by + * "normal" users. It may be this is something + * we should compute when asked for instead of + * storing in the superblock. I don't know how + * often this information is needed. + */ + /* + * graffiti area + */ +}; + +#endif /*_H_JFS_SUPERBLOCK */ diff --git a/libparted/fs/jfs/jfs_types.h b/libparted/fs/jfs/jfs_types.h new file mode 100644 index 0000000..7177f2a --- /dev/null +++ b/libparted/fs/jfs/jfs_types.h @@ -0,0 +1,528 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef _H_JFS_TYPES +#define _H_JFS_TYPES + +/* + * jfs_types.h: + * + * basic type/utility definitions + * + * note: this header file must be the 1st include file + * of JFS include list in all JFS .c file. + */ + +#ifdef _JFS_UTILITY +/* this is defined in asm/byteorder.h for i386, but + * is NOT defined in asm/byteorder.h for ppc (non-kernel). + * Until that is changed, we'll define it here. */ +#define __BYTEORDER_HAS_U64__ + +#include <sys/types.h> +//#include <asm/byteorder.h> +typedef unsigned short UniChar; +#else +#include <linux/types.h> +#include <linux/jfs_fs.h> +#include <linux/nls.h> + +#ifndef _ULS_UNICHAR_DEFINED +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)) +typedef wchar_t UniChar; +#else +typedef unsigned short UniChar; +#endif +#define _ULS_UNICHAR_DEFINED +#endif +#endif +/* #include "endian24.h" */ + +/* + * primitive types + */ +#ifdef _JFS_UTILITY +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; + +#ifndef _UINT_TYPES + /* unicode includes also define these */ +typedef u16 uint16; +typedef u32 uint32; +#define _UINT_TYPES +#endif + +typedef s8 int8; +typedef u8 uint8; +typedef s16 int16; +typedef s32 int32; +typedef s64 int64; +typedef u64 uint64; + +#endif /* _JFS_UTILITY */ +/* + * Holdovers from OS/2. Try to get away from using these altogether. + */ +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char UCHAR; +typedef void *PVOID; +#define MAXPATHLEN 255 + + +/* + * Almost identical to Linux's timespec, but not quite + */ +struct timestruc_t { + u32 tv_sec; + u32 tv_nsec; +}; + +/* + * handy + */ +#undef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#undef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#undef ROUNDUP +#define ROUNDUP(x, y) ( ((x) + ((y) - 1)) & ~((y) - 1) ) + +#define LEFTMOSTONE 0x80000000 +#define HIGHORDER 0x80000000u /* high order bit on */ +#define ONES 0xffffffffu /* all bit on */ + +typedef int boolean_t; +#define TRUE 1 +#define FALSE 0 + +/* + * logical xd (lxd) + */ +typedef struct { + unsigned len:24; + unsigned off1:8; + u32 off2; +} lxd_t; + +/* lxd_t field construction */ +#define LXDlength(lxd, length32) ( (lxd)->len = length32 ) +#define LXDoffset(lxd, offset64)\ +{\ + (lxd)->off1 = ((s64)offset64) >> 32;\ + (lxd)->off2 = (offset64) & 0xffffffff;\ +} + +/* lxd_t field extraction */ +#define lengthLXD(lxd) ( (lxd)->len ) +#define offsetLXD(lxd)\ + ( ((s64)((lxd)->off1)) << 32 | (lxd)->off2 ) + +/* lxd list */ +typedef struct { + s16 maxnlxd; + s16 nlxd; + lxd_t *lxd; +} lxdlist_t; + +/* + * physical xd (pxd) + */ +typedef struct { + unsigned len:24; + unsigned addr1:8; + u32 addr2; +} pxd_t; + +/* xd_t field construction */ + +#define PXDlength(pxd, length32) ((pxd)->len = __cpu_to_le24(length32)) +#define PXDaddress(pxd, address64)\ +{\ + (pxd)->addr1 = ((s64)address64) >> 32;\ + (pxd)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\ +} + +/* xd_t field extraction */ +#define lengthPXD(pxd) __le24_to_cpu((pxd)->len) +#define addressPXD(pxd)\ + ( ((s64)((pxd)->addr1)) << 32 | __le32_to_cpu((pxd)->addr2)) + +/* pxd list */ +typedef struct { + s16 maxnpxd; + s16 npxd; + pxd_t pxd[8]; +} pxdlist_t; + + +/* + * data extent descriptor (dxd) + */ +typedef struct { + unsigned flag:8; /* 1: flags */ + unsigned rsrvd:24; /* 3: */ + u32 size; /* 4: size in byte */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} dxd_t; /* - 16 - */ + +/* dxd_t flags */ +#define DXD_INDEX 0x80 /* B+-tree index */ +#define DXD_INLINE 0x40 /* in-line data extent */ +#define DXD_EXTENT 0x20 /* out-of-line single extent */ +#define DXD_FILE 0x10 /* out-of-line file (inode) */ +#define DXD_CORRUPT 0x08 /* Inconsistency detected */ + +/* dxd_t field construction + * Conveniently, the PXD macros work for DXD + */ +#define DXDlength PXDlength +#define DXDaddress PXDaddress +#define lengthDXD lengthPXD +#define addressDXD addressPXD + +/* + * directory entry argument + */ +typedef struct component_name { + int namlen; + UniChar *name; +} component_t; + + +/* + * DASD limit information - stored in directory inode + */ +typedef struct dasd { + u8 thresh; /* Alert Threshold (in percent) */ + u8 delta; /* Alert Threshold delta (in percent) */ + u8 rsrvd1; + u8 limit_hi; /* DASD limit (in logical blocks) */ + u32 limit_lo; /* DASD limit (in logical blocks) */ + u8 rsrvd2[3]; + u8 used_hi; /* DASD usage (in logical blocks) */ + u32 used_lo; /* DASD usage (in logical blocks) */ +} dasd_t; + +#define DASDLIMIT(dasdp) \ + (((u64)((dasdp)->limit_hi) << 32) + __le32_to_cpu((dasdp)->limit_lo)) +#define setDASDLIMIT(dasdp, limit)\ +{\ + (dasdp)->limit_hi = ((u64)limit) >> 32;\ + (dasdp)->limit_lo = __cpu_to_le32(limit);\ +} +#define DASDUSED(dasdp) \ + (((u64)((dasdp)->used_hi) << 32) + __le32_to_cpu((dasdp)->used_lo)) +#define setDASDUSED(dasdp, used)\ +{\ + (dasdp)->used_hi = ((u64)used) >> 32;\ + (dasdp)->used_lo = __cpu_to_le32(used);\ +} + +/* + * circular doubly-linked list (cdll) + * + * A circular doubly-linked list (cdll) is anchored by a pair of pointers, + * one to the head of the list and the other to the tail of the list. + * The elements are doubly linked so that an arbitrary element can be + * removed without a need to traverse the list. + * New elements can be added to the list before or after an existing element, + * at the head of the list, or at the tail of the list. + * A circle queue may be traversed in either direction. + * + * +----------+ +-------------------------------------+ + * | | | | + * +->+-----+ | +->+-----+ +->+-----+ +->+-----+ | + * | | h +-+ | | h +--+ | n +----+ | n +--+ + * | +-----+ | +-----+ | +-----+ | +-----+ + * | | t +-+ +-----+ t | | | p +--+ | | p +--+ + * | +-----+ | | | +-----+ | +-----+ | | +-----+ | + * +----------+ | +-----------------------+ | | + * | | | | + * | +-------------------------+ + * | | + * +----------------------------+ + */ +/* + * define header + * + * list header field definition in header element: + * + * type - type of list element struct embedding the link field + */ +#define CDLL_HEADER(type)\ +struct {\ + struct type *head;\ + struct type *tail;\ +} + +struct cdll_header { + struct cdll_header *head; + struct cdll_header *tail; +}; + +/* + * define link + * + * list link field definition in list element: + * + * type - type of parent list element struct embedding the link field + */ +#define CDLL_ENTRY(type)\ +struct {\ + struct type *next;\ + struct type *prev;\ +} + +struct cdll_entry { + struct cdll_entry *next; + struct cdll_entry *prev; +}; + +/* + * initialize header + * + * header - ptr to the header field in the header element + */ +#define CDLL_INIT(header) {\ + (header)->head = (void *)(header);\ + (header)->tail = (void *)(header);\ +} + +/* + * scan list + * + * header - ptr to the header field in the header element + * elm - ptr to the element to be inserted + * field - name of the link field in the list element + * + * struct header_container *container; + * struct header_type *header; + * struct element_type *elm; + * + * header = &container->header_field; + * for (elm = header->head; elm != (void *)header; elm = elm->field.next) + */ + +/* + * insert <elm> at head of list anchored at <header> + * + * header - ptr to the header field in the header element + * elm - ptr to the list element to be inserted + * field - name of the link field in the list element + */ +#define CDLL_INSERT_HEAD(header, elm, field) {\ + (elm)->field.next = (header)->head;\ + (elm)->field.prev = (void *)(header);\ + if ((header)->tail == (void *)(header))\ + (header)->tail = (elm);\ + else\ + (header)->head->field.prev = (elm);\ + (header)->head = (elm);\ +} + +/* + * insert <elm> at tail of list anchored at <header> + * + * header - ptr to the header field in the header element + * elm - ptr to the list element to be inserted + * field - name of the link field in the list element + */ +#define CDLL_INSERT_TAIL(header, elm, field) {\ + (elm)->field.next = (void *)(header);\ + (elm)->field.prev = (header)->tail;\ + if ((header)->head == (void *)(header))\ + (header)->head = (elm);\ + else\ + (header)->tail->field.next = (elm);\ + (header)->tail = (elm);\ +} + +/* + * insert <elm> after <listelm> of list anchored at <header> + * + * header - ptr to the header field in the header element + * listelm - ptr to the list element at insertion point + * elm - ptr to the list element to be inserted + * field - name of the link field in the list element + */ +#define CDLL_INSERT_AFTER(header, listelm, elm, field) {\ + (elm)->field.next = (listelm)->field.next;\ + (elm)->field.prev = (listelm);\ + if ((listelm)->field.next == (void *)(header))\ + (header)->tail = (elm);\ + else\ + (listelm)->field.next->field.prev = (elm);\ + (listelm)->field.next = (elm);\ +} + +/* + * insert <elm> before <listelm> of list anchored at <header> + * + * header - ptr to the header field in the header element + * listelm - ptr to list element at insertion point + * elm - ptr to the element to be inserted + * field - name of the link field in the list element + */ +#define CDLL_INSERT_BEFORE(header, listelm, elm, field) {\ + (elm)->field.next = (listelm);\ + (elm)->field.prev = (listelm)->field.prev;\ + if ((listelm)->field.prev == (void *)(header))\ + (header)->head = (elm);\ + else\ + (listelm)->field.prev->field.next = (elm);\ + (listelm)->field.prev = (elm);\ +} + +/* + * remove <elm> from list anchored at <header> + * + * header - ptr to the header field in the header element + * elm - ptr to the list element to be removed + * field - name of the link field in the list element + */ +#define CDLL_REMOVE(header, elm, field) {\ + if ((elm)->field.next == (void *)(header))\ + (header)->tail = (elm)->field.prev;\ + else\ + (elm)->field.next->field.prev = (elm)->field.prev;\ + if ((elm)->field.prev == (void *)(header))\ + (header)->head = (elm)->field.next;\ + else\ + (elm)->field.prev->field.next = (elm)->field.next;\ +} + +#define CDLL_MOVE_TO_HEAD(header, elm, field) {\ + if ((elm)->field.prev != (void *)(header))\ + {\ + if ((elm)->field.next == (void *)(header))\ + (header)->tail = (elm)->field.prev;\ + else\ + (elm)->field.next->field.prev = (elm)->field.prev;\ + (elm)->field.prev->field.next = (elm)->field.next;\ + (elm)->field.next = (header)->head;\ + (elm)->field.prev = (void *)(header);\ + (header)->head->field.prev = (elm);\ + (header)->head = (elm);\ + }\ +} + +#define CDLL_MOVE_TO_TAIL(header, elm, field) {\ + if ((elm)->field.next != (void *)(header))\ + {\ + (elm)->field.next->field.prev = (elm)->field.prev;\ + if ((elm)->field.prev == (void *)(header))\ + (header)->head = (elm)->field.next;\ + else\ + (elm)->field.prev->field.next = (elm)->field.next;\ + (elm)->field.next = (void *)(header);\ + (elm)->field.prev = (header)->tail;\ + (header)->tail->field.next = (elm);\ + (header)->tail = (elm);\ + }\ +} + +/* + * orphan list element + */ +#define CDLL_SELF(elm, field)\ + (elm)->field.next = (elm)->field.prev = (elm); + + +/* + * single head doubly-linked list + * + * A list is headed by a single head pointer. + * The elements are doubly linked so that an arbitrary element can be + * removed without a need to traverse the list. + * New elements can be added to the list at the head of the list, or + * after an existing element (NO insert at tail). + * A list may only be traversed in the forward direction. + * (note: the list is NULL terminated in next field.) + * + * +-----+ +->+-----+ +->+-----+ +->+-----+ + * | NULL| | | h +--+ | n +----+ | NULL| + * +-----+ | +-----+ | +-----+ +-----+ + * | | | p +--+ | p +--+ + * | | +-----+ | +-----+ | + * +-----------------------+ | + * | | + * +-------------------------+ + */ +#define LIST_HEADER(type)\ +struct {\ + struct type *head;\ +} + +#define LIST_ENTRY(type)\ +struct {\ + struct type *next;\ + struct type **prev;\ +} + +#define LIST_INIT(header) { (header)->head = NULL; } + +/* + * scan list + * + * header - ptr to the header (field in header element) + * elm - ptr to the element to be inserted + * field - name of the link field in list element + * + * struct header_container *container; + * struct header_type *header; + * struct element_type *elm; + * + * header = &container->header_field; + * for (elm = header->head; elm; elm = elm->field.next) + */ + +#define LIST_INSERT_HEAD(header, elm, field) {\ + if (((elm)->field.next = (header)->head) != NULL)\ + (header)->head->field.prev = &(elm)->field.next;\ + (header)->head = (elm);\ + (elm)->field.prev = &(header)->head;\ +} + +#define LIST_INSERT_AFTER(listelm, elm, field) {\ + if (((elm)->field.next = (listelm)->field.next) != NULL)\ + (listelm)->field.next->field.prev = &(elm)->field.next;\ + (listelm)->field.next = (elm);\ + (elm)->field.prev = &(listelm)->field.next;\ +} + +#define LIST_REMOVE(elm, field) {\ + if ((elm)->field.next != NULL)\ + (elm)->field.next->field.prev = (elm)->field.prev;\ + *(elm)->field.prev = (elm)->field.next;\ +} + +#define LIST_SELF(elm, field) {\ + (elm)->field.next = NULL;\ + (elm)->field.prev = &(elm)->field.next;\ +} + +#endif /* !_H_JFS_TYPES */ diff --git a/libparted/fs/linux_swap/Makefile.am b/libparted/fs/linux_swap/Makefile.am new file mode 100644 index 0000000..b913fe3 --- /dev/null +++ b/libparted/fs/linux_swap/Makefile.am @@ -0,0 +1,6 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = liblinuxswap.la +liblinuxswap_la_SOURCES = linux_swap.c + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/linux_swap/linux_swap.c b/libparted/fs/linux_swap/linux_swap.c new file mode 100644 index 0000000..02185e1 --- /dev/null +++ b/libparted/fs/linux_swap/linux_swap.c @@ -0,0 +1,520 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000, 2002 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/* It's a bit silly calling a swap partition a file system. Oh well... */ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/endian.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> + +#define SWAP_SPECIFIC(fs) ((SwapSpecific*) (fs->type_specific)) +#define BUFFER_SIZE 128 + +#define LINUXSWAP_BLOCK_SIZES ((int[2]){512, 0}) + +typedef struct { + char page_map[1]; +} SwapOldHeader; + +/* ripped from mkswap */ +typedef struct { + char bootbits[1024]; /* Space for disklabel etc. */ + unsigned int version; + unsigned int last_page; + unsigned int nr_badpages; + unsigned int padding[125]; + unsigned int badpages[1]; +} SwapNewHeader; + +typedef struct { + union { + SwapNewHeader new; + SwapOldHeader old; + }* header; + + void* buffer; + int buffer_size; + + PedSector page_sectors; + unsigned int page_count; + unsigned int version; + unsigned int max_bad_pages; +} SwapSpecific; + +static PedFileSystemType swap_type; + +static PedFileSystem* swap_open (PedGeometry* geom); +static int swap_close (PedFileSystem* fs); + +static PedGeometry* +swap_probe (PedGeometry* geom) +{ + PedFileSystem* fs; + SwapSpecific* fs_info; + PedGeometry* probed_geom; + PedSector length; + + fs = swap_open (geom); + if (!fs) + goto error; + fs_info = SWAP_SPECIFIC (fs); + + if (fs_info->version) + length = fs_info->page_sectors * fs_info->page_count; + else + length = geom->length; + probed_geom = ped_geometry_new (geom->dev, geom->start, length); + if (!probed_geom) + goto error_close_fs; + swap_close (fs); + return probed_geom; + +error_close_fs: + swap_close (fs); +error: + return NULL; +} + +#ifndef DISCOVER_ONLY +static int +swap_clobber (PedGeometry* geom) +{ + PedFileSystem* fs; + char buf[512]; + + fs = swap_open (geom); + if (!fs) + return 1; + + memset (buf, 0, 512); + if (!ped_geometry_write (geom, buf, getpagesize() / 512 - 1, 1)) + goto error_close_fs; + + swap_close (fs); + return 1; + +error_close_fs: + swap_close (fs); +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static void +swap_init (PedFileSystem* fs, int fresh) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + + fs_info->page_sectors = getpagesize () / 512; + fs_info->page_count = fs->geom->length / fs_info->page_sectors; + fs_info->version = 1; + fs_info->max_bad_pages = (getpagesize() + - sizeof (SwapNewHeader)) / 4; + + if (fresh) + memset (fs_info->header, 0, getpagesize()); + else + ped_geometry_read (fs->geom, fs_info->header, + 0, fs_info->page_sectors); +} + +static PedFileSystem* +swap_alloc (PedGeometry* geom) +{ + PedFileSystem* fs; + SwapSpecific* fs_info; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + goto error; + + fs->type_specific = (SwapSpecific*) ped_malloc (sizeof (SwapSpecific)); + if (!fs->type_specific) + goto error_free_fs; + + fs_info = SWAP_SPECIFIC (fs); + fs_info->header = ped_malloc (getpagesize()); + if (!fs_info->header) + goto error_free_type_specific; + + fs_info = SWAP_SPECIFIC (fs); + fs_info->buffer_size = getpagesize() * BUFFER_SIZE; + fs_info->buffer = ped_malloc (fs_info->buffer_size); + if (!fs_info->buffer) + goto error_free_header; + + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) + goto error_free_buffer; + fs->type = &swap_type; + return fs; + +error_free_buffer: + ped_free (fs_info->buffer); +error_free_header: + ped_free (fs_info->header); +error_free_type_specific: + ped_free (fs->type_specific); +error_free_fs: + ped_free (fs); +error: + return NULL; +} + +static void +swap_free (PedFileSystem* fs) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + + ped_free (fs_info->buffer); + ped_free (fs_info->header); + ped_free (fs->type_specific); + + ped_geometry_destroy (fs->geom); + ped_free (fs); +} + +static PedFileSystem* +swap_open (PedGeometry* geom) +{ + PedFileSystem* fs; + SwapSpecific* fs_info; + const char* sig; + + fs = swap_alloc (geom); + if (!fs) + goto error; + swap_init (fs, 0); + + fs_info = SWAP_SPECIFIC (fs); + if (!ped_geometry_read (fs->geom, fs_info->header, 0, + fs_info->page_sectors)) + goto error_free_fs; + + sig = ((char*) fs_info->header) + getpagesize() - 10; + if (strncmp (sig, "SWAP-SPACE", 10) == 0) { + fs_info->version = 0; + fs_info->page_count + = PED_MIN (fs->geom->length / fs_info->page_sectors, + 8 * (getpagesize() - 10)); + } else if (strncmp (sig, "SWAPSPACE2", 10) == 0) { + fs_info->version = 1; + fs_info->page_count = fs_info->header->new.last_page; + } else { + char _sig [11]; + + memcpy (_sig, sig, 10); + _sig [10] = 0; + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unrecognised linux swap signature '%10s'."), _sig); + goto error_free_fs; + } + + fs->checked = 1; + return fs; + +error_free_fs: + swap_free (fs); +error: + return NULL; +} + +static int +swap_close (PedFileSystem* fs) +{ + swap_free (fs); + return 1; +} + +#ifndef DISCOVER_ONLY +static int +swap_new_find_bad_page (PedFileSystem* fs, unsigned int page) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + unsigned int i; + + for (i=0; i < fs_info->header->new.nr_badpages; i++) { + if (fs_info->header->new.badpages [i] == page) + return i; + } + + return 0; +} + +static int +swap_new_remove_bad_page (PedFileSystem* fs, unsigned int page) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + unsigned int pos; + + pos = swap_new_find_bad_page (fs, page); + if (!pos) + return 0; + + for (; pos < fs_info->header->new.nr_badpages; pos++) { + fs_info->header->new.badpages [pos - 1] + = fs_info->header->new.badpages [pos]; + } + + return 1; +} + +static int +swap_mark_page (PedFileSystem* fs, unsigned int page, int ok) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + char* ptr; + unsigned int mask; + + if (fs_info->version == 0) { + ptr = &fs_info->header->old.page_map [page/8]; + mask = 1 << (page%8); + *ptr = (*ptr & ~mask) + ok * mask; + } else { + if (ok) { + if (swap_new_remove_bad_page (fs, page)) + fs_info->header->new.nr_badpages--; + } else { + if (swap_new_find_bad_page (fs, page)) + return 1; + + if (fs_info->header->new.nr_badpages + > fs_info->max_bad_pages) { + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Too many bad pages.")); + return 0; + } + + fs_info->header->new.badpages + [fs_info->header->new.nr_badpages] = page; + fs_info->header->new.nr_badpages++; + } + } + + return 1; +} + +static void +swap_clear_pages (PedFileSystem* fs) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + unsigned int i; + + for (i = 1; i < fs_info->page_count; i++) { + swap_mark_page (fs, i, 1); + } + + if (fs_info->version == 0) { + for (; i < 1024; i++) { + swap_mark_page (fs, i, 0); + } + } +} + +static int +swap_check_pages (PedFileSystem* fs, PedTimer* timer) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + PedSector result; + int first_page = 1; + int stop_page = 0; + int last_page = fs_info->page_count - 1; + PedTimer* nested_timer; + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("checking for bad blocks")); + + swap_clear_pages (fs); + while (first_page <= last_page) { + nested_timer = ped_timer_new_nested ( + timer, + 1.0 * (last_page - first_page) / last_page); + result = ped_geometry_check ( + fs->geom, + fs_info->buffer, + fs_info->buffer_size / 512, + first_page * fs_info->page_sectors, + fs_info->page_sectors, + (last_page - first_page + 1) + * fs_info->page_sectors, + nested_timer); + ped_timer_destroy_nested (nested_timer); + if (!result) + return 1; + stop_page = result / fs_info->page_sectors; + if (!swap_mark_page (fs, stop_page, 0)) + return 0; + first_page = stop_page + 1; + } + return 1; +} + +static int +swap_write (PedFileSystem* fs) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + char* sig = ((char*) fs_info->header) + getpagesize() - 10; + + if (fs_info->version == 0) { + memcpy (sig, "SWAP-SPACE", 10); + } else { + fs_info->header->new.version = 1; + fs_info->header->new.last_page = fs_info->page_count - 1; + fs_info->header->new.nr_badpages = 0; + memcpy (sig, "SWAPSPACE2", 10); + } + + return ped_geometry_write (fs->geom, fs_info->header, 0, + fs_info->page_sectors); +} + +static PedFileSystem* +swap_create (PedGeometry* geom, PedTimer* timer) +{ + PedFileSystem* fs; + + fs = swap_alloc (geom); + if (!fs) + goto error; + swap_init (fs, 1); + if (!swap_write (fs)) + goto error_free_fs; + return fs; + +error_free_fs: + swap_free (fs); +error: + return NULL; +} + +static int +swap_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + PedGeometry* old_geom = fs->geom; + + fs->geom = ped_geometry_duplicate (geom); + swap_init (fs, old_geom->start != geom->start); + if (!swap_write (fs)) + goto error; + ped_geometry_destroy (old_geom); + return 1; + +error: + ped_geometry_destroy (fs->geom); + fs->geom = old_geom; + return 0; +} + +static PedFileSystem* +swap_copy (const PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + return ped_file_system_create (geom, &swap_type, timer); +} + +static int +swap_check (PedFileSystem* fs, PedTimer* timer) +{ + return swap_check_pages (fs, timer) + && swap_write (fs); +} + +static PedConstraint* +swap_get_create_constraint (const PedDevice* dev) +{ + PedGeometry full_dev; + + 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, + getpagesize() / 512, dev->length); +} + +static PedConstraint* +swap_get_resize_constraint (const PedFileSystem* fs) +{ + return swap_get_create_constraint (fs->geom->dev); +} + +static PedConstraint* +swap_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev) +{ + return swap_get_create_constraint (dev); +} +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps swap_ops = { + probe: swap_probe, +#ifndef DISCOVER_ONLY + clobber: swap_clobber, + open: swap_open, + create: swap_create, + close: swap_close, + check: swap_check, + copy: swap_copy, + resize: swap_resize, + get_create_constraint: swap_get_create_constraint, + get_resize_constraint: swap_get_resize_constraint, + get_copy_constraint: swap_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 /* !DISCOVER_ONLY */ +}; + +static PedFileSystemType swap_type = { + next: NULL, + ops: &swap_ops, + name: "linux-swap", + block_sizes: LINUXSWAP_BLOCK_SIZES +}; + +void +ped_file_system_linux_swap_init () +{ + ped_file_system_type_register (&swap_type); +} + +void +ped_file_system_linux_swap_done () +{ + ped_file_system_type_unregister (&swap_type); +} + diff --git a/libparted/fs/ntfs/Makefile.am b/libparted/fs/ntfs/Makefile.am new file mode 100644 index 0000000..8a423bb --- /dev/null +++ b/libparted/fs/ntfs/Makefile.am @@ -0,0 +1,6 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libntfs.la +libntfs_la_SOURCES = ntfs.c + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/ntfs/ntfs.c b/libparted/fs/ntfs/ntfs.c new file mode 100644 index 0000000..317d0e3 --- /dev/null +++ b/libparted/fs/ntfs/ntfs.c @@ -0,0 +1,104 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/endian.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> + + +#define NTFS_BLOCK_SIZES ((int[2]){512, 0}) + +#define NTFS_SIGNATURE "NTFS" + +static PedGeometry* +ntfs_probe (PedGeometry* geom) +{ + char buf[512]; + + 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]; + + memset (buf, 0, 512); + return ped_geometry_write (geom, buf, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps ntfs_ops = { + probe: ntfs_probe, +#ifndef DISCOVER_ONLY + clobber: ntfs_clobber, +#else + clobber: NULL, +#endif + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL +}; + +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); +} + + diff --git a/libparted/fs/reiserfs/Makefile.am b/libparted/fs/reiserfs/Makefile.am new file mode 100644 index 0000000..9812ede --- /dev/null +++ b/libparted/fs/reiserfs/Makefile.am @@ -0,0 +1,7 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libreiserfs.la + +libreiserfs_la_SOURCES = reiserfs.c reiserfs.h geom_dal.c geom_dal.h + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/reiserfs/geom_dal.c b/libparted/fs/reiserfs/geom_dal.c new file mode 100644 index 0000000..ad315f0 --- /dev/null +++ b/libparted/fs/reiserfs/geom_dal.c @@ -0,0 +1,139 @@ +/* + geom_dal.c -- parted device abstraction layer + Copyright (C) 2001, 2002 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#if (DYNAMIC_LOADING || HAVE_LIBREISERFS) && !DISCOVER_ONLY + +#include "geom_dal.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +static blk_t __len(dal_t *dal) { + PED_ASSERT(dal != NULL, return 0); + + return ((PedGeometry *)dal->dev)->length / + (dal->block_size / PED_SECTOR_SIZE_DEFAULT); +} + +static int __read(dal_t *dal, void *buff, blk_t block, blk_t count) { + blk_t k; + PedSector block_pos; + PedSector block_count; + + PED_ASSERT(dal != NULL, return 0); + + k = dal->block_size / PED_SECTOR_SIZE_DEFAULT; + block_pos = (PedSector)(block * k); + block_count = (PedSector)(count * k); + + return ped_geometry_read((PedGeometry *)dal->dev, buff, block_pos, block_count); +} + +static int __write(dal_t *dal, void *buff, blk_t block, blk_t count) { + blk_t k; + PedSector block_pos; + PedSector block_count; + + PED_ASSERT(dal != NULL, return 0); + + k = dal->block_size / PED_SECTOR_SIZE_DEFAULT; + block_pos = (PedSector)(block * k); + block_count = (PedSector)(count * k); + + return ped_geometry_write((PedGeometry *)dal->dev, buff, block_pos, + block_count); +} + +static int __sync(dal_t *dal) { + PED_ASSERT(dal != NULL, return 0); + return ped_geometry_sync((PedGeometry *)dal->dev); +} + +static int __flags(dal_t *dal) { + PED_ASSERT(dal != NULL, return 0); + return dal->flags; +} + +static int __equals(dal_t *dal1, dal_t *dal2) { + PED_ASSERT(dal1 != NULL, return 0); + PED_ASSERT(dal2 != NULL, return 0); + + return ped_geometry_test_equal((PedGeometry *)dal1->dev, + (PedGeometry *)dal2->dev); +} + +static int __stat(dal_t *dal, struct stat *st) { + + PED_ASSERT(dal != NULL, return 0); + PED_ASSERT(st != NULL, return 0); + + if (stat(((PedGeometry *)dal->dev)->dev->path, st)) + return 0; + + return 1; +} + +static dev_t __dev(dal_t *dal) { + struct stat st; + + if (!__stat(dal, &st)) + return (dev_t)0; + + return st.st_dev; +} + +static struct dal_ops ops = { + __len, __read, __write, __sync, + __flags, __equals, __stat, __dev +}; + +dal_t *geom_dal_create(PedGeometry *geom, size_t block_size, int flags) { + dal_t *dal; + + if (!geom) + return NULL; + + if (!(dal = ped_malloc(sizeof(dal_t)))) + return NULL; + + dal->ops = &ops; + dal->dev = geom; + dal->block_size = block_size; + dal->flags = flags; + dal->len = 0; + + return dal; +} + +int geom_dal_reopen(dal_t *dal, int flags) { + + if (!dal) return 0; + dal->flags = flags; + + return 1; +} + +void geom_dal_free(dal_t *dal) { + PED_ASSERT(dal != NULL, return); + ped_free(dal); +} + +#endif diff --git a/libparted/fs/reiserfs/geom_dal.h b/libparted/fs/reiserfs/geom_dal.h new file mode 100644 index 0000000..d4f29f3 --- /dev/null +++ b/libparted/fs/reiserfs/geom_dal.h @@ -0,0 +1,63 @@ +/* + geom_dal.h -- parted device abstraction layer + Copyright (C) 2001, 2002 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef GEOM_DAL_H +#define GEOM_DAL_H + +#include "config.h" + +#include <parted/parted.h> + +#if DYNAMIC_LOADING || !DISCOVER_ONLY + +#include <sys/stat.h> + +typedef unsigned long blk_t; + +struct dal_ops; + +struct _dal { + struct dal_ops *ops; + const void *dev; + size_t block_size; + int flags; + void *data; + blk_t len; +}; + +typedef struct _dal dal_t; + +struct dal_ops { + blk_t (*len)(dal_t *); + int (*read)(dal_t *, void *, blk_t, blk_t); + int (*write)(dal_t *, void *, blk_t, blk_t); + int (*sync)(dal_t *); + int (*flags)(dal_t *); + int (*equals)(dal_t *, dal_t *); + int (*stat)(dal_t *, struct stat *); + dev_t (*dev)(dal_t *); +}; + +extern dal_t *geom_dal_create(PedGeometry *geom, size_t block_size, int flags); +extern int geom_dal_reopen(dal_t *dal, int flags); +extern void geom_dal_free(dal_t *dal); + +#endif + +#endif diff --git a/libparted/fs/reiserfs/reiserfs.c b/libparted/fs/reiserfs/reiserfs.c new file mode 100644 index 0000000..219857b --- /dev/null +++ b/libparted/fs/reiserfs/reiserfs.c @@ -0,0 +1,867 @@ +/* + reiserfs.c -- libparted / libreiserfs glue + Copyright (C) 2001, 2002 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + This is all rather complicated. There are a few combinations: + * shared libraries full support + * dynamic libraries present full support (via dlopen) + * dynamic libraries absent (full support disabled) (via dlopen) + * discover only + + We'd love to hear comments... + + So far, we've opted for maximum flexibility for the user. Is it + all worth it? +*/ + +#include "config.h" + +#if (HAVE_LIBREISERFS || DYNAMIC_LOADING) && !DISCOVER_ONLY +# define REISER_FULL_SUPPORT +#endif + +#include <uuid/uuid.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> + +#ifdef DYNAMIC_LOADING +# include <dlfcn.h> +#endif + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif + +#include "reiserfs.h" +#include "geom_dal.h" + +#define REISERFS_BLOCK_SIZES ((int[2]){512, 0}) + +static PedSector reiserfs_super_offset[] = { 128, 16, -1 }; +static PedFileSystemType* reiserfs_type; + +#ifdef DYNAMIC_LOADING +# define FPTR * +# define FCLASS static +#else +# define FPTR +# define FCLASS extern +#endif + +#ifdef DYNAMIC_LOADING + +static int libreiserfs_present; + +static void *libdal_handle; +static void *libreiserfs_handle; + +#endif /* DYNAMIC_LOADING */ + +#ifdef REISER_FULL_SUPPORT + +FCLASS blk_t (FPTR reiserfs_fs_probe) (dal_t *); + +FCLASS int (FPTR libreiserfs_exception_type) (reiserfs_exception_t *); +FCLASS int (FPTR libreiserfs_exception_option) (reiserfs_exception_t *); +FCLASS char *(FPTR libreiserfs_exception_message) (reiserfs_exception_t *); +FCLASS void (FPTR libreiserfs_exception_set_handler) + (int(FPTR)(reiserfs_exception_t *)); + +FCLASS void (FPTR dal_realize) (dal_t *); +FCLASS size_t (FPTR dal_block_size) (dal_t *); +FCLASS blk_t (FPTR dal_len) (dal_t *); +FCLASS int (FPTR dal_flags) (dal_t *); + +FCLASS reiserfs_fs_t* (FPTR reiserfs_fs_open) (dal_t *, dal_t *); +FCLASS reiserfs_fs_t* (FPTR reiserfs_fs_create) (dal_t *, dal_t *, + blk_t, blk_t, blk_t, size_t, + int, int, const char *, + const char *, blk_t, + reiserfs_gauge_t *); + +FCLASS int (FPTR reiserfs_fs_resize) (reiserfs_fs_t *, blk_t, reiserfs_gauge_t *); +FCLASS int (FPTR reiserfs_fs_check) (reiserfs_fs_t *, reiserfs_gauge_t *); + +FCLASS reiserfs_fs_t *(FPTR reiserfs_fs_copy) (reiserfs_fs_t *, dal_t *, + reiserfs_gauge_t *); + +FCLASS int (FPTR reiserfs_fs_clobber) (dal_t *); +FCLASS void (FPTR reiserfs_fs_close) (reiserfs_fs_t *); + +FCLASS int (FPTR reiserfs_fs_is_resizeable) (reiserfs_fs_t *); +FCLASS int (FPTR reiserfs_fs_is_consistent) (reiserfs_fs_t *); + +FCLASS blk_t (FPTR reiserfs_fs_min_size) (reiserfs_fs_t *); +FCLASS blk_t (FPTR reiserfs_fs_block_size) (reiserfs_fs_t *); +FCLASS dal_t* (FPTR reiserfs_fs_host_dal) (reiserfs_fs_t *); + +FCLASS blk_t (FPTR reiserfs_fs_bitmap_used) (reiserfs_fs_t *); +FCLASS int (FPTR reiserfs_fs_bitmap_check) (reiserfs_fs_t *); + +FCLASS reiserfs_gauge_t *(FPTR libreiserfs_gauge_create) ( + char *, reiserfs_gauge_handler_t, void *); + +FCLASS void (FPTR libreiserfs_gauge_free) (reiserfs_gauge_t *); + +static void gauge_handler(const char *name, unsigned int value, void *data, + int determined, int update_header, + int update_footer) +{ + PedTimer *timer = (PedTimer *) data; + ped_timer_set_state_name(timer, name); + ped_timer_update(timer, 1.0 * value / 100); +} + +static PedExceptionOption +exopt_libreiserfs_to_parted(reiserfs_exception_option_t option) +{ + switch (option) { + case EXCEPTION_UNHANDLED: + return PED_EXCEPTION_UNHANDLED; + case EXCEPTION_FIX: + return PED_EXCEPTION_FIX; + case EXCEPTION_YES: + return PED_EXCEPTION_YES; + case EXCEPTION_NO: + return PED_EXCEPTION_NO; + case EXCEPTION_OK: + return PED_EXCEPTION_OK; + case EXCEPTION_RETRY: + return PED_EXCEPTION_RETRY; + case EXCEPTION_IGNORE: + return PED_EXCEPTION_IGNORE; + case EXCEPTION_CANCEL: + return PED_EXCEPTION_CANCEL; + + default: + return PED_EXCEPTION_UNHANDLED; + } +} + +static PedExceptionType +extype_libreiserfs_to_parted(reiserfs_exception_type_t type) +{ + switch (type) { + case EXCEPTION_INFORMATION: + return PED_EXCEPTION_INFORMATION; + case EXCEPTION_WARNING: + return PED_EXCEPTION_WARNING; + case EXCEPTION_ERROR: + return PED_EXCEPTION_ERROR; + case EXCEPTION_FATAL: + return PED_EXCEPTION_FATAL; + case EXCEPTION_BUG: + return PED_EXCEPTION_BUG; + case EXCEPTION_NO_FEATURE: + return PED_EXCEPTION_NO_FEATURE; + + default: + return PED_EXCEPTION_NO_FEATURE; + } +} + +static int exception_handler(reiserfs_exception_t *exception) +{ + int ex_type = libreiserfs_exception_type(exception); + int ex_option = libreiserfs_exception_option(exception); + char *ex_message = libreiserfs_exception_message(exception); + + return ped_exception_throw (extype_libreiserfs_to_parted (ex_type), + exopt_libreiserfs_to_parted (ex_option), + ex_message); +} +#endif /* REISER_FULL_SUPPORT */ + +static PedGeometry *reiserfs_probe(PedGeometry *geom) +{ + int i; + reiserfs_super_block_t sb; + + PED_ASSERT(geom != NULL, return NULL); + + for (i = 0; reiserfs_super_offset[i] != -1; i++) { + if (reiserfs_super_offset[i] >= geom->length) + continue; + if (!ped_geometry_read (geom, &sb, reiserfs_super_offset[i], 1)) + continue; + + if (strncmp(REISERFS_SIGNATURE, sb.s_magic, + strlen(REISERFS_SIGNATURE)) == 0 + || strncmp(REISER2FS_SIGNATURE, sb.s_magic, + strlen(REISER2FS_SIGNATURE)) == 0 + || strncmp(REISER3FS_SIGNATURE, sb.s_magic, + strlen(REISER3FS_SIGNATURE)) == 0) { + PedSector block_size; + PedSector block_count; + + block_size = PED_LE16_TO_CPU(sb.s_blocksize) + / PED_SECTOR_SIZE_DEFAULT; + block_count = PED_LE32_TO_CPU(sb.s_block_count); + + return ped_geometry_new(geom->dev, geom->start, + block_size * block_count); + } + } + return NULL; +} + +#ifndef DISCOVER_ONLY +static int reiserfs_clobber(PedGeometry *geom) +{ + int i; + char buf[512]; + + PED_ASSERT(geom != NULL, return 0); + + memset(buf, 0, 512); + for (i = 0; reiserfs_super_offset[i] != -1; i++) { + if (reiserfs_super_offset[i] >= geom->length) + continue; + if (!ped_geometry_write + (geom, buf, reiserfs_super_offset[i], 1)) + return 0; + } + return 1; +} +#endif /* !DISCOVER_ONLY */ + +#ifdef REISER_FULL_SUPPORT + +static PedFileSystem *reiserfs_open(PedGeometry *geom) +{ + PedFileSystem *fs; + PedGeometry *fs_geom; + dal_t *dal; + reiserfs_fs_t *fs_info; + + PED_ASSERT(geom != NULL, return NULL); + + if (!(fs_geom = ped_geometry_duplicate(geom))) + goto error; + + if (! (dal = geom_dal_create(fs_geom, DEFAULT_BLOCK_SIZE, O_RDONLY))) + goto error_fs_geom_free; + + /* + We are passing NULL as DAL for journal. Therefore we let libreiserfs know, + that journal not available and parted will be working fine for reiserfs + with relocated journal too. + */ + if (!(fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem)))) + goto error_free_dal; + + if (!(fs_info = reiserfs_fs_open(dal, NULL))) + goto error_free_fs; + + fs->type = reiserfs_type; + fs->geom = fs_geom; + fs->type_specific = (void *) fs_info; + + return fs; + +error_free_fs: + ped_free(fs); +error_free_dal: + geom_dal_free(dal); +error_fs_geom_free: + ped_geometry_destroy(fs_geom); +error: + return NULL; +} + +static PedFileSystem *reiserfs_create(PedGeometry *geom, PedTimer *timer) +{ + dal_t *dal; + uuid_t uuid; + PedFileSystem *fs; + PedGeometry *fs_geom; + reiserfs_fs_t *fs_info; + reiserfs_gauge_t *gauge = NULL; + + PED_ASSERT(geom != NULL, return NULL); + + fs_geom = ped_geometry_duplicate(geom); + + if (!(dal = geom_dal_create(fs_geom, DEFAULT_BLOCK_SIZE, O_RDWR))) + goto error_fs_geom_free; + + memset(uuid, 0, sizeof(uuid)); + uuid_generate(uuid); + + ped_timer_reset(timer); + ped_timer_set_state_name(timer, _("creating")); + + if (libreiserfs_gauge_create && libreiserfs_gauge_free) { + if (! (gauge = + libreiserfs_gauge_create(NULL, gauge_handler, timer))) + goto error_free_dal; + } + + if (!(fs_info = reiserfs_fs_create(dal, dal, 0, JOURNAL_MAX_TRANS, + DEFAULT_JOURNAL_SIZE, + DEFAULT_BLOCK_SIZE, + FS_FORMAT_3_6, R5_HASH, NULL, + (char *) uuid, dal_len(dal), + gauge))) + goto error_free_gauge; + + ped_timer_update(timer, 1.0); + + if (gauge) + libreiserfs_gauge_free(gauge); + + if (!(fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem)))) + goto error_free_fs_info; + + fs->type = reiserfs_type; + fs->geom = fs_geom; + fs->type_specific = (void *) fs_info; + + return fs; + +error_free_fs_info: + ped_free(fs_info); +error_free_gauge: + if (gauge) + libreiserfs_gauge_free(gauge); +error_free_dal: + geom_dal_free(dal); +error_fs_geom_free: + ped_geometry_destroy(fs_geom); +error: + return NULL; +} + +static int reiserfs_close(PedFileSystem *fs) +{ + dal_t *dal; + + PED_ASSERT(fs != NULL, return 0); + + dal = reiserfs_fs_host_dal(fs->type_specific); + reiserfs_fs_close(fs->type_specific); + + geom_dal_free(dal); + ped_geometry_sync(fs->geom); + + ped_free(fs); + return 1; +} + +static PedConstraint *reiserfs_get_create_constraint(const PedDevice *dev) +{ + PedGeometry full_dev; + PedSector min_blks = (SUPER_OFFSET_IN_BYTES / DEFAULT_BLOCK_SIZE) + + 2 + DEFAULT_JOURNAL_SIZE + 1 + 100 + 1; + + 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, + min_blks * (DEFAULT_BLOCK_SIZE / 512), + dev->length); +} + +static int reiserfs_check(PedFileSystem *fs, PedTimer *timer) +{ + reiserfs_fs_t *fs_info; + reiserfs_gauge_t *gauge = NULL; + + PED_ASSERT(fs != NULL, return 0); + + fs_info = fs->type_specific; + + if (!reiserfs_fs_is_consistent(fs_info)) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system is in an invalid " + "state. Perhaps it is mounted?")); + return 0; + } + + if (!reiserfs_fs_is_resizeable(fs_info)) + ped_exception_throw(PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("The file system is in old " + "(unresizeable) format.")); + + if (!reiserfs_fs_bitmap_check(fs_info)) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Invalid free blocks count. Run " + "reiserfsck --check first.")); + return 0; + } + +#ifdef HAVE_REISERFS_FS_CHECK + ped_timer_reset(timer); + + if (libreiserfs_gauge_create && libreiserfs_gauge_free) { + if (! + (gauge = + libreiserfs_gauge_create(NULL, gauge_handler, timer))) + return 0; + } + + ped_timer_set_state_name(timer, _("checking")); + ped_timer_update(timer, 0.0); + + if (!reiserfs_fs_check(fs_info, gauge)) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Reiserfs tree seems to be corrupted. " + "Run reiserfsck --check first.")); + return 0; + } + + ped_timer_update(timer, 1.0); + + if (gauge) + libreiserfs_gauge_free(gauge); +#endif + + ped_exception_throw(PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK, + _("The reiserfs file system passed a basic check. " + "For a more comprehensive check, run " + "reiserfsck --check.")); + + return 1; +} + +static int reiserfs_resize(PedFileSystem *fs, PedGeometry *geom, + PedTimer *timer) +{ + dal_t *dal; + blk_t fs_len; + PedSector old_length; + reiserfs_fs_t *fs_info; + reiserfs_gauge_t *gauge = NULL; + + PED_ASSERT(fs != NULL, return 0); + + old_length = fs->geom->length; + + PED_ASSERT (fs->geom->dev == geom->dev, return 0); + + if (fs->geom->start != geom->start) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Sorry, can't move the start of " + "reiserfs partitions yet.")); + return 0; + } + + fs_info = fs->type_specific; + + fs_len = (blk_t) (geom->length / (reiserfs_fs_block_size(fs_info) / + PED_SECTOR_SIZE_DEFAULT)); + + dal = reiserfs_fs_host_dal(fs_info); + + if (dal_flags(dal) && O_RDONLY) { + if (!geom_dal_reopen(dal, O_RDWR)) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Couldn't reopen device " + "abstraction layer for " + "read/write.")); + return 0; + } + } + + ped_timer_reset(timer); + + if (libreiserfs_gauge_create && libreiserfs_gauge_free) { + if (! + (gauge = + libreiserfs_gauge_create(NULL, gauge_handler, timer))) + return 0; + } + + if (old_length > geom->length) { + + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + + if (!reiserfs_fs_resize(fs_info, fs_len, gauge)) + goto error_free_gauge; + + ped_geometry_set_end (fs->geom, geom->end); + dal_realize(dal); + } else { + ped_geometry_set_end (fs->geom, geom->end); + dal_realize(dal); + + ped_timer_set_state_name(timer, _("expanding")); + ped_timer_update(timer, 0.0); + + if (!reiserfs_fs_resize(fs_info, fs_len, gauge)) + goto error_free_gauge; + } + + ped_timer_update(timer, 1.0); + + if (gauge) + libreiserfs_gauge_free(gauge); + + return 1; + +error_free_gauge: + if (gauge) + libreiserfs_gauge_free(gauge); +error: + ped_geometry_set_end (fs->geom, fs->geom->start + old_length - 1); + return 0; +} + +static PedConstraint *reiserfs_get_resize_constraint(const PedFileSystem * + fs) +{ + PedDevice *dev; + PedSector min_size; + PedGeometry full_disk; + reiserfs_fs_t *fs_info; + PedAlignment start_align; + PedGeometry start_sector; + + PED_ASSERT(fs != NULL, return NULL); + + fs_info = fs->type_specific; + dev = fs->geom->dev; + + if (!ped_alignment_init(&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init(&full_disk, dev, 0, dev->length - 1)) + return NULL; + if (!ped_geometry_init(&start_sector, dev, fs->geom->start, 1)) + return NULL; + + /* + Minsize for reiserfs is area occupied by data blocks and + metadata blocks minus free space blocks and minus bitmap + blocks which describes free space blocks. + */ + min_size = reiserfs_fs_min_size(fs_info) * + (reiserfs_fs_block_size(fs_info) / PED_SECTOR_SIZE_DEFAULT); + + return ped_constraint_new(&start_align, ped_alignment_any, + &start_sector, &full_disk, min_size, + dev->length); +} + +static PedFileSystem *reiserfs_copy(const PedFileSystem *fs, + PedGeometry *geom, PedTimer *timer) +{ + dal_t *dal; + PedGeometry *fs_geom; + PedFileSystem *new_fs; + blk_t fs_len, min_needed_blk; + + reiserfs_fs_t *dest_fs, *src_fs; + reiserfs_gauge_t *gauge = NULL; + + fs_geom = ped_geometry_duplicate(geom); + + if (!(dal = geom_dal_create(fs_geom, DEFAULT_BLOCK_SIZE, O_RDWR))) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Couldn't create reiserfs device " + "abstraction handler.")); + goto error_free_fs_geom; + } + + src_fs = fs->type_specific; + + fs_len = + (geom->length / (reiserfs_fs_block_size(src_fs) / PED_SECTOR_SIZE_DEFAULT)); + min_needed_blk = reiserfs_fs_bitmap_used(src_fs); + + if (fs_len <= min_needed_blk) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Device is too small for %lu blocks."), + min_needed_blk); + goto error_free_dal; + } + + if (! (new_fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem)))) + goto error_free_dal; + + ped_timer_reset(timer); + ped_timer_set_state_name(timer, _("copying")); + ped_timer_update(timer, 0.0); + + if (libreiserfs_gauge_create && libreiserfs_gauge_free) { + if (! (gauge = + libreiserfs_gauge_create(NULL, gauge_handler, timer))) + goto error_free_new_fs; + } + + if (!(dest_fs = reiserfs_fs_copy(src_fs, dal, gauge))) + goto error_free_gauge; + + ped_timer_update(timer, 1.0); + + if (gauge) + libreiserfs_gauge_free(gauge); + + new_fs->type = reiserfs_type; + new_fs->geom = fs_geom; + new_fs->type_specific = (void *) dest_fs; + + return new_fs; + +error_free_gauge: + if (gauge) + libreiserfs_gauge_free(gauge); +error_free_new_fs: + ped_free(new_fs); +error_free_dal: + geom_dal_free(dal); +error_free_fs_geom: + ped_geometry_destroy(fs_geom); +error: + return NULL; +} + +static PedConstraint *reiserfs_get_copy_constraint(const PedFileSystem *fs, + const PedDevice *dev) +{ + PedGeometry full_dev; + + PED_ASSERT(fs != NULL, return NULL); + PED_ASSERT(dev != NULL, return NULL); + + 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, + reiserfs_fs_bitmap_used(fs->type_specific), + dev->length); +} + +#endif /* !REISER_FULL_SUPPORT */ + +#ifdef DYNAMIC_LOADING + +#define INIT_SYM(SYM) SYM = getsym (libreiserfs_handle, #SYM) + +static void *getsym(void *handle, const char *symbol) +{ + void *entry; + char *error; + + entry = dlsym(handle, symbol); + if ((error = dlerror()) != NULL) { + ped_exception_throw(PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("Couldn't resolve symbol %s. " + "Error: %s."), + symbol, error); + return NULL; + } + + return entry; +} + +static int reiserfs_ops_interface_version_check(void) +{ + int min_interface_version, max_interface_version; + int (*libreiserfs_get_max_interface_version) (void); + int (*libreiserfs_get_min_interface_version) (void); + + INIT_SYM(libreiserfs_get_max_interface_version); + INIT_SYM(libreiserfs_get_min_interface_version); + + if (!libreiserfs_get_min_interface_version || + !libreiserfs_get_max_interface_version) { + ped_exception_throw( + PED_EXCEPTION_WARNING, PED_EXCEPTION_CANCEL, + _("GNU Parted found an invalid libreiserfs library.")); + return 0; + } + + min_interface_version = libreiserfs_get_min_interface_version(); + max_interface_version = libreiserfs_get_max_interface_version(); + + if (REISERFS_API_VERSION < min_interface_version || + REISERFS_API_VERSION > max_interface_version) { + ped_exception_throw( + PED_EXCEPTION_WARNING, PED_EXCEPTION_CANCEL, + _("GNU Parted has detected libreiserfs interface " + "version mismatch. Found %d-%d, required %d. " + "ReiserFS support will be disabled."), + min_interface_version, + max_interface_version, + REISERFS_API_VERSION); + return 0; + } + + return 1; +} + +static int reiserfs_ops_init(void) +{ + if (!(libreiserfs_handle = dlopen("libreiserfs.so", RTLD_NOW))) + goto error; + + if (!reiserfs_ops_interface_version_check()) + goto error_free_libreiserfs_handle; + + if (!(libdal_handle = dlopen("libdal.so", RTLD_NOW))) + goto error_free_libreiserfs_handle; + + INIT_SYM(reiserfs_fs_probe); + INIT_SYM(libreiserfs_exception_type); + + INIT_SYM(libreiserfs_exception_option); + INIT_SYM(libreiserfs_exception_message); + INIT_SYM(libreiserfs_exception_set_handler); + + INIT_SYM(reiserfs_fs_clobber); + INIT_SYM(reiserfs_fs_open); + INIT_SYM(reiserfs_fs_create); + INIT_SYM(reiserfs_fs_resize); + INIT_SYM(reiserfs_fs_copy); + + INIT_SYM(reiserfs_fs_is_resizeable); + INIT_SYM(reiserfs_fs_is_consistent); + + INIT_SYM(reiserfs_fs_bitmap_check); + INIT_SYM(reiserfs_fs_bitmap_used); + + INIT_SYM(reiserfs_fs_min_size); + INIT_SYM(reiserfs_fs_block_size); + + INIT_SYM(reiserfs_fs_host_dal); + INIT_SYM(reiserfs_fs_close); + + INIT_SYM(libreiserfs_gauge_create); + INIT_SYM(libreiserfs_gauge_free); + + INIT_SYM(dal_realize); + INIT_SYM(dal_flags); + + INIT_SYM(dal_block_size); + INIT_SYM(dal_len); + + return 1; + +error_free_libreiserfs_handle: + dlclose(libreiserfs_handle); +error: + return 0; +} + +static void reiserfs_ops_done() +{ + if (libdal_handle) + dlclose(libdal_handle); + if (libreiserfs_handle) + dlclose(libreiserfs_handle); +} +#endif /* DYNAMIC_LOADING */ + +#define REISER_BLOCK_SIZES ((int[]){512, 1024, 2048, 4096, 8192, 0}) + +#ifdef REISER_FULL_SUPPORT +static PedFileSystemOps reiserfs_full_ops = { + probe: reiserfs_probe, + clobber: reiserfs_clobber, + open: reiserfs_open, + create: reiserfs_create, + close: reiserfs_close, + check: reiserfs_check, + copy: reiserfs_copy, + resize: reiserfs_resize, + get_create_constraint: reiserfs_get_create_constraint, + get_resize_constraint: reiserfs_get_resize_constraint, + get_copy_constraint: reiserfs_get_copy_constraint +}; + +static PedFileSystemType reiserfs_full_type = { + next: NULL, + ops: &reiserfs_full_ops, + name: "reiserfs", + block_sizes: REISER_BLOCK_SIZES +}; +#endif /* REISER_FULL_SUPPORT */ + +static PedFileSystemOps reiserfs_simple_ops = { + probe: reiserfs_probe, +#ifdef DISCOVER_ONLY + clobber: NULL, +#else + clobber: reiserfs_clobber, +#endif + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL +}; + +static PedFileSystemType reiserfs_simple_type = { + next: NULL, + ops: &reiserfs_simple_ops, + name: "reiserfs", + block_sizes: REISER_BLOCK_SIZES +}; + +void ped_file_system_reiserfs_init() +{ +#ifdef DYNAMIC_LOADING + libreiserfs_present = reiserfs_ops_init(); + if (libreiserfs_present) { + reiserfs_type = &reiserfs_full_type; + libreiserfs_exception_set_handler(exception_handler); + } else { + reiserfs_type = &reiserfs_simple_type; + } +#else /* !DYNAMIC_LOADING */ +#ifdef REISER_FULL_SUPPORT + libreiserfs_exception_set_handler(exception_handler); + reiserfs_type = &reiserfs_full_type; +#else + reiserfs_type = &reiserfs_simple_type; +#endif +#endif /* !DYNAMIC_LOADING */ + ped_file_system_type_register(reiserfs_type); +} + +void ped_file_system_reiserfs_done() +{ + ped_file_system_type_unregister(reiserfs_type); +#ifdef DYNAMIC_LOADING + reiserfs_ops_done(); +#endif /* DYNAMIC_LOADING */ +} + diff --git a/libparted/fs/reiserfs/reiserfs.h b/libparted/fs/reiserfs/reiserfs.h new file mode 100644 index 0000000..67bf9bf --- /dev/null +++ b/libparted/fs/reiserfs/reiserfs.h @@ -0,0 +1,109 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef REISERFS_H +#define REISERFS_H + +#define REISERFS_API_VERSION 0 + +#define REISERFS_SIGNATURE "ReIsErFs" +#define REISER2FS_SIGNATURE "ReIsEr2Fs" +#define REISER3FS_SIGNATURE "ReIsEr3Fs" + +#define DEFAULT_BLOCK_SIZE 4096 + +struct reiserfs_super_block { + uint32_t s_block_count; + uint32_t s_free_blocks; + uint32_t s_root_block; + uint32_t s_journal_block; + uint32_t s_journal_dev; + uint32_t s_orig_journal_size; + uint32_t s_journal_trans_max; + uint32_t s_journal_block_count; + uint32_t s_journal_max_batch; + uint32_t s_journal_max_commit_age; + uint32_t s_journal_max_trans_age; + uint16_t s_blocksize; + uint16_t s_oid_maxsize; + uint16_t s_oid_cursize; + uint16_t s_state; + char s_magic[10]; + uint16_t s_fsck_state; + uint32_t s_hash_function_code; + uint16_t s_tree_height; + uint16_t s_bmap_nr; + uint16_t s_version; + char padding[438]; +}; + +typedef struct reiserfs_super_block reiserfs_super_block_t; + +enum reiserfs_exception_type { + EXCEPTION_INFORMATION = 1, + EXCEPTION_WARNING = 2, + EXCEPTION_ERROR = 3, + EXCEPTION_FATAL = 4, + EXCEPTION_BUG = 5, + EXCEPTION_NO_FEATURE = 6 +}; + +typedef enum reiserfs_exception_type reiserfs_exception_type_t; + +enum reiserfs_exception_option { + EXCEPTION_UNHANDLED = 1 << 0, + EXCEPTION_FIX = 1 << 1, + EXCEPTION_YES = 1 << 2, + EXCEPTION_NO = 1 << 3, + EXCEPTION_OK = 1 << 4, + EXCEPTION_RETRY = 1 << 5, + EXCEPTION_IGNORE = 1 << 6, + EXCEPTION_CANCEL = 1 << 7 +}; + +typedef enum reiserfs_exception_option reiserfs_exception_option_t; + +typedef void (reiserfs_gauge_handler_t)(const char *, unsigned int, void *, int, int, int); + +typedef void * reiserfs_exception_t; +typedef void * reiserfs_gauge_t; +typedef void * reiserfs_fs_t; + +#define FS_FORMAT_3_5 0 +#define FS_FORMAT_3_6 2 + +#define SUPER_OFFSET_IN_BYTES 64*1024 + +#define DEFAULT_JOURNAL_SIZE 8192 + +#define JOURNAL_MIN_SIZE 512 +#define JOURNAL_MIN_TRANS 256 +#define JOURNAL_MAX_TRANS 1024 + +#define JOURNAL_DEF_RATIO 8 +#define JOURNAL_MIN_RATIO 2 +#define JOURNAL_MAX_BATCH 900 +#define JOURNAL_MAX_COMMIT_AGE 30 +#define JOURNAL_MAX_TRANS_AGE 30 + +#define TEA_HASH 1 +#define YURA_HASH 2 +#define R5_HASH 3 + +#endif diff --git a/libparted/fs/ufs/Makefile.am b/libparted/fs/ufs/Makefile.am new file mode 100644 index 0000000..b506bd7 --- /dev/null +++ b/libparted/fs/ufs/Makefile.am @@ -0,0 +1,6 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libufs.la +libufs_la_SOURCES = ufs.c + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/ufs/ufs.c b/libparted/fs/ufs/ufs.c new file mode 100644 index 0000000..6428e7d --- /dev/null +++ b/libparted/fs/ufs/ufs.c @@ -0,0 +1,325 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + Contributor: Ben Collins <bcollins@debian.org> +*/ + +#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> + +#define SUN_UFS_BLOCK_SIZES ((int[2]){512, 0}) +#define HP_UFS_BLOCK_SIZES ((int[2]){512, 0}) + + +/* taken from ufs_fs.h in Linux */ +#define UFS_MAXNAMLEN 255 +#define UFS_MAXMNTLEN 512 +#define UFS_MAXCSBUFS 31 +#define UFS_LINK_MAX 32000 + +#define UFS_MAGIC 0x00011954 +#define UFS_MAGIC_LFN 0x00095014 +#define UFS_MAGIC_FEA 0x00195612 +#define UFS_MAGIC_4GB 0x05231994 + +struct ufs_csum { + uint32_t cs_ndir; /* number of directories */ + uint32_t cs_nbfree; /* number of free blocks */ + uint32_t cs_nifree; /* number of free inodes */ + uint32_t cs_nffree; /* number of free frags */ +}; + +struct ufs_super_block { + uint32_t fs_link; /* UNUSED */ + uint32_t fs_rlink; /* UNUSED */ + uint32_t fs_sblkno; /* addr of super-block in filesys */ + uint32_t fs_cblkno; /* offset of cyl-block in filesys */ + uint32_t fs_iblkno; /* offset of inode-blocks in filesys */ + uint32_t fs_dblkno; /* offset of first data after cg */ + uint32_t fs_cgoffset; /* cylinder group offset in cylinder */ + uint32_t fs_cgmask; /* used to calc mod fs_ntrak */ + uint32_t fs_time; /* last time written -- time_t */ + uint32_t fs_size; /* number of blocks in fs */ + uint32_t fs_dsize; /* number of data blocks in fs */ + uint32_t fs_ncg; /* number of cylinder groups */ + uint32_t fs_bsize; /* size of basic blocks in fs */ + uint32_t fs_fsize; /* size of frag blocks in fs */ + uint32_t fs_frag; /* number of frags in a block in fs */ +/* these are configuration parameters */ + uint32_t fs_minfree; /* minimum percentage of free blocks */ + uint32_t fs_rotdelay; /* num of ms for optimal next block */ + uint32_t fs_rps; /* disk revolutions per second */ +/* these fields can be computed from the others */ + uint32_t fs_bmask; /* ``blkoff'' calc of blk offsets */ + uint32_t fs_fmask; /* ``fragoff'' calc of frag offsets */ + uint32_t fs_bshift; /* ``lblkno'' calc of logical blkno */ + uint32_t fs_fshift; /* ``numfrags'' calc number of frags */ +/* these are configuration parameters */ + uint32_t fs_maxcontig; /* max number of contiguous blks */ + uint32_t fs_maxbpg; /* max number of blks per cyl group */ +/* these fields can be computed from the others */ + uint32_t fs_fragshift; /* block to frag shift */ + uint32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ + uint32_t fs_sbsize; /* actual size of super block */ + uint32_t fs_csmask; /* csum block offset */ + uint32_t fs_csshift; /* csum block number */ + uint32_t fs_nindir; /* value of NINDIR */ + uint32_t fs_inopb; /* value of INOPB */ + uint32_t fs_nspf; /* value of NSPF */ +/* yet another configuration parameter */ + uint32_t fs_optim; /* optimization preference, see below */ +/* these fields are derived from the hardware */ + union { + struct { + uint32_t fs_npsect; /* # sectors/track including spares */ + } fs_sun; + struct { + int32_t fs_state; /* file system state time stamp */ + } fs_sunx86; + } fs_u1; + uint32_t fs_interleave; /* hardware sector interleave */ + uint32_t fs_trackskew; /* sector 0 skew, per track */ +/* a unique id for this file system (currently unused and unmaintained) */ +/* In 4.3 Tahoe this space is used by fs_headswitch and fs_trkseek */ +/* Neither of those fields is used in the Tahoe code right now but */ +/* there could be problems if they are. */ + uint32_t fs_id[2]; /* file system id */ +/* sizes determined by number of cylinder groups and their sizes */ + uint32_t fs_csaddr; /* blk addr of cyl grp summary area */ + uint32_t fs_cssize; /* size of cyl grp summary area */ + uint32_t fs_cgsize; /* cylinder group size */ +/* these fields are derived from the hardware */ + uint32_t fs_ntrak; /* tracks per cylinder */ + uint32_t fs_nsect; /* sectors per track */ + uint32_t fs_spc; /* sectors per cylinder */ +/* this comes from the disk driver partitioning */ + uint32_t fs_ncyl; /* cylinders in file system */ +/* these fields can be computed from the others */ + uint32_t fs_cpg; /* cylinders per group */ + uint32_t fs_ipg; /* inodes per group */ + uint32_t fs_fpg; /* blocks per group * fs_frag */ +/* this data must be re-computed after crashes */ + struct ufs_csum fs_cstotal; /* cylinder summary information */ +/* these fields are cleared at mount time */ + int8_t fs_fmod; /* super block modified flag */ + int8_t fs_clean; /* file system is clean flag */ + int8_t fs_ronly; /* mounted read-only flag */ + int8_t fs_flags; /* currently unused flag */ + int8_t fs_fsmnt[UFS_MAXMNTLEN]; /* name mounted on */ +/* these fields retain the current block allocation info */ + uint32_t fs_cgrotor; /* last cg searched */ + uint32_t fs_csp[UFS_MAXCSBUFS]; /* list of fs_cs info buffers */ + uint32_t fs_maxcluster; + uint32_t fs_cpc; /* cyl per cycle in postbl */ + uint16_t fs_opostbl[16][8]; /* old rotation block list head */ + union { + struct { + int32_t fs_sparecon[53];/* reserved for future constants */ + int32_t fs_reclaim; + int32_t fs_sparecon2[1]; + int32_t fs_state; /* file system state time stamp */ + uint32_t fs_qbmask[2]; /* ~usb_bmask */ + uint32_t fs_qfmask[2]; /* ~usb_fmask */ + } fs_sun; + struct { + int32_t fs_sparecon[53];/* reserved for future constants */ + int32_t fs_reclaim; + int32_t fs_sparecon2[1]; + uint32_t fs_npsect; /* # sectors/track including spares */ + uint32_t fs_qbmask[2]; /* ~usb_bmask */ + uint32_t fs_qfmask[2]; /* ~usb_fmask */ + } fs_sunx86; + struct { + int32_t fs_sparecon[50];/* reserved for future constants */ + int32_t fs_contigsumsize;/* size of cluster summary array */ + int32_t fs_maxsymlinklen;/* max length of an internal symlink */ + int32_t fs_inodefmt; /* format of on-disk inodes */ + uint32_t fs_maxfilesize[2]; /* max representable file size */ + uint32_t fs_qbmask[2]; /* ~usb_bmask */ + uint32_t fs_qfmask[2]; /* ~usb_fmask */ + int32_t fs_state; /* file system state time stamp */ + } fs_44; + } fs_u2; + int32_t fs_postblformat; /* format of positional layout tables */ + int32_t fs_nrpos; /* number of rotational positions */ + int32_t fs_postbloff; /* (__s16) rotation block list head */ + int32_t fs_rotbloff; /* (uint8_t) blocks for each rotation */ + int32_t fs_magic; /* magic number */ + uint8_t fs_space[4]; /* list of blocks for each rotation */ +}; + +static PedGeometry* +ufs_probe_sun (PedGeometry* geom) +{ + int8_t buf[512 * 3]; + struct ufs_super_block *sb; + + if (geom->length < 5) + return 0; + if (!ped_geometry_read (geom, buf, 16, 3)) + return 0; + + sb = (struct ufs_super_block *)buf; + + if (PED_BE32_TO_CPU(sb->fs_magic) == UFS_MAGIC) { + PedSector block_size = PED_BE32_TO_CPU(sb->fs_bsize) / 512; + PedSector block_count = PED_BE32_TO_CPU(sb->fs_size); + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + if (PED_LE32_TO_CPU(sb->fs_magic) == UFS_MAGIC) { + PedSector block_size = PED_LE32_TO_CPU(sb->fs_bsize) / 512; + PedSector block_count = PED_LE32_TO_CPU(sb->fs_size); + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + return NULL; +} + +static PedGeometry* +ufs_probe_hp (PedGeometry* geom) +{ + int8_t buf[1536]; + struct ufs_super_block *sb; + PedSector block_size; + PedSector block_count; + + if (geom->length < 5) + return 0; + if (!ped_geometry_read (geom, buf, 16, 3)) + return 0; + + sb = (struct ufs_super_block *)buf; + + /* Try sane bytesex */ + switch (PED_BE32_TO_CPU(sb->fs_magic)) { + case UFS_MAGIC_LFN: + case UFS_MAGIC_FEA: + case UFS_MAGIC_4GB: + block_size = PED_BE32_TO_CPU(sb->fs_bsize) / 512; + block_count = PED_BE32_TO_CPU(sb->fs_size); + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + + /* Try perverted bytesex */ + switch (PED_LE32_TO_CPU(sb->fs_magic)) { + case UFS_MAGIC_LFN: + case UFS_MAGIC_FEA: + case UFS_MAGIC_4GB: + block_size = PED_LE32_TO_CPU(sb->fs_bsize) / 512; + block_count = PED_LE32_TO_CPU(sb->fs_size); + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + return NULL; +} + +#ifndef DISCOVER_ONLY +static int +ufs_clobber (PedGeometry* geom) +{ + char buf[1536]; + + if (!ped_geometry_read (geom, buf, 16, 3)) + return 0; + + memset (buf, 0, sizeof(struct ufs_super_block)); + + return ped_geometry_write (geom, buf, 16, 3); +} +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps ufs_ops_sun = { + probe: ufs_probe_sun, +#ifndef DISCOVER_ONLY + clobber: ufs_clobber, +#else + clobber: NULL, +#endif + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL +}; + +static PedFileSystemOps ufs_ops_hp = { + probe: ufs_probe_hp, +#ifndef DISCOVER_ONLY + clobber: ufs_clobber, +#else + clobber: NULL, +#endif + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL +}; + +static PedFileSystemType ufs_type_sun = { + next: NULL, + ops: &ufs_ops_sun, + name: "sun-ufs", + block_sizes: SUN_UFS_BLOCK_SIZES +}; + +static PedFileSystemType ufs_type_hp = { + next: NULL, + ops: &ufs_ops_hp, + name: "hp-ufs", + block_sizes: HP_UFS_BLOCK_SIZES +}; + +void +ped_file_system_ufs_init () +{ + PED_ASSERT (sizeof (struct ufs_super_block) == 1380, return); + + ped_file_system_type_register (&ufs_type_sun); + ped_file_system_type_register (&ufs_type_hp); +} + +void +ped_file_system_ufs_done () +{ + ped_file_system_type_unregister (&ufs_type_hp); + ped_file_system_type_unregister (&ufs_type_sun); +} diff --git a/libparted/fs/xfs/Makefile.am b/libparted/fs/xfs/Makefile.am new file mode 100644 index 0000000..84ab926 --- /dev/null +++ b/libparted/fs/xfs/Makefile.am @@ -0,0 +1,6 @@ +partedincludedir = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libxfs.la +libxfs_la_SOURCES = xfs.c xfs_sb.h xfs_types.h platform_defs.h + +INCLUDES = $(partedincludedir) @INTLINCS@ diff --git a/libparted/fs/xfs/platform_defs.h b/libparted/fs/xfs/platform_defs.h new file mode 100644 index 0000000..a2b45b9 --- /dev/null +++ b/libparted/fs/xfs/platform_defs.h @@ -0,0 +1,115 @@ +/* include/platform_defs.h. Generated automatically by configure. */ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston MA 02110-1301, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + * + * @configure_input@ + */ +#ifndef __XFS_PLATFORM_DEFS_H__ +#define __XFS_PLATFORM_DEFS_H__ + +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <endian.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/types.h> + +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ <= 1) +# define constpp const char * const * +#else +# define constpp char * const * +#endif + +#ifdef __sparc__ +# ifndef O_DIRECT +# define O_DIRECT 0x100000 +# endif +#endif + +typedef loff_t xfs_off_t; +typedef uint64_t xfs_ino_t; +typedef uint32_t xfs_dev_t; +typedef int64_t xfs_daddr_t; +typedef char* xfs_caddr_t; + +/* long and pointer must be either 32 bit or 64 bit */ +/* #undef HAVE_64BIT_LONG */ +#define HAVE_32BIT_LONG 1 +#define HAVE_32BIT_PTR 1 +/* #undef HAVE_64BIT_PTR */ + +/* Check if __psint_t is set to something meaningful */ +/* #undef HAVE___PSINT_T */ +#ifndef HAVE___PSINT_T +# ifdef HAVE_32BIT_PTR +typedef int __psint_t; +# elif defined HAVE_64BIT_PTR +# ifdef HAVE_64BIT_LONG +typedef long __psint_t; +# else +/* This is a very strange architecture, which has 64 bit pointers but + * not 64 bit longs. So, I'd just punt here and assume long long is Ok */ +typedef long long __psint_t; +# endif +# else +# error Unknown pointer size +# endif +#endif + +/* Check if __psunsigned_t is set to something meaningful */ +/* #undef HAVE___PSUNSIGNED_T */ +#ifndef HAVE___PSUNSIGNED_T +# ifdef HAVE_32BIT_PTR +typedef unsigned int __psunsigned_t; +# elif defined HAVE_64BIT_PTR +# ifdef HAVE_64BIT_LONG +typedef long __psunsigned_t; +# else +/* This is a very strange architecture, which has 64 bit pointers but + * not 64 bit longs. So, I'd just punt here and assume long long is Ok */ +typedef unsigned long long __psunsigned_t; +# endif +# else +# error Unknown pointer size +# endif +#endif + +#ifdef DEBUG +# define ASSERT assert +#else +# define ASSERT(EX) ((void) 0) +#endif + +#endif /* __XFS_PLATFORM_DEFS_H__ */ diff --git a/libparted/fs/xfs/xfs.c b/libparted/fs/xfs/xfs.c new file mode 100644 index 0000000..e583442 --- /dev/null +++ b/libparted/fs/xfs/xfs.c @@ -0,0 +1,122 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <uuid/uuid.h> +#include "platform_defs.h" +#include "xfs_types.h" +#include "xfs_sb.h" + +#include <string.h> + +#define XFS_BLOCK_SIZES ((int[2]){512, 0}) + +static PedGeometry* +xfs_probe (PedGeometry* geom) +{ + PedSector block_size; + PedSector block_count; + union { + struct xfs_sb sb; + char bytes [512]; + } buf; + + if (geom->length < XFS_SB_DADDR + 1) + return NULL; + if (!ped_geometry_read (geom, &buf, XFS_SB_DADDR, 1)) + return NULL; + + if (PED_LE32_TO_CPU (buf.sb.sb_magicnum) == XFS_SB_MAGIC) { + block_size = PED_LE32_TO_CPU (buf.sb.sb_blocksize) / 512; + block_count = PED_LE64_TO_CPU (buf.sb.sb_dblocks); + + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + + if (PED_BE32_TO_CPU (buf.sb.sb_magicnum) == XFS_SB_MAGIC) { + block_size = PED_BE32_TO_CPU (buf.sb.sb_blocksize) / 512; + block_count = PED_BE64_TO_CPU (buf.sb.sb_dblocks); + + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + + return NULL; +} + +#ifndef DISCOVER_ONLY +static int +xfs_clobber (PedGeometry* geom) +{ + char buf[512]; + + memset (buf, 0, 512); + return ped_geometry_write (geom, buf, XFS_SB_DADDR, 1); +} +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps xfs_ops = { + probe: xfs_probe, +#ifndef DISCOVER_ONLY + clobber: xfs_clobber, +#else + clobber: NULL, +#endif + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL +}; + +static PedFileSystemType xfs_type = { + next: NULL, + ops: &xfs_ops, + name: "xfs", + block_sizes: XFS_BLOCK_SIZES +}; + +void +ped_file_system_xfs_init () +{ + ped_file_system_type_register (&xfs_type); +} + +void +ped_file_system_xfs_done () +{ + ped_file_system_type_unregister (&xfs_type); +} + diff --git a/libparted/fs/xfs/xfs_sb.h b/libparted/fs/xfs/xfs_sb.h new file mode 100644 index 0000000..1411c47 --- /dev/null +++ b/libparted/fs/xfs/xfs_sb.h @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston MA 02110-1301, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ +#ifndef __XFS_SB_H__ +#define __XFS_SB_H__ + +/* + * Super block + * Fits into a 512-byte buffer at daddr_t 0 of each allocation group. + * Only the first of these is ever updated except during growfs. + */ + +struct xfs_buf; +struct xfs_mount; + +#define XFS_SB_MAGIC 0x58465342 /* 'XFSB' */ +#define XFS_SB_VERSION_1 1 /* 5.3, 6.0.1, 6.1 */ +#define XFS_SB_VERSION_2 2 /* 6.2 - attributes */ +#define XFS_SB_VERSION_3 3 /* 6.2 - new inode version */ +#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */ +#define XFS_SB_VERSION_NUMBITS 0x000f +#define XFS_SB_VERSION_ALLFBITS 0xfff0 +#define XFS_SB_VERSION_SASHFBITS 0xf000 +#define XFS_SB_VERSION_REALFBITS 0x0ff0 +#define XFS_SB_VERSION_ATTRBIT 0x0010 +#define XFS_SB_VERSION_NLINKBIT 0x0020 +#define XFS_SB_VERSION_QUOTABIT 0x0040 +#define XFS_SB_VERSION_ALIGNBIT 0x0080 +#define XFS_SB_VERSION_DALIGNBIT 0x0100 +#define XFS_SB_VERSION_SHAREDBIT 0x0200 +#define XFS_SB_VERSION_EXTFLGBIT 0x1000 +#define XFS_SB_VERSION_DIRV2BIT 0x2000 +#define XFS_SB_VERSION_OKSASHFBITS \ + (XFS_SB_VERSION_EXTFLGBIT | \ + XFS_SB_VERSION_DIRV2BIT) +#define XFS_SB_VERSION_OKREALFBITS \ + (XFS_SB_VERSION_ATTRBIT | \ + XFS_SB_VERSION_NLINKBIT | \ + XFS_SB_VERSION_QUOTABIT | \ + XFS_SB_VERSION_ALIGNBIT | \ + XFS_SB_VERSION_DALIGNBIT | \ + XFS_SB_VERSION_SHAREDBIT) +#define XFS_SB_VERSION_OKSASHBITS \ + (XFS_SB_VERSION_NUMBITS | \ + XFS_SB_VERSION_REALFBITS | \ + XFS_SB_VERSION_OKSASHFBITS) +#define XFS_SB_VERSION_OKREALBITS \ + (XFS_SB_VERSION_NUMBITS | \ + XFS_SB_VERSION_OKREALFBITS | \ + XFS_SB_VERSION_OKSASHFBITS) +#define XFS_SB_VERSION_MKFS(ia,dia,extflag,dirv2) \ + (((ia) || (dia) || (extflag) || (dirv2)) ? \ + (XFS_SB_VERSION_4 | \ + ((ia) ? XFS_SB_VERSION_ALIGNBIT : 0) | \ + ((dia) ? XFS_SB_VERSION_DALIGNBIT : 0) | \ + ((extflag) ? XFS_SB_VERSION_EXTFLGBIT : 0) | \ + ((dirv2) ? XFS_SB_VERSION_DIRV2BIT : 0)) : \ + XFS_SB_VERSION_1) + +typedef struct xfs_sb +{ + uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ + uint32_t sb_blocksize; /* logical block size, bytes */ + xfs_drfsbno_t sb_dblocks; /* number of data blocks */ + xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */ + xfs_drtbno_t sb_rextents; /* number of realtime extents */ + uuid_t sb_uuid; /* file system unique id */ + xfs_dfsbno_t sb_logstart; /* starting block of log if internal */ + xfs_ino_t sb_rootino; /* root inode number */ + xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */ + xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */ + xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */ + xfs_agblock_t sb_agblocks; /* size of an allocation group */ + xfs_agnumber_t sb_agcount; /* number of allocation groups */ + xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */ + xfs_extlen_t sb_logblocks; /* number of log blocks */ + uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ + uint16_t sb_sectsize; /* volume sector size, bytes */ + uint16_t sb_inodesize; /* inode size, bytes */ + uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + uint8_t sb_blocklog; /* log2 of sb_blocksize */ + uint8_t sb_sectlog; /* log2 of sb_sectsize */ + uint8_t sb_inodelog; /* log2 of sb_inodesize */ + uint8_t sb_inopblog; /* log2 of sb_inopblock */ + uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + uint8_t sb_rextslog; /* log2 of sb_rextents */ + uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + /* + * These fields must remain contiguous. If you really + * want to change their layout, make sure you fix the + * code in xfs_trans_apply_sb_deltas(). + */ + uint64_t sb_icount; /* allocated inodes */ + uint64_t sb_ifree; /* free inodes */ + uint64_t sb_fdblocks; /* free data blocks */ + uint64_t sb_frextents; /* free realtime extents */ + /* + * End contiguous fields. + */ + xfs_ino_t sb_uquotino; /* user quota inode */ + xfs_ino_t sb_gquotino; /* group quota inode */ + uint16_t sb_qflags; /* quota flags */ + uint8_t sb_flags; /* misc. flags */ + uint8_t sb_shared_vn; /* shared version number */ + xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */ + uint32_t sb_unit; /* stripe or raid unit */ + uint32_t sb_width; /* stripe or raid width */ + uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */ + uint8_t sb_dummy[7]; /* padding */ +} xfs_sb_t; + +/* + * Sequence number values for the fields. + */ +typedef enum { + XFS_SBS_MAGICNUM, XFS_SBS_BLOCKSIZE, XFS_SBS_DBLOCKS, XFS_SBS_RBLOCKS, + XFS_SBS_REXTENTS, XFS_SBS_UUID, XFS_SBS_LOGSTART, XFS_SBS_ROOTINO, + XFS_SBS_RBMINO, XFS_SBS_RSUMINO, XFS_SBS_REXTSIZE, XFS_SBS_AGBLOCKS, + XFS_SBS_AGCOUNT, XFS_SBS_RBMBLOCKS, XFS_SBS_LOGBLOCKS, + XFS_SBS_VERSIONNUM, XFS_SBS_SECTSIZE, XFS_SBS_INODESIZE, + XFS_SBS_INOPBLOCK, XFS_SBS_FNAME, XFS_SBS_BLOCKLOG, + XFS_SBS_SECTLOG, XFS_SBS_INODELOG, XFS_SBS_INOPBLOG, XFS_SBS_AGBLKLOG, + XFS_SBS_REXTSLOG, XFS_SBS_INPROGRESS, XFS_SBS_IMAX_PCT, XFS_SBS_ICOUNT, + XFS_SBS_IFREE, XFS_SBS_FDBLOCKS, XFS_SBS_FREXTENTS, XFS_SBS_UQUOTINO, + XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN, + XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG, + XFS_SBS_DUMMY, + XFS_SBS_FIELDCOUNT +} xfs_sb_field_t; + +/* + * Mask values, defined based on the xfs_sb_field_t values. + * Only define the ones we're using. + */ +#define XFS_SB_MVAL(x) (1LL << XFS_SBS_ ## x) +#define XFS_SB_UUID XFS_SB_MVAL(UUID) +#define XFS_SB_FNAME XFS_SB_MVAL(FNAME) +#define XFS_SB_ROOTINO XFS_SB_MVAL(ROOTINO) +#define XFS_SB_RBMINO XFS_SB_MVAL(RBMINO) +#define XFS_SB_RSUMINO XFS_SB_MVAL(RSUMINO) +#define XFS_SB_VERSIONNUM XFS_SB_MVAL(VERSIONNUM) +#define XFS_SB_UQUOTINO XFS_SB_MVAL(UQUOTINO) +#define XFS_SB_GQUOTINO XFS_SB_MVAL(GQUOTINO) +#define XFS_SB_QFLAGS XFS_SB_MVAL(QFLAGS) +#define XFS_SB_SHARED_VN XFS_SB_MVAL(SHARED_VN) +#define XFS_SB_UNIT XFS_SB_MVAL(UNIT) +#define XFS_SB_WIDTH XFS_SB_MVAL(WIDTH) +#define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT) +#define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1) +#define XFS_SB_MOD_BITS \ + (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \ + XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \ + XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH) + +/* + * Misc. Flags - warning - these will be cleared by xfs_repair unless + * a feature bit is set when the flag is used. + */ +#define XFS_SBF_NOFLAGS 0x00 /* no flags set */ +#define XFS_SBF_READONLY 0x01 /* only read-only mounts allowed */ + +/* + * define max. shared version we can interoperate with + */ +#define XFS_SB_MAX_SHARED_VN 0 + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_NUM) +int xfs_sb_version_num(xfs_sb_t *sbp); +#define XFS_SB_VERSION_NUM(sbp) xfs_sb_version_num(sbp) +#else +#define XFS_SB_VERSION_NUM(sbp) ((sbp)->sb_versionnum & XFS_SB_VERSION_NUMBITS) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_GOOD_VERSION) +int xfs_sb_good_version(xfs_sb_t *sbp); +#define XFS_SB_GOOD_VERSION(sbp) xfs_sb_good_version(sbp) +#else +#define XFS_SB_GOOD_VERSION_INT(sbp) \ + ((((sbp)->sb_versionnum >= XFS_SB_VERSION_1) && \ + ((sbp)->sb_versionnum <= XFS_SB_VERSION_3)) || \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + !((sbp)->sb_versionnum & ~XFS_SB_VERSION_OKREALBITS) +#ifdef __KERNEL__ +#define XFS_SB_GOOD_VERSION(sbp) \ + (XFS_SB_GOOD_VERSION_INT(sbp) && \ + (sbp)->sb_shared_vn <= XFS_SB_MAX_SHARED_VN) )) +#else +/* + * extra 2 paren's here (( to unconfuse paren-matching editors + * like vi because XFS_SB_GOOD_VERSION_INT is a partial expression + * and the two XFS_SB_GOOD_VERSION's each 2 more close paren's to + * complete the expression. + */ +#define XFS_SB_GOOD_VERSION(sbp) \ + (XFS_SB_GOOD_VERSION_INT(sbp) && \ + (!((sbp)->sb_versionnum & XFS_SB_VERSION_SHAREDBIT) || \ + (sbp)->sb_shared_vn <= XFS_SB_MAX_SHARED_VN)) )) +#endif /* __KERNEL__ */ +#endif + +#define XFS_SB_GOOD_SASH_VERSION(sbp) \ + ((((sbp)->sb_versionnum >= XFS_SB_VERSION_1) && \ + ((sbp)->sb_versionnum <= XFS_SB_VERSION_3)) || \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + !((sbp)->sb_versionnum & ~XFS_SB_VERSION_OKSASHBITS))) + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_TONEW) +unsigned xfs_sb_version_tonew(unsigned v); +#define XFS_SB_VERSION_TONEW(v) xfs_sb_version_tonew(v) +#else +#define XFS_SB_VERSION_TONEW(v) \ + ((((v) == XFS_SB_VERSION_1) ? \ + 0 : \ + (((v) == XFS_SB_VERSION_2) ? \ + XFS_SB_VERSION_ATTRBIT : \ + (XFS_SB_VERSION_ATTRBIT | XFS_SB_VERSION_NLINKBIT))) | \ + XFS_SB_VERSION_4) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_TOOLD) +unsigned xfs_sb_version_toold(unsigned v); +#define XFS_SB_VERSION_TOOLD(v) xfs_sb_version_toold(v) +#else +#define XFS_SB_VERSION_TOOLD(v) \ + (((v) & (XFS_SB_VERSION_QUOTABIT | XFS_SB_VERSION_ALIGNBIT)) ? \ + 0 : \ + (((v) & XFS_SB_VERSION_NLINKBIT) ? \ + XFS_SB_VERSION_3 : \ + (((v) & XFS_SB_VERSION_ATTRBIT) ? \ + XFS_SB_VERSION_2 : \ + XFS_SB_VERSION_1))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASATTR) +int xfs_sb_version_hasattr(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASATTR(sbp) xfs_sb_version_hasattr(sbp) +#else +#define XFS_SB_VERSION_HASATTR(sbp) \ + (((sbp)->sb_versionnum == XFS_SB_VERSION_2) || \ + ((sbp)->sb_versionnum == XFS_SB_VERSION_3) || \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_ATTRBIT))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDATTR) +void xfs_sb_version_addattr(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDATTR(sbp) xfs_sb_version_addattr(sbp) +#else +#define XFS_SB_VERSION_ADDATTR(sbp) \ + ((sbp)->sb_versionnum = \ + (((sbp)->sb_versionnum == XFS_SB_VERSION_1) ? \ + XFS_SB_VERSION_2 : \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) ? \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_ATTRBIT) : \ + (XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT)))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASNLINK) +int xfs_sb_version_hasnlink(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASNLINK(sbp) xfs_sb_version_hasnlink(sbp) +#else +#define XFS_SB_VERSION_HASNLINK(sbp) \ + (((sbp)->sb_versionnum == XFS_SB_VERSION_3) || \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_NLINKBIT))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDNLINK) +void xfs_sb_version_addnlink(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDNLINK(sbp) xfs_sb_version_addnlink(sbp) +#else +#define XFS_SB_VERSION_ADDNLINK(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum <= XFS_SB_VERSION_2 ? \ + XFS_SB_VERSION_3 : \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_NLINKBIT))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASQUOTA) +int xfs_sb_version_hasquota(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASQUOTA(sbp) xfs_sb_version_hasquota(sbp) +#else +#define XFS_SB_VERSION_HASQUOTA(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_QUOTABIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDQUOTA) +void xfs_sb_version_addquota(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDQUOTA(sbp) xfs_sb_version_addquota(sbp) +#else +#define XFS_SB_VERSION_ADDQUOTA(sbp) \ + ((sbp)->sb_versionnum = \ + (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 ? \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_QUOTABIT) : \ + (XFS_SB_VERSION_TONEW((sbp)->sb_versionnum) | \ + XFS_SB_VERSION_QUOTABIT))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASALIGN) +int xfs_sb_version_hasalign(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASALIGN(sbp) xfs_sb_version_hasalign(sbp) +#else +#define XFS_SB_VERSION_HASALIGN(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_ALIGNBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBALIGN) +void xfs_sb_version_subalign(xfs_sb_t *sbp); +#define XFS_SB_VERSION_SUBALIGN(sbp) xfs_sb_version_subalign(sbp) +#else +#define XFS_SB_VERSION_SUBALIGN(sbp) \ + ((sbp)->sb_versionnum = \ + XFS_SB_VERSION_TOOLD((sbp)->sb_versionnum & ~XFS_SB_VERSION_ALIGNBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASDALIGN) +int xfs_sb_version_hasdalign(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASDALIGN(sbp) xfs_sb_version_hasdalign(sbp) +#else +#define XFS_SB_VERSION_HASDALIGN(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_DALIGNBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDDALIGN) +int xfs_sb_version_adddalign(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDDALIGN(sbp) xfs_sb_version_adddalign(sbp) +#else +#define XFS_SB_VERSION_ADDDALIGN(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_DALIGNBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASSHARED) +int xfs_sb_version_hasshared(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASSHARED(sbp) xfs_sb_version_hasshared(sbp) +#else +#define XFS_SB_VERSION_HASSHARED(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_SHAREDBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDSHARED) +int xfs_sb_version_addshared(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDSHARED(sbp) xfs_sb_version_addshared(sbp) +#else +#define XFS_SB_VERSION_ADDSHARED(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_SHAREDBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBSHARED) +int xfs_sb_version_subshared(xfs_sb_t *sbp); +#define XFS_SB_VERSION_SUBSHARED(sbp) xfs_sb_version_subshared(sbp) +#else +#define XFS_SB_VERSION_SUBSHARED(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum & ~XFS_SB_VERSION_SHAREDBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASDIRV2) +int xfs_sb_version_hasdirv2(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASDIRV2(sbp) xfs_sb_version_hasdirv2(sbp) +#else +#define XFS_SB_VERSION_HASDIRV2(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_DIRV2BIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASEXTFLGBIT) +int xfs_sb_version_hasextflgbit(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASEXTFLGBIT(sbp) xfs_sb_version_hasextflgbit(sbp) +#else +#define XFS_SB_VERSION_HASEXTFLGBIT(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDEXTFLGBIT) +int xfs_sb_version_addextflgbit(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDEXTFLGBIT(sbp) xfs_sb_version_addextflgbit(sbp) +#else +#define XFS_SB_VERSION_ADDEXTFLGBIT(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_EXTFLGBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBEXTFLGBIT) +int xfs_sb_version_subextflgbit(xfs_sb_t *sbp); +#define XFS_SB_VERSION_SUBEXTFLGBIT(sbp) xfs_sb_version_subextflgbit(sbp) +#else +#define XFS_SB_VERSION_SUBEXTFLGBIT(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum & ~XFS_SB_VERSION_EXTFLGBIT)) +#endif + +/* + * end of superblock version macros + */ + +#define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in file system/ag */ +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_BLOCK) +xfs_agblock_t xfs_sb_block(struct xfs_mount *mp); +#define XFS_SB_BLOCK(mp) xfs_sb_block(mp) +#else +#define XFS_SB_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_SB_DADDR) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_HDR_BLOCK) +xfs_agblock_t xfs_hdr_block(struct xfs_mount *mp, xfs_daddr_t d); +#define XFS_HDR_BLOCK(mp,d) xfs_hdr_block(mp,d) +#else +#define XFS_HDR_BLOCK(mp,d) ((xfs_agblock_t)(XFS_BB_TO_FSBT(mp,d))) +#endif +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DADDR_TO_FSB) +xfs_fsblock_t xfs_daddr_to_fsb(struct xfs_mount *mp, xfs_daddr_t d); +#define XFS_DADDR_TO_FSB(mp,d) xfs_daddr_to_fsb(mp,d) +#else +#define XFS_DADDR_TO_FSB(mp,d) \ + XFS_AGB_TO_FSB(mp, XFS_DADDR_TO_AGNO(mp,d), XFS_DADDR_TO_AGBNO(mp,d)) +#endif +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FSB_TO_DADDR) +xfs_daddr_t xfs_fsb_to_daddr(struct xfs_mount *mp, xfs_fsblock_t fsbno); +#define XFS_FSB_TO_DADDR(mp,fsbno) xfs_fsb_to_daddr(mp,fsbno) +#else +#define XFS_FSB_TO_DADDR(mp,fsbno) \ + XFS_AGB_TO_DADDR(mp, XFS_FSB_TO_AGNO(mp,fsbno), \ + XFS_FSB_TO_AGBNO(mp,fsbno)) +#endif + +/* + * File system block to basic block conversions. + */ +#define XFS_FSB_TO_BB(mp,fsbno) ((fsbno) << (mp)->m_blkbb_log) +#define XFS_BB_TO_FSB(mp,bb) \ + (((bb) + (XFS_FSB_TO_BB(mp,1) - 1)) >> (mp)->m_blkbb_log) +#define XFS_BB_TO_FSBT(mp,bb) ((bb) >> (mp)->m_blkbb_log) +#define XFS_BB_FSB_OFFSET(mp,bb) ((bb) & ((mp)->m_bsize - 1)) + +/* + * File system block to byte conversions. + */ +#define XFS_FSB_TO_B(mp,fsbno) ((xfs_fsize_t)(fsbno) << \ + (mp)->m_sb.sb_blocklog) +#define XFS_B_TO_FSB(mp,b) \ + ((((uint64_t)(b)) + (mp)->m_blockmask) >> (mp)->m_sb.sb_blocklog) +#define XFS_B_TO_FSBT(mp,b) (((uint64_t)(b)) >> (mp)->m_sb.sb_blocklog) +#define XFS_B_FSB_OFFSET(mp,b) ((b) & (mp)->m_blockmask) + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_SBP) +xfs_sb_t *xfs_buf_to_sbp(struct xfs_buf *bp); +#define XFS_BUF_TO_SBP(bp) xfs_buf_to_sbp(bp) +#else +#define XFS_BUF_TO_SBP(bp) ((xfs_sb_t *)XFS_BUF_PTR(bp)) +#endif + +#endif /* __XFS_SB_H__ */ diff --git a/libparted/fs/xfs/xfs_types.h b/libparted/fs/xfs/xfs_types.h new file mode 100644 index 0000000..e62dd53 --- /dev/null +++ b/libparted/fs/xfs/xfs_types.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston MA 02110-1301, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ +#ifndef __XFS_TYPES_H__ +#define __XFS_TYPES_H__ + +/* + * Some types are conditional based on the selected configuration. + * Set XFS_BIG_FILES=1 or 0 and XFS_BIG_FILESYSTEMS=1 or 0 depending + * on the desired configuration. + * XFS_BIG_FILES needs pgno_t to be 64 bits (64-bit kernels). + * XFS_BIG_FILESYSTEMS needs daddr_t to be 64 bits (N32 and 64-bit kernels). + * + * Expect these to be set from klocaldefs, or from the machine-type + * defs files for the normal case. + */ + +#define XFS_BIG_FILES 1 +#define XFS_BIG_FILESYSTEMS 1 + +typedef uint32_t xfs_agblock_t; /* blockno in alloc. group */ +typedef uint32_t xfs_extlen_t; /* extent length in blocks */ +typedef uint32_t xfs_agnumber_t; /* allocation group number */ +typedef int32_t xfs_extnum_t; /* # of extents in a file */ +typedef int16_t xfs_aextnum_t; /* # extents in an attribute fork */ +typedef int64_t xfs_fsize_t; /* bytes in a file */ +typedef uint64_t xfs_ufsize_t; /* unsigned bytes in a file */ + +typedef int32_t xfs_suminfo_t; /* type of bitmap summary info */ +typedef int32_t xfs_rtword_t; /* word type for bitmap manipulations */ + +typedef int64_t xfs_lsn_t; /* log sequence number */ +typedef int32_t xfs_tid_t; /* transaction identifier */ + +typedef uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ +typedef uint32_t xfs_dahash_t; /* dir/attr hash value */ + +typedef uint16_t xfs_prid_t; /* prid_t truncated to 16bits in XFS */ + +/* + * These types are 64 bits on disk but are either 32 or 64 bits in memory. + * Disk based types: + */ +typedef uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */ +typedef uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */ +typedef uint64_t xfs_drtbno_t; /* extent (block) in realtime area */ +typedef uint64_t xfs_dfiloff_t; /* block number in a file */ +typedef uint64_t xfs_dfilblks_t; /* number of blocks in a file */ + +/* + * Memory based types are conditional. + */ +#if XFS_BIG_FILESYSTEMS +typedef uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef uint64_t xfs_rfsblock_t; /* blockno in filesystem (raw) */ +typedef uint64_t xfs_rtblock_t; /* extent (block) in realtime area */ +typedef int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */ +#else +typedef uint32_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef uint32_t xfs_rfsblock_t; /* blockno in filesystem (raw) */ +typedef uint32_t xfs_rtblock_t; /* extent (block) in realtime area */ +typedef int32_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */ +#endif +#if XFS_BIG_FILES +typedef uint64_t xfs_fileoff_t; /* block number in a file */ +typedef int64_t xfs_sfiloff_t; /* signed block number in a file */ +typedef uint64_t xfs_filblks_t; /* number of blocks in a file */ +#else +typedef uint32_t xfs_fileoff_t; /* block number in a file */ +typedef int32_t xfs_sfiloff_t; /* signed block number in a file */ +typedef uint32_t xfs_filblks_t; /* number of blocks in a file */ +#endif + +typedef uint8_t xfs_arch_t; /* architecutre of an xfs fs */ + +/* + * Null values for the types. + */ +#define NULLDFSBNO ((xfs_dfsbno_t)-1) +#define NULLDRFSBNO ((xfs_drfsbno_t)-1) +#define NULLDRTBNO ((xfs_drtbno_t)-1) +#define NULLDFILOFF ((xfs_dfiloff_t)-1) + +#define NULLFSBLOCK ((xfs_fsblock_t)-1) +#define NULLRFSBLOCK ((xfs_rfsblock_t)-1) +#define NULLRTBLOCK ((xfs_rtblock_t)-1) +#define NULLFILEOFF ((xfs_fileoff_t)-1) + +#define NULLAGBLOCK ((xfs_agblock_t)-1) +#define NULLAGNUMBER ((xfs_agnumber_t)-1) +#define NULLEXTNUM ((xfs_extnum_t)-1) + +#define NULLCOMMITLSN ((xfs_lsn_t)-1) + +/* + * Max values for extlen, extnum, aextnum. + */ +#define MAXEXTLEN ((xfs_extlen_t)0x001fffff) /* 21 bits */ +#define MAXEXTNUM ((xfs_extnum_t)0x7fffffff) /* signed int */ +#define MAXAEXTNUM ((xfs_aextnum_t)0x7fff) /* signed short */ + +/* + * MAXNAMELEN is the length (including the terminating null) of + * the longest permissible file (component) name. + */ +#define MAXNAMELEN 256 + +typedef enum { + XFS_LOOKUP_EQi, XFS_LOOKUP_LEi, XFS_LOOKUP_GEi +} xfs_lookup_t; + +typedef enum { + XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi, + XFS_BTNUM_MAX +} xfs_btnum_t; + + +#ifdef CONFIG_PROC_FS +/* + * XFS global statistics + */ +struct xfsstats { +# define XFSSTAT_END_EXTENT_ALLOC 4 + uint32_t xs_allocx; + uint32_t xs_allocb; + uint32_t xs_freex; + uint32_t xs_freeb; +# define XFSSTAT_END_ALLOC_BTREE (XFSSTAT_END_EXTENT_ALLOC+4) + uint32_t xs_abt_lookup; + uint32_t xs_abt_compare; + uint32_t xs_abt_insrec; + uint32_t xs_abt_delrec; +# define XFSSTAT_END_BLOCK_MAPPING (XFSSTAT_END_ALLOC_BTREE+7) + uint32_t xs_blk_mapr; + uint32_t xs_blk_mapw; + uint32_t xs_blk_unmap; + uint32_t xs_add_exlist; + uint32_t xs_del_exlist; + uint32_t xs_look_exlist; + uint32_t xs_cmp_exlist; +# define XFSSTAT_END_BLOCK_MAP_BTREE (XFSSTAT_END_BLOCK_MAPPING+4) + uint32_t xs_bmbt_lookup; + uint32_t xs_bmbt_compare; + uint32_t xs_bmbt_insrec; + uint32_t xs_bmbt_delrec; +# define XFSSTAT_END_DIRECTORY_OPS (XFSSTAT_END_BLOCK_MAP_BTREE+4) + uint32_t xs_dir_lookup; + uint32_t xs_dir_create; + uint32_t xs_dir_remove; + uint32_t xs_dir_getdents; +# define XFSSTAT_END_TRANSACTIONS (XFSSTAT_END_DIRECTORY_OPS+3) + uint32_t xs_trans_sync; + uint32_t xs_trans_async; + uint32_t xs_trans_empty; +# define XFSSTAT_END_INODE_OPS (XFSSTAT_END_TRANSACTIONS+7) + uint32_t xs_ig_attempts; + uint32_t xs_ig_found; + uint32_t xs_ig_frecycle; + uint32_t xs_ig_missed; + uint32_t xs_ig_dup; + uint32_t xs_ig_reclaims; + uint32_t xs_ig_attrchg; +# define XFSSTAT_END_LOG_OPS (XFSSTAT_END_INODE_OPS+5) + uint32_t xs_log_writes; + uint32_t xs_log_blocks; + uint32_t xs_log_noiclogs; + uint32_t xs_log_force; + uint32_t xs_log_force_sleep; +# define XFSSTAT_END_TAIL_PUSHING (XFSSTAT_END_LOG_OPS+10) + uint32_t xs_try_logspace; + uint32_t xs_sleep_logspace; + uint32_t xs_push_ail; + uint32_t xs_push_ail_success; + uint32_t xs_push_ail_pushbuf; + uint32_t xs_push_ail_pinned; + uint32_t xs_push_ail_locked; + uint32_t xs_push_ail_flushing; + uint32_t xs_push_ail_restarts; + uint32_t xs_push_ail_flush; +# define XFSSTAT_END_WRITE_CONVERT (XFSSTAT_END_TAIL_PUSHING+2) + uint32_t xs_xstrat_quick; + uint32_t xs_xstrat_split; +# define XFSSTAT_END_READ_WRITE_OPS (XFSSTAT_END_WRITE_CONVERT+2) + uint32_t xs_write_calls; + uint32_t xs_read_calls; +# define XFSSTAT_END_ATTRIBUTE_OPS (XFSSTAT_END_READ_WRITE_OPS+4) + uint32_t xs_attr_get; + uint32_t xs_attr_set; + uint32_t xs_attr_remove; + uint32_t xs_attr_list; +# define XFSSTAT_END_QUOTA_OPS (XFSSTAT_END_ATTRIBUTE_OPS+8) + uint32_t xs_qm_dqreclaims; + uint32_t xs_qm_dqreclaim_misses; + uint32_t xs_qm_dquot_dups; + uint32_t xs_qm_dqcachemisses; + uint32_t xs_qm_dqcachehits; + uint32_t xs_qm_dqwants; + uint32_t xs_qm_dqshake_reclaims; + uint32_t xs_qm_dqinact_reclaims; +# define XFSSTAT_END_INODE_CLUSTER (XFSSTAT_END_QUOTA_OPS+3) + uint32_t xs_iflush_count; + uint32_t xs_icluster_flushcnt; + uint32_t xs_icluster_flushinode; +# define XFSSTAT_END_VNODE_OPS (XFSSTAT_END_INODE_CLUSTER+8) + uint32_t vn_active; /* # vnodes not on free lists */ + uint32_t vn_alloc; /* # times vn_alloc called */ + uint32_t vn_get; /* # times vn_get called */ + uint32_t vn_hold; /* # times vn_hold called */ + uint32_t vn_rele; /* # times vn_rele called */ + uint32_t vn_reclaim; /* # times vn_reclaim called */ + uint32_t vn_remove; /* # times vn_remove called */ + uint32_t vn_free; /* # times vn_free called */ + struct xfsstats_xpc { + uint64_t xs_xstrat_bytes; + uint64_t xs_write_bytes; + uint64_t xs_read_bytes; + } xpc; +} xfsstats; + +# define XFS_STATS_INC(count) ( xfsstats.##count ++ ) +# define XFS_STATS_DEC(count) ( xfsstats.##count -- ) +# define XFS_STATS_ADD(count, inc) ( xfsstats.##count += (inc) ) +# define XFS_STATS64_INC(count) ( xfsstats.xpc.##count ++ ) +# define XFS_STATS64_ADD(count, inc) ( xfsstats.xpc.##count += (inc) ) +#else /* !CONFIG_PROC_FS */ +# define XFS_STATS_INC(count) +# define XFS_STATS_DEC(count) +# define XFS_STATS_ADD(count, inc) +# define XFS_STATS64_INC(count) +# define XFS_STATS64_ADD(count, inc) +#endif /* !CONFIG_PROC_FS */ + + +#ifdef __KERNEL__ + +/* juggle IRIX device numbers - still used in ondisk structures */ + +#define IRIX_DEV_BITSMAJOR 14 +#define IRIX_DEV_BITSMINOR 18 +#define IRIX_DEV_MAXMAJ 0x1ff +#define IRIX_DEV_MAXMIN 0x3ffff +#define IRIX_DEV_MAJOR(dev) ((int)(((unsigned)(dev)>>IRIX_DEV_BITSMINOR) \ + & IRIX_DEV_MAXMAJ)) +#define IRIX_DEV_MINOR(dev) ((int)((dev)&IRIX_DEV_MAXMIN)) +#define IRIX_MKDEV(major,minor) ((xfs_dev_t)(((major)<<IRIX_DEV_BITSMINOR) \ + | (minor&IRIX_DEV_MAXMIN))) + +#define IRIX_DEV_TO_KDEVT(dev) MKDEV(IRIX_DEV_MAJOR(dev),IRIX_DEV_MINOR(dev)) +#define IRIX_DEV_TO_DEVT(dev) ((IRIX_DEV_MAJOR(dev)<<8)|IRIX_DEV_MINOR(dev)) + +/* __psint_t is the same size as a pointer */ +#if (BITS_PER_LONG == 32) +typedef int32_t __psint_t; +typedef uint32_t __psunsigned_t; +#elif (BITS_PER_LONG == 64) +typedef int64_t __psint_t; +typedef uint64_t __psunsigned_t; +#else +#error BITS_PER_LONG must be 32 or 64 +#endif + + +/* + * struct for passing owner/requestor id + */ +typedef struct flid { +#ifdef CELL_CAPABLE + pid_t fl_pid; + sysid_t fl_sysid; +#endif +} flid_t; + +#endif /* __KERNEL__ */ + +#endif /* !__XFS_TYPES_H */ diff --git a/libparted/labels/Makefile.am b/libparted/labels/Makefile.am new file mode 100644 index 0000000..c8efbe4 --- /dev/null +++ b/libparted/labels/Makefile.am @@ -0,0 +1,35 @@ +# This file is part of GNU Parted +# Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +# +# This file may be modified and/or distributed without restriction. + +LIBS = @INTLLIBS@ @LIBS@ + +partedincludedir = -I$(top_srcdir)/include +noinst_LTLIBRARIES = liblabels.la +liblabels_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -release $(LT_RELEASE) + +liblabels_la_SOURCES = rdb.c \ + bsd.c \ + dasd.c \ + fdasd.c \ + fdash.h \ + vtoc.c \ + vtoc.h \ + efi_crc32.c \ + dos.c \ + dvh.h \ + dvh.c \ + gpt.c \ + loop.c \ + mac.c \ + pc98.c \ + sun.c \ + aix.c + + +liblabels_la_LIBADD = @OS_LIBS@ + +INCLUDES = $(partedincludedir) @INTLINCS@ + diff --git a/libparted/labels/aix.c b/libparted/labels/aix.c new file mode 100644 index 0000000..4d28b18 --- /dev/null +++ b/libparted/labels/aix.c @@ -0,0 +1,282 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contributor: Matt Wilson <msw@redhat.com> +*/ + +#include "config.h" + +#include <string.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +typedef struct { + unsigned int magic; /* expect AIX_LABEL_MAGIC */ + unsigned int fillbytes[127]; +} AixLabel; + +#define AIX_LABEL_MAGIC 0xc9c2d4c1 + +static PedDiskType aix_disk_type; + +static int +aix_probe (const PedDevice *dev) +{ + PedDiskType* disk_type; + AixLabel label; + int i; + + PED_ASSERT (dev != NULL, return 0); + + if (!ped_device_read (dev, &label, 0, 1)) + return 0; + + if (PED_BE32_TO_CPU (label.magic) != AIX_LABEL_MAGIC) + return 0; + + return 1; +} + +#ifndef DISCOVER_ONLY +static int +aix_clobber (PedDevice* dev) +{ + AixLabel label; + + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (aix_probe (dev), return 0); + + if (!ped_device_read (dev, &label, 0, 1)) + return 0; + + label.magic = 0; + return ped_device_write (dev, &label, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + +static PedDisk* +aix_alloc (const PedDevice* dev) +{ + PedDisk* disk; + + disk = _ped_disk_alloc (dev, &aix_disk_type); + if (!disk) + return NULL; + + return disk; +} + +static PedDisk* +aix_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + + new_disk = ped_disk_new_fresh (disk->dev, &aix_disk_type); + if (!new_disk) + return NULL; + + return new_disk; +} + +static void +aix_free (PedDisk *disk) +{ + _ped_disk_free (disk); +} + +static int +aix_read (PedDisk* disk) +{ + ped_disk_delete_all (disk); + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for reading AIX disk labels is " + "is not implemented yet.")); + return 0; +} + +#ifndef DISCOVER_ONLY +static int +aix_write (PedDisk* disk) +{ + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for writing AIX disk labels is " + "is not implemented yet.")); + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +aix_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for adding partitions to AIX disk " + "labels is not implemented yet.")); + return NULL; +} + +static PedPartition* +aix_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for duplicating partitions in AIX " + "disk labels is not implemented yet.")); + return NULL; +} + +static void +aix_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + + _ped_partition_free (part); +} + +static int +aix_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for setting system type of partitions " + "in AIX disk labels is not implemented yet.")); + return 0; +} + +static int +aix_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for setting flags " + "in AIX disk labels is not implemented yet.")); + return 0; +} + +static int +aix_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + return 0; +} + + +static int +aix_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + return 0; +} + + +static int +aix_get_max_primary_partition_count (const PedDisk* disk) +{ + return 4; +} + +static int +aix_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL, return 0); + + return 1; +} + +static int +aix_partition_enumerate (PedPartition* part) +{ + return 1; +} + +static int +aix_alloc_metadata (PedDisk* disk) +{ + return 1; +} + +static PedDiskOps aix_disk_ops = { + probe: aix_probe, +#ifndef DISCOVER_ONLY + clobber: aix_clobber, +#else + clobber: NULL, +#endif + alloc: aix_alloc, + duplicate: aix_duplicate, + free: aix_free, + read: aix_read, +#ifndef DISCOVER_ONLY + write: aix_write, +#else + write: NULL, +#endif + + partition_new: aix_partition_new, + partition_duplicate: aix_partition_duplicate, + partition_destroy: aix_partition_destroy, + partition_set_system: aix_partition_set_system, + partition_set_flag: aix_partition_set_flag, + partition_get_flag: aix_partition_get_flag, + partition_is_flag_available: aix_partition_is_flag_available, + partition_align: aix_partition_align, + partition_enumerate: aix_partition_enumerate, + alloc_metadata: aix_alloc_metadata, + get_max_primary_partition_count: + aix_get_max_primary_partition_count, + + partition_set_name: NULL, + partition_get_name: NULL, +}; + +static PedDiskType aix_disk_type = { + next: NULL, + name: "aix", + ops: &aix_disk_ops, + features: 0 +}; + +void +ped_disk_aix_init () +{ + PED_ASSERT (sizeof (AixLabel) == 512, return); + ped_register_disk_type (&aix_disk_type); +} + +void +ped_disk_aix_done () +{ + ped_unregister_disk_type (&aix_disk_type); +} diff --git a/libparted/labels/bsd.c b/libparted/labels/bsd.c new file mode 100644 index 0000000..21ee870 --- /dev/null +++ b/libparted/labels/bsd.c @@ -0,0 +1,617 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + Contributor: Matt Wilson <msw@redhat.com> +*/ + +#include "config.h" + +#include <string.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* struct's & #define's stolen from libfdisk, which probably came from + * Linux... + */ + +#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ +#define BSD_MAXPARTITIONS 8 +#define BSD_FS_UNUSED 0 /* disklabel unused partition entry ID */ +#define BSD_LABEL_OFFSET 64 + +#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ +#define BSD_DTYPE_MSCP 2 /* MSCP */ +#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */ +#define BSD_DTYPE_SCSI 4 /* SCSI */ +#define BSD_DTYPE_ESDI 5 /* ESDI interface */ +#define BSD_DTYPE_ST506 6 /* ST506 etc. */ +#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */ +#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */ +#define BSD_DTYPE_FLOPPY 10 /* floppy */ + +#define BSD_BBSIZE 8192 /* size of boot area, with label */ +#define BSD_SBSIZE 8192 /* max size of fs superblock */ + +typedef struct _BSDRawPartition BSDRawPartition; +typedef struct _BSDRawLabel BSDRawLabel; + +struct _BSDRawPartition { /* the partition table */ + uint32_t p_size; /* number of sectors in partition */ + uint32_t p_offset; /* starting sector */ + uint32_t p_fsize; /* file system basic fragment size */ + uint8_t p_fstype; /* file system type, see below */ + uint8_t p_frag; /* file system fragments per block */ + uint16_t p_cpg; /* file system cylinders per group */ +} __attribute__((packed)); + +struct _BSDRawLabel { + uint32_t d_magic; /* the magic number */ + int16_t d_type; /* drive type */ + int16_t d_subtype; /* controller/d_type specific */ + int8_t d_typename[16]; /* type name, e.g. "eagle" */ + int8_t d_packname[16]; /* pack identifier */ + uint32_t d_secsize; /* # of bytes per sector */ + uint32_t d_nsectors; /* # of data sectors per track */ + uint32_t d_ntracks; /* # of tracks per cylinder */ + uint32_t d_ncylinders; /* # of data cylinders per unit */ + uint32_t d_secpercyl; /* # of data sectors per cylinder */ + uint32_t d_secperunit; /* # of data sectors per unit */ + uint16_t d_sparespertrack; /* # of spare sectors per track */ + uint16_t d_sparespercyl; /* # of spare sectors per cylinder */ + uint32_t d_acylinders; /* # of alt. cylinders per unit */ + uint16_t d_rpm; /* rotational speed */ + uint16_t d_interleave; /* hardware sector interleave */ + uint16_t d_trackskew; /* sector 0 skew, per track */ + uint16_t d_cylskew; /* sector 0 skew, per cylinder */ + uint32_t d_headswitch; /* head switch time, usec */ + uint32_t d_trkseek; /* track-to-track seek, usec */ + uint32_t d_flags; /* generic flags */ +#define NDDATA 5 + uint32_t d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + uint32_t d_spare[NSPARE]; /* reserved for future use */ + uint32_t d_magic2; /* the magic number (again) */ + uint16_t d_checksum; /* xor of data incl. partitions */ + + /* file system and partition information: */ + uint16_t d_npartitions; /* number of partitions in following */ + uint32_t d_bbsize; /* size of boot area at sn0, bytes */ + uint32_t d_sbsize; /* max size of fs superblock, bytes */ + BSDRawPartition d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */ +} __attribute__((packed)); + +typedef struct { + char boot_code [512]; +} BSDDiskData; + +typedef struct { + uint8_t type; +} BSDPartitionData; + +static PedDiskType bsd_disk_type; + +/* XXX fixme: endian? */ +static unsigned short +xbsd_dkcksum (BSDRawLabel *lp) { + unsigned short *start, *end; + unsigned short sum = 0; + + lp->d_checksum = 0; + start = (u_short*) lp; + end = (u_short*) &lp->d_partitions [ + PED_LE16_TO_CPU (lp->d_npartitions)]; + while (start < end) + sum ^= *start++; + return sum; +} + +/* XXX fixme: endian? */ +static void +alpha_bootblock_checksum (char *boot) { + uint64_t *dp, sum; + int i; + + dp = (uint64_t *)boot; + sum = 0; + for (i = 0; i < 63; i++) + sum += dp[i]; + dp[63] = sum; +} + + +static int +bsd_probe (const PedDevice *dev) +{ + char boot[512]; + BSDRawLabel *label; + int i; + + PED_ASSERT (dev != NULL, return 0); + + if (dev->sector_size != 512) + return 0; + + if (!ped_device_read (dev, boot, 0, 1)) + return 0; + + label = (BSDRawLabel *) (boot + BSD_LABEL_OFFSET); + + alpha_bootblock_checksum(boot); + + /* check magic */ + if (PED_LE32_TO_CPU (label->d_magic) != BSD_DISKMAGIC) + return 0; + + return 1; +} + +static PedDisk* +bsd_alloc (const PedDevice* dev) +{ + PedDisk* disk; + BSDDiskData* bsd_specific; + BSDRawLabel* label; + + PED_ASSERT(dev->sector_size % 512 == 0, return 0); + + disk = _ped_disk_alloc ((PedDevice*)dev, &bsd_disk_type); + if (!disk) + goto error; + disk->disk_specific = bsd_specific = ped_malloc (sizeof (BSDDiskData)); + if (!bsd_specific) + goto error_free_disk; + + label = (BSDRawLabel*) (bsd_specific->boot_code + BSD_LABEL_OFFSET); + memset(label, 0, sizeof(BSDRawLabel)); + + label->d_magic = PED_CPU_TO_LE32 (BSD_DISKMAGIC); + label->d_type = PED_CPU_TO_LE16 (BSD_DTYPE_SCSI); + label->d_flags = 0; + label->d_secsize = PED_CPU_TO_LE16 (dev->sector_size); + label->d_nsectors = PED_CPU_TO_LE32 (dev->bios_geom.sectors); + label->d_ntracks = PED_CPU_TO_LE32 (dev->bios_geom.heads); + label->d_ncylinders = PED_CPU_TO_LE32 (dev->bios_geom.cylinders); + label->d_secpercyl = PED_CPU_TO_LE32 (dev->bios_geom.sectors + * dev->bios_geom.heads); + label->d_secperunit + = PED_CPU_TO_LE32 (dev->bios_geom.sectors + * dev->bios_geom.heads + * dev->bios_geom.cylinders); + + label->d_rpm = PED_CPU_TO_LE16 (3600); + label->d_interleave = PED_CPU_TO_LE16 (1);; + label->d_trackskew = 0; + label->d_cylskew = 0; + label->d_headswitch = 0; + label->d_trkseek = 0; + + label->d_magic2 = PED_CPU_TO_LE32 (BSD_DISKMAGIC); + label->d_bbsize = PED_CPU_TO_LE32 (BSD_BBSIZE); + label->d_sbsize = PED_CPU_TO_LE32 (BSD_SBSIZE); + + label->d_npartitions = 0; + label->d_checksum = xbsd_dkcksum (label); + return disk; + +error_free_disk: + ped_free (disk); +error: + return NULL; +} + +static PedDisk* +bsd_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + BSDDiskData* new_bsd_data; + BSDDiskData* old_bsd_data = (BSDDiskData*) disk->disk_specific; + + new_disk = ped_disk_new_fresh (disk->dev, &bsd_disk_type); + if (!new_disk) + return NULL; + + new_bsd_data = (BSDDiskData*) new_disk->disk_specific; + memcpy (new_bsd_data->boot_code, old_bsd_data->boot_code, 512); + return new_disk; +} + +static void +bsd_free (PedDisk* disk) +{ + ped_free (disk->disk_specific); + _ped_disk_free (disk); +} + +#ifndef DISCOVER_ONLY +static int +bsd_clobber (PedDevice* dev) +{ + char boot [512]; + BSDRawLabel* label = (BSDRawLabel *) (boot + BSD_LABEL_OFFSET); + + if (!ped_device_read (dev, boot, 0, 1)) + return 0; + label->d_magic = 0; + return ped_device_write (dev, (void*) boot, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + +static int +bsd_read (PedDisk* disk) +{ + BSDDiskData* bsd_specific = (BSDDiskData*) disk->disk_specific; + BSDRawLabel* label; + int i; + + ped_disk_delete_all (disk); + + if (!ped_device_read (disk->dev, bsd_specific->boot_code, 0, 1)) + goto error; + label = (BSDRawLabel *) (bsd_specific->boot_code + BSD_LABEL_OFFSET); + + for (i = 1; i <= BSD_MAXPARTITIONS; i++) { + PedPartition* part; + BSDPartitionData* bsd_part_data; + PedSector start; + PedSector end; + PedConstraint* constraint_exact; + + if (!label->d_partitions[i - 1].p_size + || !label->d_partitions[i - 1].p_fstype) + continue; + start = PED_LE32_TO_CPU(label->d_partitions[i - 1].p_offset); + end = PED_LE32_TO_CPU(label->d_partitions[i - 1].p_offset) + + PED_LE32_TO_CPU(label->d_partitions[i - 1].p_size) - 1; + part = ped_partition_new (disk, 0, NULL, start, end); + if (!part) + goto error; + bsd_part_data = part->disk_specific; + bsd_part_data->type = label->d_partitions[i - 1].p_fstype; + part->num = i; + part->fs_type = ped_file_system_probe (&part->geom); + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) + goto error; + ped_constraint_destroy (constraint_exact); + } + + return 1; + +error: + return 0; +} + +static void +_probe_and_add_boot_code (PedDisk* disk) +{ + BSDDiskData* bsd_specific; + BSDRawLabel* old_label; + char old_boot_code [512]; + + bsd_specific = (BSDDiskData*) disk->disk_specific; + old_label = (BSDRawLabel*) (old_boot_code + BSD_LABEL_OFFSET); + + if (!ped_device_read (disk->dev, old_boot_code, 0, 1)) + return; + if (old_boot_code [0] + && old_label->d_magic == PED_CPU_TO_LE32 (BSD_DISKMAGIC)) + memcpy (bsd_specific->boot_code, old_boot_code, 512); +} + +#ifndef DISCOVER_ONLY +static int +bsd_write (PedDisk* disk) +{ + BSDDiskData* bsd_specific; + BSDRawLabel* label; + BSDPartitionData* bsd_data; + PedPartition* part; + int i; + int max_part = 0; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + bsd_specific = (BSDDiskData*) disk->disk_specific; + label = (BSDRawLabel *) (bsd_specific->boot_code + BSD_LABEL_OFFSET); + + if (!bsd_specific->boot_code [0]) + _probe_and_add_boot_code (disk); + + memset (label->d_partitions, 0, + sizeof (BSDRawPartition) * BSD_MAXPARTITIONS); + + for (i = 1; i <= BSD_MAXPARTITIONS; i++) { + part = ped_disk_get_partition (disk, i); + if (!part) + continue; + bsd_data = part->disk_specific; + label->d_partitions[i - 1].p_fstype = bsd_data->type; + label->d_partitions[i - 1].p_offset + = PED_CPU_TO_LE32 (part->geom.start); + label->d_partitions[i - 1].p_size + = PED_CPU_TO_LE32 (part->geom.length); + max_part = i; + } + + label->d_npartitions = PED_CPU_TO_LE16 (max_part) + 1; + label->d_checksum = xbsd_dkcksum (label); + + alpha_bootblock_checksum (bsd_specific->boot_code); + + if (!ped_device_write (disk->dev, (void*) bsd_specific->boot_code, + 0, 1)) + goto error; + return ped_device_sync (disk->dev); + +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +bsd_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + BSDPartitionData* bsd_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = bsd_data = ped_malloc (sizeof (BSDPartitionData)); + if (!bsd_data) + goto error_free_part; + bsd_data->type = 0; + } else { + part->disk_specific = NULL; + } + return part; + +error_free_bsd_data: + ped_free (bsd_data); +error_free_part: + ped_free (part); +error: + return 0; +} + +static PedPartition* +bsd_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + BSDPartitionData* new_bsd_data; + BSDPartitionData* old_bsd_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_bsd_data = (BSDPartitionData*) part->disk_specific; + new_bsd_data = (BSDPartitionData*) new_part->disk_specific; + new_bsd_data->type = old_bsd_data->type; + return new_part; +} + +static void +bsd_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + + if (ped_partition_is_active (part)) + ped_free (part->disk_specific); + _ped_partition_free (part); +} + +static int +bsd_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + BSDPartitionData* bsd_data = part->disk_specific; + + part->fs_type = fs_type; + + if (!fs_type) + bsd_data->type = 0x8; + else if (!strcmp (fs_type->name, "linux-swap")) + bsd_data->type = 0x1; + else + bsd_data->type = 0x8; + + return 1; +} + +static int +bsd_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + /* no flags for bsd */ + return 0; +} + +static int +bsd_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + /* no flags for bsd */ + return 0; +} + +static int +bsd_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + /* no flags for bsd */ + return 0; +} + + +static int +bsd_get_max_primary_partition_count (const PedDisk* disk) +{ + return BSD_MAXPARTITIONS; +} + +static PedConstraint* +_get_constraint (const PedDevice* dev) +{ + PedGeometry max; + + ped_geometry_init (&max, dev, 1, dev->length - 1); + return ped_constraint_new_from_max (&max); +} + +static int +bsd_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + if (_ped_partition_attempt_align (part, constraint, + _get_constraint (part->disk->dev))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +bsd_partition_enumerate (PedPartition* part) +{ + int i; + PedPartition* p; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + for (i = 1; i <= BSD_MAXPARTITIONS; i++) { + p = ped_disk_get_partition (part->disk, i); + if (!p) { + part->num = i; + return 1; + } + } + + /* failed to allocate a number */ +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to allocate a bsd disklabel slot.")); +#endif + return 0; +} + +static int +bsd_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + PedConstraint* constraint_any = NULL; + + PED_ASSERT (disk != NULL, goto error); + PED_ASSERT (disk->dev != NULL, goto error); + + constraint_any = ped_constraint_any (disk->dev); + + /* allocate 1 sector for the disk label at the start */ + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, 0, 0); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static PedDiskOps bsd_disk_ops = { + probe: bsd_probe, +#ifndef DISCOVER_ONLY + clobber: bsd_clobber, +#else + clobber: NULL, +#endif + alloc: bsd_alloc, + duplicate: bsd_duplicate, + free: bsd_free, + read: bsd_read, +#ifndef DISCOVER_ONLY + write: bsd_write, +#else + write: NULL, +#endif + + partition_new: bsd_partition_new, + partition_duplicate: bsd_partition_duplicate, + partition_destroy: bsd_partition_destroy, + partition_set_system: bsd_partition_set_system, + partition_set_flag: bsd_partition_set_flag, + partition_get_flag: bsd_partition_get_flag, + partition_is_flag_available: bsd_partition_is_flag_available, + partition_set_name: NULL, + partition_get_name: NULL, + partition_align: bsd_partition_align, + partition_enumerate: bsd_partition_enumerate, + + alloc_metadata: bsd_alloc_metadata, + get_max_primary_partition_count: + bsd_get_max_primary_partition_count +}; + +static PedDiskType bsd_disk_type = { + next: NULL, + name: "bsd", + ops: &bsd_disk_ops, + features: 0 +}; + +void +ped_disk_bsd_init () +{ + PED_ASSERT (sizeof (BSDRawPartition) == 16, return); + PED_ASSERT (sizeof (BSDRawLabel) == 276, return); + + ped_register_disk_type (&bsd_disk_type); +} + +void +ped_disk_bsd_done () +{ + ped_unregister_disk_type (&bsd_disk_type); +} diff --git a/libparted/labels/dasd.c b/libparted/labels/dasd.c new file mode 100644 index 0000000..4405cab --- /dev/null +++ b/libparted/labels/dasd.c @@ -0,0 +1,874 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contributor: Phil Knirsch <phil@redhat.de> + Harald Hoyer <harald@redhat.de> +*/ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <time.h> +#include <fcntl.h> +#include <unistd.h> + +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include <parted/vtoc.h> +#include <parted/fdasd.h> +#include <parted/linux.h> + +#include <libintl.h> +#if ENABLE_NLS +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define PARTITION_LINUX_SWAP 0x82 +#define PARTITION_LINUX 0x83 +#define PARTITION_LINUX_EXT 0x85 +#define PARTITION_LINUX_LVM 0x8e +#define PARTITION_LINUX_RAID 0xfd +#define PARTITION_LINUX_LVM_OLD 0xfe + +extern void ped_disk_dasd_init (); +extern void ped_disk_dasd_done (); + +#define DASD_NAME "dasd" + +typedef struct { + int type; + int system; + int raid; + int lvm; + void *part_info; +} DasdPartitionData; + +typedef struct { + unsigned int real_sector_size; + unsigned int format_type; + /* IBM internal dasd structure (i guess ;), required. */ + struct fdasd_anchor *anchor; +} DasdDiskSpecific; + +static int dasd_probe (const PedDevice *dev); +static int dasd_clobber (PedDevice* dev); +static int dasd_read (PedDisk* disk); +static int dasd_write (PedDisk* disk); + +static PedPartition* dasd_partition_new (const PedDisk* disk, + PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, + PedSector end); +static void dasd_partition_destroy (PedPartition* part); +static int dasd_partition_set_flag (PedPartition* part, + PedPartitionFlag flag, + int state); +static int dasd_partition_get_flag (const PedPartition* part, + PedPartitionFlag flag); +static int dasd_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag); +static int dasd_partition_align (PedPartition* part, + const PedConstraint* constraint); +static int dasd_partition_enumerate (PedPartition* part); +static int dasd_get_max_primary_partition_count (const PedDisk* disk); + +static PedDisk* dasd_alloc (const PedDevice* dev); +static PedDisk* dasd_duplicate (const PedDisk* disk); +static void dasd_free (PedDisk* disk); +static int dasd_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type); +static int dasd_alloc_metadata (PedDisk* disk); + +static PedDiskOps dasd_disk_ops = { + probe: dasd_probe, + clobber: dasd_clobber, + read: dasd_read, + write: dasd_write, + + alloc: dasd_alloc, + duplicate: dasd_duplicate, + free: dasd_free, + partition_set_system: dasd_partition_set_system, + + partition_new: dasd_partition_new, + partition_destroy: dasd_partition_destroy, + partition_set_flag: dasd_partition_set_flag, + partition_get_flag: dasd_partition_get_flag, + partition_is_flag_available: dasd_partition_is_flag_available, + partition_set_name: NULL, + partition_get_name: NULL, + partition_align: dasd_partition_align, + partition_enumerate: dasd_partition_enumerate, + + alloc_metadata: dasd_alloc_metadata, + get_max_primary_partition_count: dasd_get_max_primary_partition_count, + + partition_duplicate: NULL +}; + +static PedDiskType dasd_disk_type = { + next: NULL, + name: "dasd", + ops: &dasd_disk_ops, + features: 0 +}; + +static PedDisk* +dasd_alloc (const PedDevice* dev) +{ + PedDisk* disk; + LinuxSpecific* arch_specific; + DasdDiskSpecific *disk_specific; + + PED_ASSERT (dev != NULL, return NULL); + + arch_specific = LINUX_SPECIFIC (dev); + disk = _ped_disk_alloc (dev, &dasd_disk_type); + if (!disk) + return NULL; + + disk->disk_specific = disk_specific = ped_malloc(sizeof(DasdDiskSpecific)); + if (!disk->disk_specific) { + ped_free (disk); + return NULL; + } + + /* because we lie to parted we have to compensate with the + real sector size. Record that now. */ + if (ioctl(arch_specific->fd, BLKSSZGET, + &disk_specific->real_sector_size) == -1) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to determine the block " + "size of this dasd")); + ped_free(disk_specific); + ped_free(disk); + return NULL; + } + + return disk; +} + +static PedDisk* +dasd_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + + new_disk = ped_disk_new_fresh(disk->dev, &dasd_disk_type); + + if (!new_disk) + return NULL; + + new_disk->disk_specific = NULL; + + return new_disk; +} + +static void +dasd_free (PedDisk* disk) +{ + PED_ASSERT(disk != NULL, return); + + _ped_disk_free(disk); +} + + +void +ped_disk_dasd_init () +{ + ped_register_disk_type(&dasd_disk_type); +} + +void +ped_disk_dasd_done () +{ + ped_unregister_disk_type(&dasd_disk_type); +} + +static int +dasd_probe (const PedDevice *dev) +{ + char *errstr = 0; + LinuxSpecific* arch_specific; + struct fdasd_anchor anchor; + + PED_ASSERT(dev != NULL, return 0); + PED_ASSERT((dev->type == PED_DEVICE_DASD + || dev->type == PED_DEVICE_VIODASD), return 0); + + arch_specific = LINUX_SPECIFIC(dev); + + /* add partition test here */ + fdasd_initialize_anchor(&anchor); + + fdasd_get_geometry(&anchor, arch_specific->fd); + + fdasd_check_api_version(&anchor, arch_specific->fd); + + if (fdasd_check_volume(&anchor, arch_specific->fd)) + goto error_cleanup; + + fdasd_cleanup(&anchor); + + return 1; + +error_cleanup: + fdasd_cleanup(&anchor); +error: + ped_exception_throw(PED_EXCEPTION_ERROR,PED_EXCEPTION_IGNORE_CANCEL,errstr); + + return 0; +} + +static int +dasd_clobber (PedDevice* dev) +{ + int i; + LinuxSpecific* arch_specific; + struct fdasd_anchor anchor; + + PED_ASSERT(dev != NULL, return 0); + + arch_specific = LINUX_SPECIFIC(dev); + + fdasd_initialize_anchor(&anchor); + fdasd_get_geometry(&anchor, arch_specific->fd); + + fdasd_recreate_vtoc(&anchor); + fdasd_write_labels(&anchor, arch_specific->fd); + + return 1; +error: + return 0; +} + +static int +dasd_read (PedDisk* disk) +{ + int i, s; + char str[20]; + PedDevice* dev; + PedPartition* part; + PedSector start, end; + PedConstraint* constraint_exact; + partition_info_t *p; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + + PDEBUG; + + PED_ASSERT (disk != NULL, return 0); + PDEBUG; + PED_ASSERT (disk->dev != NULL, return 0); + PDEBUG; + + dev = disk->dev; + + arch_specific = LINUX_SPECIFIC(dev); + disk_specific = disk->disk_specific; + + disk_specific->anchor = ped_malloc(sizeof(fdasd_anchor_t)); + + PDEBUG; + + fdasd_initialize_anchor(disk_specific->anchor); + + fdasd_get_geometry(disk_specific->anchor, arch_specific->fd); + + /* check dasd for labels and vtoc */ + if (fdasd_check_volume(disk_specific->anchor, arch_specific->fd)) + goto error_close_dev; + + if ((disk_specific->anchor->geo.cylinders + * disk_specific->anchor->geo.heads) > BIG_DISK_SIZE) + disk_specific->anchor->big_disk++; + + ped_disk_delete_all (disk); + + if (strncmp(disk_specific->anchor->vlabel->volkey, + vtoc_ebcdic_enc ("LNX1", str, 4), 4) == 0) { + DasdPartitionData* dasd_data; + + /* LDL format, old one */ + disk_specific->format_type = 1; + start = 24; + end = (long long)(long long) disk_specific->anchor->geo.cylinders + * (long long)disk_specific->anchor->geo.heads + * (long long)disk->dev->hw_geom.sectors + * (long long)disk_specific->real_sector_size + / (long long)disk->dev->sector_size - 1; + part = ped_partition_new (disk, PED_PARTITION_PROTECTED, NULL, start, end); + if (!part) + goto error_close_dev; + + part->num = 1; + part->fs_type = ped_file_system_probe (&part->geom); + dasd_data = part->disk_specific; + dasd_data->raid = 0; + dasd_data->lvm = 0; + dasd_data->type = 0; + + if (!ped_disk_add_partition (disk, part, NULL)) + goto error_close_dev; + + return 1; + } + + /* CDL format, newer */ + disk_specific->format_type = 2; + + p = disk_specific->anchor->first; + PDEBUG; + + for (i = 1 ; i <= USABLE_PARTITIONS; i++) { + char *ch = p->f1->DS1DSNAM; + DasdPartitionData* dasd_data; + + + if (p->used != 0x01) + continue; + + PDEBUG; + + start = (long long)(long long) p->start_trk + * (long long) disk->dev->hw_geom.sectors + * (long long) disk_specific->real_sector_size + / (long long) disk->dev->sector_size; + end = (long long)((long long) p->end_trk + 1) + * (long long) disk->dev->hw_geom.sectors + * (long long) disk_specific->real_sector_size + / (long long) disk->dev->sector_size - 1; + part = ped_partition_new(disk, 0, NULL, start, end); + PDEBUG; + + if (!part) + goto error_close_dev; + + PDEBUG; + + part->num = i; + part->fs_type = ped_file_system_probe(&part->geom); + + vtoc_ebcdic_dec(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + ch = strstr(p->f1->DS1DSNAM, "PART"); + + if (ch != NULL) { + strncpy(str, ch+9, 6); + str[6] = '\0'; + } + + dasd_data = part->disk_specific; + + if (strncmp(PART_TYPE_RAID, str, 6) == 0) + ped_partition_set_flag(part, PED_PARTITION_RAID, 1); + else + ped_partition_set_flag(part, PED_PARTITION_RAID, 0); + + if (strncmp(PART_TYPE_LVM, str, 6) == 0) + ped_partition_set_flag(part, PED_PARTITION_LVM, 1); + else + ped_partition_set_flag(part, PED_PARTITION_LVM, 0); + + if (strncmp(PART_TYPE_SWAP, str, 6) == 0) { + dasd_data->system = PARTITION_LINUX_SWAP; + PDEBUG; + } + + vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + + dasd_data->part_info = (void *) p; + dasd_data->type = 0; + + constraint_exact = ped_constraint_exact (&part->geom); + if (!constraint_exact) + goto error_close_dev; + if (!ped_disk_add_partition(disk, part, constraint_exact)) + goto error_close_dev; + ped_constraint_destroy(constraint_exact); + + if (p->fspace_trk > 0) { + start = (long long)((long long) p->end_trk + 1) + * (long long) disk->dev->hw_geom.sectors + * (long long) disk_specific->real_sector_size + / (long long) disk->dev->sector_size; + end = (long long)((long long) p->end_trk + 1 + p->fspace_trk) + * (long long) disk->dev->hw_geom.sectors + * (long long) disk_specific->real_sector_size + / (long long) disk->dev->sector_size - 1; + part = ped_partition_new (disk, 0, NULL, start, end); + + if (!part) + goto error_close_dev; + + part->type = PED_PARTITION_FREESPACE; + constraint_exact = ped_constraint_exact(&part->geom); + + if (!constraint_exact) + goto error_close_dev; + if (!ped_disk_add_partition(disk, part, constraint_exact)) + goto error_close_dev; + + ped_constraint_destroy (constraint_exact); + } + + p = p->next; + } + + PDEBUG; + return 1; + +error_close_dev: + PDEBUG; + return 0; +} + +static int +dasd_update_type (PedDisk* disk) +{ + PedPartition* part; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + char *sys = NULL; + + arch_specific = LINUX_SPECIFIC(disk->dev); + disk_specific = disk->disk_specific; + + PDEBUG; + + for (part = ped_disk_next_partition(disk, NULL); part; + part = ped_disk_next_partition(disk, part)) { + partition_info_t *p; + char *ch = NULL; + DasdPartitionData* dasd_data; + + PDEBUG; + + if (part->type & PED_PARTITION_FREESPACE + || part->type & PED_PARTITION_METADATA) + continue; + + PDEBUG; + + dasd_data = part->disk_specific; + p = dasd_data->part_info; + + if (!p ) { + PDEBUG; + continue; + } + + vtoc_ebcdic_dec(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + ch = strstr(p->f1->DS1DSNAM, "PART"); + + PDEBUG; + if (ch == NULL) { + vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + PDEBUG; + continue; + } + + ch += 9; + + switch (dasd_data->system) { + case PARTITION_LINUX_LVM: + PDEBUG; + strncpy(ch, PART_TYPE_LVM, 6); + break; + case PARTITION_LINUX_RAID: + PDEBUG; + strncpy(ch, PART_TYPE_RAID, 6); + break; + case PARTITION_LINUX: + PDEBUG; + strncpy(ch, PART_TYPE_NATIVE, 6); + break; + case PARTITION_LINUX_SWAP: + PDEBUG; + strncpy(ch, PART_TYPE_SWAP, 6); + break; + default: + PDEBUG; + strncpy(ch, PART_TYPE_NATIVE, 6); + break; + } + + disk_specific->anchor->vtoc_changed++; + vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + } + + return 1; +} + +static int +dasd_write (PedDisk* disk) +{ + DasdPartitionData* dasd_data; + PedPartition* part; + int i; + int ret; + partition_info_t *p; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + PED_ASSERT(disk != NULL, return 0); + PED_ASSERT(disk->dev != NULL, return 0); + + arch_specific = LINUX_SPECIFIC (disk->dev); + disk_specific = disk->disk_specific; + + PDEBUG; + + /* If formated in LDL, don't write anything. */ + if (disk_specific->format_type == 1) + return 1; + + /* XXX re-initialize anchor? */ + fdasd_initialize_anchor(disk_specific->anchor); + fdasd_get_geometry(disk_specific->anchor, arch_specific->fd); + + /* check dasd for labels and vtoc */ + if (fdasd_check_volume(disk_specific->anchor, arch_specific->fd)) + goto error; + + if ((disk_specific->anchor->geo.cylinders + * disk_specific->anchor->geo.heads) > BIG_DISK_SIZE) + disk_specific->anchor->big_disk++; + + fdasd_recreate_vtoc(disk_specific->anchor); + + for (i = 1; i <= USABLE_PARTITIONS; i++) { + unsigned int start, stop; + int type; + + PDEBUG; + part = ped_disk_get_partition(disk, i); + if (!part) + continue; + + PDEBUG; + + start = part->geom.start * disk->dev->sector_size + / disk_specific->real_sector_size / disk->dev->hw_geom.sectors; + stop = (part->geom.end + 1) + * disk->dev->sector_size / disk_specific->real_sector_size + / disk->dev->hw_geom.sectors - 1; + + PDEBUG; + dasd_data = part->disk_specific; + + type = dasd_data->type; + PDEBUG; + + p = fdasd_add_partition(disk_specific->anchor, start, stop); + if (!p) { + PDEBUG; + return 0; + } + dasd_data->part_info = (void *) p; + p->type = dasd_data->system; + } + + PDEBUG; + + if (!fdasd_prepare_labels(disk_specific->anchor, arch_specific->fd)) + return 0; + + dasd_update_type(disk); + PDEBUG; + + if (!fdasd_write_labels(disk_specific->anchor, arch_specific->fd)) + return 0; + + return 1; + +error: + PDEBUG; + return 0; +} + +static PedPartition* +dasd_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + + part = _ped_partition_alloc(disk, part_type, fs_type, start, end); + if (!part) + goto error; + + part->disk_specific = ped_malloc (sizeof (DasdPartitionData)); + return part; + +error: + return 0; +} + +static void +dasd_partition_destroy (PedPartition* part) +{ + PED_ASSERT(part != NULL, return); + + if (ped_partition_is_active(part)) + ped_free(part->disk_specific); + ped_free(part); +} + +static int +dasd_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + PedDisk* disk; + PedPartition* walk; + DasdPartitionData* dasd_data; + const PedFileSystemType* fs_type; + + PED_ASSERT(part != NULL, return 0); + PED_ASSERT(part->disk_specific != NULL, return 0); + dasd_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_RAID: + if (state) + dasd_data->lvm = 0; + dasd_data->raid = state; + return ped_partition_set_system(part, part->fs_type); + case PED_PARTITION_LVM: + if (state) + dasd_data->raid = 0; + dasd_data->lvm = state; + return ped_partition_set_system(part, part->fs_type); + default: + return 0; + } +} + +static int +dasd_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + DasdPartitionData* dasd_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + dasd_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_RAID: + return dasd_data->raid; + case PED_PARTITION_LVM: + return dasd_data->lvm; + default: + return 0; + } +} + +static int +dasd_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_RAID: + return 1; + case PED_PARTITION_LVM: + return 1; + default: + return 0; + } +} + + +static int +dasd_get_max_primary_partition_count (const PedDisk* disk) +{ + DasdDiskSpecific* disk_specific; + + disk_specific = disk->disk_specific; + /* If formated in LDL, maximum partition number is 1 */ + if (disk_specific->format_type == 1) + return 1; + + return USABLE_PARTITIONS; +} + +static PedConstraint* +_primary_constraint (PedDisk* disk) +{ + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + PedSector sector_size; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + + PDEBUG; + + arch_specific = LINUX_SPECIFIC (disk->dev); + disk_specific = disk->disk_specific; + sector_size = disk_specific->real_sector_size / disk->dev->sector_size; + + if (!ped_alignment_init (&start_align, 0, + disk->dev->hw_geom.sectors * sector_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, + disk->dev->hw_geom.sectors * sector_size)) + return NULL; + if (!ped_geometry_init (&max_geom, disk->dev, 0, disk->dev->length)) + return NULL; + + return ped_constraint_new(&start_align, &end_align, &max_geom, + &max_geom, 1, disk->dev->length); +} + +static int +dasd_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + DasdDiskSpecific* disk_specific; + + PED_ASSERT (part != NULL, return 0); + + disk_specific = part->disk->disk_specific; + /* If formated in LDL, ignore metadata partition */ + if (disk_specific->format_type == 1) + return 1; + + if (_ped_partition_attempt_align(part, constraint, + _primary_constraint(part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + + return 0; +} + +static int +dasd_partition_enumerate (PedPartition* part) +{ + int i; + PedPartition* p; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + + for (i = 1; i <= USABLE_PARTITIONS; i++) { + p = ped_disk_get_partition (part->disk, i); + if (!p) { + part->num = i; + return 1; + } + } + + /* failed to allocate a number */ + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to allocate a dasd disklabel slot")); + return 0; +} + +static int +dasd_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type) +{ + DasdPartitionData* dasd_data = part->disk_specific; + PedSector cyl_size; + + cyl_size=part->disk->dev->hw_geom.sectors * part->disk->dev->hw_geom.heads; + PDEBUG; + + part->fs_type = fs_type; + + if (dasd_data->lvm) { + dasd_data->system = PARTITION_LINUX_LVM; + PDEBUG; + return 1; + } + + if (dasd_data->raid) { + dasd_data->system = PARTITION_LINUX_RAID; + PDEBUG; + return 1; + } + + if (!fs_type) { + dasd_data->system = PARTITION_LINUX; + PDEBUG; + } else if (!strcmp (fs_type->name, "linux-swap")) { + dasd_data->system = PARTITION_LINUX_SWAP; + PDEBUG; + } else { + dasd_data->system = PARTITION_LINUX; + PDEBUG; + } + + return 1; +} + +static int +dasd_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + PedConstraint* constraint_any = NULL; + PedSector vtoc_end; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + + PED_ASSERT (disk != NULL, goto error); + PED_ASSERT (disk->dev != NULL, goto error); + + arch_specific = LINUX_SPECIFIC (disk->dev); + disk_specific = disk->disk_specific; + + constraint_any = ped_constraint_any (disk->dev); + + /* If formated in LDL, the real partition starts at sector 24. */ + if (disk_specific->format_type == 1) + vtoc_end = 23; + else + /* Mark the start of the disk as metadata. */ + vtoc_end = (FIRST_USABLE_TRK * (long long) disk->dev->hw_geom.sectors + * (long long) disk_specific->real_sector_size + / (long long) disk->dev->sector_size) - 1; + + new_part = ped_partition_new (disk,PED_PARTITION_METADATA,NULL,0,vtoc_end); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; + +error: + ped_constraint_destroy (constraint_any); + return 0; +} diff --git a/libparted/labels/dos.c b/libparted/labels/dos.c new file mode 100644 index 0000000..01d9602 --- /dev/null +++ b/libparted/labels/dos.c @@ -0,0 +1,2212 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000, 2001, 2004, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <sys/time.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <string.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* this MBR boot code is loaded into 0000:7c00 by the BIOS. See mbr.s for + * the source, and how to build it + */ + +static char MBR_BOOT_CODE[] = { + 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, + 0xb0, 0xb8, 0x00, 0x00, 0x8e, 0xd8, 0x8e, 0xc0, + 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, + 0x00, 0x02, 0xf3, 0xa4, 0xea, 0x21, 0x06, 0x00, + 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, + 0x83, 0xc6, 0x10, 0x81, 0xfe, 0xfe, 0x07, 0x75, + 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, + 0x00, 0x7c, 0xb2, 0x80, 0x8a, 0x74, 0x01, 0x8b, + 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, + 0x00, 0xeb, 0xfe +}; + +#define MSDOS_MAGIC 0xAA55 +#define PARTITION_MAGIC_MAGIC 0xf6f6 + +#define PARTITION_EMPTY 0x00 +#define PARTITION_FAT12 0x01 +#define PARTITION_FAT16_SM 0x04 +#define PARTITION_EXT 0x05 +#define PARTITION_FAT16 0x06 +#define PARTITION_NTFS 0x07 +#define PARTITION_HPFS 0x07 +#define PARTITION_FAT32 0x0b +#define PARTITION_FAT32_LBA 0x0c +#define PARTITION_FAT16_LBA 0x0e +#define PARTITION_EXT_LBA 0x0f + +#define PART_FLAG_HIDDEN 0x10 /* Valid for FAT/NTFS only */ +#define PARTITION_FAT12_H (PARTITION_FAT12 | PART_FLAG_HIDDEN) +#define PARTITION_FAT16_SM_H (PARTITION_FAT16_SM | PART_FLAG_HIDDEN) +#define PARTITION_EXT_H (PARTITION_EXT | PART_FLAG_HIDDEN) +#define PARTITION_FAT16_H (PARTITION_FAT16 | PART_FLAG_HIDDEN) +#define PARTITION_NTFS_H (PARTITION_NTFS | PART_FLAG_HIDDEN) +#define PARTITION_FAT32_H (PARTITION_FAT32 | PART_FLAG_HIDDEN) +#define PARTITION_FAT32_LBA_H (PARTITION_FAT32_LBA | PART_FLAG_HIDDEN) +#define PARTITION_FAT16_LBA_H (PARTITION_FAT16_LBA | PART_FLAG_HIDDEN) + +#define PARTITION_LDM 0x42 + +#define PARTITION_COMPAQ_DIAG 0x12 +#define PARTITION_LINUX_SWAP 0x82 +#define PARTITION_LINUX 0x83 +#define PARTITION_LINUX_EXT 0x85 +#define PARTITION_LINUX_LVM 0x8e +#define PARTITION_SUN_UFS 0xbf +#define PARTITION_DELL_DIAG 0xde +#define PARTITION_GPT 0xee +#define PARTITION_PALO 0xf0 +#define PARTITION_PREP 0x41 +#define PARTITION_LINUX_RAID 0xfd +#define PARTITION_LINUX_LVM_OLD 0xfe + +/* This constant contains the maximum cylinder number that can be represented + * in (C,H,S) notation. Higher cylinder numbers are reserved for + * "too big" indicators (in which case only LBA addressing can be used). + * Some partition tables in the wild indicate this number is 1021. + * (i.e. 1022 is sometimes used to indicate "use LBA"). + */ +#define MAX_CHS_CYLINDER 1021 + +typedef struct _DosRawPartition DosRawPartition; +typedef struct _DosRawTable DosRawTable; + +/* note: lots of bit-bashing here, thus, you shouldn't look inside it. + * Use chs_to_sector() and sector_to_chs() instead. + */ +typedef struct { + uint8_t head; + uint8_t sector; + uint8_t cylinder; +} __attribute__((packed)) RawCHS; + +/* ripped from Linux source */ +struct _DosRawPartition { + uint8_t boot_ind; /* 00: 0x80 - active */ + RawCHS chs_start; /* 01: */ + uint8_t type; /* 04: partition type */ + RawCHS chs_end; /* 05: */ + uint32_t start; /* 08: starting sector counting from 0 */ + uint32_t length; /* 0c: nr of sectors in partition */ +} __attribute__((packed)); + +struct _DosRawTable { + char boot_code [440]; + uint32_t mbr_signature; /* really a unique ID */ + uint16_t Unknown; + DosRawPartition partitions [4]; + uint16_t magic; +} __attribute__((packed)); + +/* OrigState is information we want to preserve about the partition for + * dealing with CHS issues + */ +typedef struct { + PedGeometry geom; + DosRawPartition raw_part; + PedSector lba_offset; /* needed for computing start/end for + * logical partitions */ +} OrigState; + +typedef struct { + unsigned char system; + int boot; + int hidden; + int raid; + int lvm; + int lba; + int palo; + int prep; + OrigState* orig; /* used for CHS stuff */ +} DosPartitionData; + +static PedDiskType msdos_disk_type; + +static int +msdos_probe (const PedDevice *dev) +{ + PedDiskType* disk_type; + DosRawTable part_table; + int i; + + PED_ASSERT (dev != NULL, return 0); + + if (dev->sector_size != 512) + return 0; + + if (!ped_device_read (dev, &part_table, 0, 1)) + return 0; + + /* check magic */ + if (PED_LE16_TO_CPU (part_table.magic) != MSDOS_MAGIC) + return 0; + + /* if this is a FAT fs, fail here. Note that the Smart Boot Manager + * Loader (SBML) signature indicates a partition table, not a file + * system. + */ + if ((!strncmp (part_table.boot_code + 0x36, "FAT", 3) + && strncmp (part_table.boot_code + 0x40, "SBML", 4) != 0) + || !strncmp (part_table.boot_code + 0x52, "FAT", 3)) + return 0; + + /* If this is a GPT disk, fail here */ + for (i = 0; i < 4; i++) { + if (part_table.partitions[i].type == PARTITION_GPT) + return 0; + } + + /* If this is an AIX Physical Volume, fail here. IBMA in EBCDIC */ + if (part_table.boot_code[0] == (char) 0xc9 && + part_table.boot_code[1] == (char) 0xc2 && + part_table.boot_code[2] == (char) 0xd4 && + part_table.boot_code[3] == (char) 0xc1) + return 0; + +#ifdef ENABLE_PC98 + /* HACK: it's impossible to tell PC98 and msdos disk labels apart. + * Someone made the signatures the same (very clever). Since + * PC98 has some idiosyncracies with it's boot-loader, it's detection + * is more reliable */ + disk_type = ped_disk_type_get ("pc98"); + if (disk_type && disk_type->ops->probe (dev)) + return 0; +#endif /* ENABLE_PC98 */ + + return 1; +} + +static PedDisk* +msdos_alloc (const PedDevice* dev) +{ + PedDisk* disk; + PED_ASSERT (dev != NULL, return NULL); + + disk = _ped_disk_alloc ((PedDevice*)dev, &msdos_disk_type); + if (disk) + disk->disk_specific = NULL; + return disk; +} + +static PedDisk* +msdos_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + + new_disk = ped_disk_new_fresh (disk->dev, &msdos_disk_type); + if (!new_disk) + return NULL; + new_disk->disk_specific = NULL; + return new_disk; +} + +static void +msdos_free (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, return); + + _ped_disk_free (disk); +} + +#ifndef DISCOVER_ONLY +static int +msdos_clobber (PedDevice* dev) +{ + DosRawTable table; + + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (msdos_probe (dev), return 0); + + if (!ped_device_read (dev, &table, 0, 1)) + return 0; + table.magic = 0; + return ped_device_write (dev, (void*) &table, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + +static int +chs_get_cylinder (const RawCHS* chs) +{ + return chs->cylinder + ((chs->sector >> 6) << 8); +} + +static int +chs_get_head (const RawCHS* chs) +{ + return chs->head; +} + +/* counts from 0 */ +static int +chs_get_sector (const RawCHS* chs) +{ + return (chs->sector & 0x3f) - 1; +} + +static PedSector +chs_to_sector (PedDevice* dev, const PedCHSGeometry *bios_geom, + const RawCHS* chs) +{ + PedSector c; /* not measured in sectors, but need */ + PedSector h; /* lots of bits */ + PedSector s; + + PED_ASSERT (bios_geom != NULL, return 0); + PED_ASSERT (chs != NULL, return 0); + + c = chs_get_cylinder (chs); + h = chs_get_head (chs); + s = chs_get_sector (chs); + + if (c > MAX_CHS_CYLINDER) /* MAGIC: C/H/S is irrelevant */ + return 0; + if (s < 0) + return 0; + return ((c * bios_geom->heads + h) * bios_geom->sectors + s) + * (dev->sector_size / 512); +} + +static void +sector_to_chs (PedDevice* dev, const PedCHSGeometry* bios_geom, + PedSector sector, RawCHS* chs) +{ + PedSector real_c, real_h, real_s; + + PED_ASSERT (dev != NULL, return); + PED_ASSERT (chs != NULL, return); + + if (!bios_geom) + bios_geom = &dev->bios_geom; + + sector /= (dev->sector_size / 512); + + real_c = sector / (bios_geom->heads * bios_geom->sectors); + real_h = (sector / bios_geom->sectors) % bios_geom->heads; + real_s = sector % bios_geom->sectors; + + if (real_c > MAX_CHS_CYLINDER) { + real_c = 1023; + real_h = bios_geom->heads - 1; + real_s = bios_geom->sectors - 1; + } + + chs->cylinder = real_c % 0x100; + chs->head = real_h; + chs->sector = real_s + 1 + (real_c >> 8 << 6); +} + +static PedSector +legacy_start (PedDisk* disk, const PedCHSGeometry* bios_geom, + const DosRawPartition* raw_part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (raw_part != NULL, return 0); + + return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_start); +} + +static PedSector +legacy_end (PedDisk* disk, const PedCHSGeometry* bios_geom, + const DosRawPartition* raw_part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (raw_part != NULL, return 0); + + return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_end); +} + +static PedSector +linear_start (const PedDisk* disk, const DosRawPartition* raw_part, + PedSector offset) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (raw_part != NULL, return 0); + + return offset + + PED_LE32_TO_CPU (raw_part->start) + * (disk->dev->sector_size / 512); +} + +static PedSector +linear_end (const PedDisk* disk, const DosRawPartition* raw_part, + PedSector offset) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (raw_part != NULL, return 0); + + return linear_start (disk, raw_part, offset) + + (PED_LE32_TO_CPU (raw_part->length) - 1) + * (disk->dev->sector_size / 512); +} + +#ifndef DISCOVER_ONLY +static int +partition_check_bios_geometry (PedPartition* part, PedCHSGeometry* bios_geom) +{ + PedSector leg_start, leg_end; + DosPartitionData* dos_data; + PedDisk* disk; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + dos_data = part->disk_specific; + + if (!dos_data->orig) + return 1; + + disk = part->disk; + leg_start = legacy_start (disk, bios_geom, &dos_data->orig->raw_part); + leg_end = legacy_end (disk, bios_geom, &dos_data->orig->raw_part); + + if (leg_start && leg_start != dos_data->orig->geom.start) + return 0; + if (leg_end && leg_end != dos_data->orig->geom.end) + return 0; + return 1; +} + +static int +disk_check_bios_geometry (PedDisk* disk, PedCHSGeometry* bios_geom) +{ + PedPartition* part = NULL; + + PED_ASSERT (disk != NULL, return 0); + + while ((part = ped_disk_next_partition (disk, part))) { + if (ped_partition_is_active (part)) { + if (!partition_check_bios_geometry (part, bios_geom)) + return 0; + } + } + + return 1; +} + +static int +probe_filesystem_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom) +{ + const char* ms_types[] = {"ntfs", "fat16", "fat32", NULL}; + int i; + int found; + unsigned char* buf; + int sectors; + int heads; + + PED_ASSERT (bios_geom != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + PED_ASSERT (part->disk->dev != NULL, return 0); + PED_ASSERT (part->disk->dev->sector_size % 512 == 0, return 0); + + buf = ped_malloc (part->disk->dev->sector_size); + + if (!part->fs_type) + return 0; + + found = 0; + for (i = 0; ms_types[i]; i++) { + if (!strcmp(ms_types[i], part->fs_type->name)) + found = 1; + } + if (!found) + return 0; + + if (!ped_geometry_read(&part->geom, buf, 0, 1)) + return 0; + + /* shared by the start of all Microsoft file systems */ + sectors = buf[0x18] + (buf[0x19] << 8); + heads = buf[0x1a] + (buf[0x1b] << 8); + + if (sectors < 1 || sectors > 63) + return 0; + if (heads > 255 || heads < 1) + return 0; + + bios_geom->sectors = sectors; + bios_geom->heads = heads; + bios_geom->cylinders = part->disk->dev->length / (sectors * heads); + return 1; +} + +/* This function attempts to infer the BIOS CHS geometry of the hard disk + * from the CHS + LBA information contained in the partition table from + * a single partition's entry. + * + * This involves some maths. Let (c,h,s,a) be the starting cylinder, + * starting head, starting sector and LBA start address of the partition. + * Likewise, (C,H,S,A) the end addresses. Using both of these pieces + * of information, we want to deduce cyl_sectors and head_sectors which + * are the sizes of a single cylinder and a single head, respectively. + * + * The relationships are: + * c*cyl_sectors + h * head_sectors + s = a + * C*cyl_sectors + H * head_sectors + S = A + * + * We can rewrite this in matrix form: + * + * [ c h ] [ cyl_sectors ] = [ s - a ] = [ a_ ] + * [ C H ] [ head_sectors ] [ S - A ] [ A_ ]. + * + * (s - a is abbreviated to a_to simplify the notation.) + * + * This can be abbreviated into augmented matrix form: + * + * [ c h | a_ ] + * [ C H | A_ ]. + * + * Solving these equations requires following the row reduction algorithm. We + * need to be careful about a few things though: + * - the equations might be linearly dependent, in which case there + * are many solutions. + * - the equations might be inconsistent, in which case there + * are no solutions. (Inconsistent partition table entry!) + * - there might be zeros, so we need to be careful about applying + * the algorithm. We know, however, that C > 0. + */ +static int +probe_partition_for_geom (PedPartition* part, PedCHSGeometry* bios_geom) +{ + DosPartitionData* dos_data; + RawCHS* start_chs; + RawCHS* end_chs; + PedSector c, h, s, a, a_; /* start */ + PedSector C, H, S, A, A_; /* end */ + PedSector dont_overflow, denum; + PedSector cyl_size, head_size; + PedSector cylinders, heads, sectors; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + PED_ASSERT (bios_geom != NULL, return 0); + + dos_data = part->disk_specific; + + if (!dos_data->orig) + return 0; + + start_chs = &dos_data->orig->raw_part.chs_start; + c = chs_get_cylinder (start_chs); + h = chs_get_head (start_chs); + s = chs_get_sector (start_chs); + a = dos_data->orig->geom.start; + a_ = a - s; + + end_chs = &dos_data->orig->raw_part.chs_end; + C = chs_get_cylinder (end_chs); + H = chs_get_head (end_chs); + S = chs_get_sector (end_chs); + A = dos_data->orig->geom.end; + A_ = A - S; + + if (h < 0 || H < 0 || h > 254 || H > 254) + return 0; + if (c > C) + return 0; + + /* If no geometry is feasible, then don't even bother. + * Useful for eliminating assertions for broken partition + * tables generated by Norton Ghost et al. + */ + if (A > (C+1) * 255 * 63) + return 0; + + /* Not enough information. In theory, we can do better. Should we? */ + if (C > MAX_CHS_CYLINDER) + return 0; + if (C == 0) + return 0; + + /* Calculate the maximum number that can be multiplied by + * any head count without overflowing a PedSector + * 2^8 = 256, 8 bits + 1(sign bit) = 9 + */ + dont_overflow = 1; + dont_overflow <<= (8*sizeof(dont_overflow)) - 9; + dont_overflow--; + + if (a_ > dont_overflow || A_ > dont_overflow) + return 0; + + /* The matrix is solved by : + * + * [ c h | a_] R1 + * [ C H | A_] R2 + * + * (cH - Ch) cyl_size = a_H - A_h H R1 - h R2 + * => (if cH - Ch != 0) cyl_size = (a_H - A_h) / (cH - Ch) + * + * (Hc - hC) head_size = A_c - a_C c R2 - C R1 + * => (if cH - Ch != 0) head_size = (A_c - a_C) / (cH - Ch) + * + * But this calculation of head_size would need + * not overflowing A_c or a_C + * So substitution is use instead, to minimize dimension + * of temporary results : + * + * If h != 0 : head_size = ( a_ - c cyl_size ) / h + * If H != 0 : head_size = ( A_ - C cyl_size ) / H + * + */ + denum = c * H - C * h; + if (denum == 0) + return 0; + + cyl_size = (a_*H - A_*h) / denum; + /* Check for non integer result */ + if (cyl_size * denum != a_*H - A_*h) + return 0; + + PED_ASSERT (cyl_size > 0, return 0); + PED_ASSERT (cyl_size <= 255 * 63, return 0); + + if (h > 0) + head_size = ( a_ - c * cyl_size ) / h; + else if (H > 0) + head_size = ( A_ - C * cyl_size ) / H; + else { + /* should not happen because denum != 0 */ + head_size = 0; + PED_ASSERT (0, return 0); + } + + PED_ASSERT (head_size > 0, return 0); + PED_ASSERT (head_size <= 63, return 0); + + cylinders = part->disk->dev->length / cyl_size; + heads = cyl_size / head_size; + sectors = head_size; + + PED_ASSERT (heads > 0, return 0); + PED_ASSERT (heads < 256, return 0); + + PED_ASSERT (sectors > 0, return 0); + PED_ASSERT (sectors <= 63, return 0); + + /* Some broken OEM partitioning program(s) seem to have an out-by-one + * error on the end of partitions. We should offer to fix the + * partition table... + */ + if (((C + 1) * heads + H) * sectors + S == A) + C++; + + PED_ASSERT ((c * heads + h) * sectors + s == a, return 0); + PED_ASSERT ((C * heads + H) * sectors + S == A, return 0); + + bios_geom->cylinders = cylinders; + bios_geom->heads = heads; + bios_geom->sectors = sectors; + + return 1; +} + +static void +partition_probe_bios_geometry (PedPartition* part, PedCHSGeometry* bios_geom) +{ + PED_ASSERT (part != NULL, return); + PED_ASSERT (part->disk != NULL, return); + PED_ASSERT (bios_geom != NULL, return); + + if (ped_partition_is_active (part)) { + if (probe_partition_for_geom (part, bios_geom)) + return; + if (part->type & PED_PARTITION_EXTENDED) { + if (probe_filesystem_for_geom (part, bios_geom)) + return; + } + } + if (part->type & PED_PARTITION_LOGICAL) { + PedPartition* ext_part; + ext_part = ped_disk_extended_partition (part->disk); + PED_ASSERT (ext_part != NULL, return); + partition_probe_bios_geometry (ext_part, bios_geom); + } else { + *bios_geom = part->disk->dev->bios_geom; + } +} + +static void +disk_probe_bios_geometry (PedDisk* disk, PedCHSGeometry* bios_geom) +{ + PedPartition* part; + + /* first look at the boot partition */ + part = NULL; + while ((part = ped_disk_next_partition (disk, part))) { + if (!ped_partition_is_active (part)) + continue; + if (ped_partition_get_flag (part, PED_PARTITION_BOOT)) { + if (probe_filesystem_for_geom (part, bios_geom)) + return; + if (probe_partition_for_geom (part, bios_geom)) + return; + } + } + + /* that didn't work... try all partition table entries */ + part = NULL; + while ((part = ped_disk_next_partition (disk, part))) { + if (ped_partition_is_active (part)) { + if (probe_partition_for_geom (part, bios_geom)) + return; + } + } + + /* that didn't work... look at all file systems */ + part = NULL; + while ((part = ped_disk_next_partition (disk, part))) { + if (ped_partition_is_active (part)) { + if (probe_filesystem_for_geom (part, bios_geom)) + return; + } + } +} +#endif /* !DISCOVER_ONLY */ + +static int +raw_part_is_extended (const DosRawPartition* raw_part) +{ + PED_ASSERT (raw_part != NULL, return 0); + + switch (raw_part->type) { + case PARTITION_EXT: + case PARTITION_EXT_LBA: + case PARTITION_LINUX_EXT: + return 1; + + default: + return 0; + } + + return 0; +} + +static int +raw_part_is_hidden (const DosRawPartition* raw_part) +{ + PED_ASSERT (raw_part != NULL, return 0); + + switch (raw_part->type) { + case PARTITION_FAT12_H: + case PARTITION_FAT16_SM_H: + case PARTITION_FAT16_H: + case PARTITION_FAT32_H: + case PARTITION_NTFS_H: + case PARTITION_FAT32_LBA_H: + case PARTITION_FAT16_LBA_H: + return 1; + + default: + return 0; + } + + return 0; +} + +static int +raw_part_is_lba (const DosRawPartition* raw_part) +{ + PED_ASSERT (raw_part != NULL, return 0); + + switch (raw_part->type) { + case PARTITION_FAT32_LBA: + case PARTITION_FAT16_LBA: + case PARTITION_EXT_LBA: + case PARTITION_FAT32_LBA_H: + case PARTITION_FAT16_LBA_H: + return 1; + + default: + return 0; + } + + return 0; +} + +PedPartition* +raw_part_parse (PedDisk* disk, const DosRawPartition* raw_part, + PedSector lba_offset, PedPartitionType type) +{ + PedPartition* part; + DosPartitionData* dos_data; + + PED_ASSERT (disk != NULL, return NULL); + PED_ASSERT (raw_part != NULL, return NULL); + + part = ped_partition_new ( + disk, type, NULL, + linear_start (disk, raw_part, lba_offset), + linear_end (disk, raw_part, lba_offset)); + if (!part) + return NULL; + dos_data = part->disk_specific; + dos_data->system = raw_part->type; + dos_data->boot = raw_part->boot_ind != 0; + dos_data->hidden = raw_part_is_hidden (raw_part); + dos_data->raid = raw_part->type == PARTITION_LINUX_RAID; + dos_data->lvm = raw_part->type == PARTITION_LINUX_LVM_OLD + || raw_part->type == PARTITION_LINUX_LVM; + dos_data->lba = raw_part_is_lba (raw_part); + dos_data->palo = raw_part->type == PARTITION_PALO; + dos_data->prep = raw_part->type == PARTITION_PREP; + dos_data->orig = ped_malloc (sizeof (OrigState)); + if (!dos_data->orig) { + ped_partition_destroy (part); + return NULL; + } + dos_data->orig->geom = part->geom; + dos_data->orig->raw_part = *raw_part; + dos_data->orig->lba_offset = lba_offset; + return part; +} + +static int +read_table (PedDisk* disk, PedSector sector, int is_extended_table) +{ + int i; + DosRawTable table; + DosRawPartition* raw_part; + PedPartition* part; + PedPartitionType type; + PedSector lba_offset; + PedConstraint* constraint_exact; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + if (!ped_device_read (disk->dev, (void*) &table, sector, 1)) + goto error; + + /* weird: empty extended partitions are filled with 0xf6 by PM */ + if (is_extended_table + && PED_LE16_TO_CPU (table.magic) == PARTITION_MAGIC_MAGIC) + return 1; + +#ifndef DISCOVER_ONLY + if (PED_LE16_TO_CPU (table.magic) != MSDOS_MAGIC) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid partition table on %s " + "-- wrong signature %x."), + disk->dev->path, + PED_LE16_TO_CPU (table.magic)) + != PED_EXCEPTION_IGNORE) + goto error; + return 1; + } +#endif + + /* parse the partitions from this table */ + for (i = 0; i < 4; i++) { + raw_part = &table.partitions [i]; + if (raw_part->type == PARTITION_EMPTY || !raw_part->length) + continue; + + /* process nested extended partitions after normal logical + * partitions, to make sure we get the order right. + */ + if (is_extended_table && raw_part_is_extended (raw_part)) + continue; + + lba_offset = is_extended_table ? sector : 0; + + if (linear_start (disk, raw_part, lba_offset) == sector) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid partition table - recursive " + "partition on %s."), + disk->dev->path) + != PED_EXCEPTION_IGNORE) + goto error; + continue; /* avoid infinite recursion */ + } + + if (is_extended_table) + type = PED_PARTITION_LOGICAL; + else if (raw_part_is_extended (raw_part)) + type = PED_PARTITION_EXTENDED; + else + type = PED_PARTITION_NORMAL; + + part = raw_part_parse (disk, raw_part, lba_offset, type); + if (!part) + goto error; + if (!is_extended_table) + part->num = i + 1; + if (type != PED_PARTITION_EXTENDED) + part->fs_type = ped_file_system_probe (&part->geom); + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) + goto error; + ped_constraint_destroy (constraint_exact); + + /* non-nested extended partition */ + if (part->type == PED_PARTITION_EXTENDED) { + if (!read_table (disk, part->geom.start, 1)) + goto error; + } + } + + if (is_extended_table) { + /* process the nested extended partitions */ + for (i = 0; i < 4; i++) { + PedSector part_start; + + raw_part = &table.partitions [i]; + if (!raw_part_is_extended (raw_part)) + continue; + + lba_offset = ped_disk_extended_partition + (disk)->geom.start; + part_start = linear_start (disk, raw_part, lba_offset); + if (part_start == sector) { + /* recursive table - already threw an + * exception above. + */ + continue; + } + if (!read_table (disk, part_start, 1)) + goto error; + } + } + + return 1; + +error: + ped_disk_delete_all (disk); + return 0; +} + +static int +msdos_read (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + ped_disk_delete_all (disk); + if (!read_table (disk, 0, 0)) + return 0; + +#ifndef DISCOVER_ONLY + /* try to figure out the correct BIOS CHS values */ + if (!disk_check_bios_geometry (disk, &disk->dev->bios_geom)) { + PedCHSGeometry bios_geom = disk->dev->bios_geom; + disk_probe_bios_geometry (disk, &bios_geom); + + /* if the geometry was wrong, then we should reread, to + * make sure the metadata is allocated in the right places. + */ + if (disk->dev->bios_geom.cylinders != bios_geom.cylinders + || disk->dev->bios_geom.heads != bios_geom.heads + || disk->dev->bios_geom.sectors != bios_geom.sectors) { + disk->dev->bios_geom = bios_geom; + return msdos_read (disk); + } + } +#endif + + return 1; +} + +#ifndef DISCOVER_ONLY +static int +fill_raw_part (DosRawPartition* raw_part, PedPartition* part, PedSector offset) +{ + DosPartitionData* dos_data; + PedCHSGeometry bios_geom; + + PED_ASSERT (raw_part != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + + partition_probe_bios_geometry (part, &bios_geom); + + dos_data = part->disk_specific; + + raw_part->boot_ind = 0x80 * dos_data->boot; + raw_part->type = dos_data->system; + raw_part->start = PED_CPU_TO_LE32 ((part->geom.start - offset) + / (part->disk->dev->sector_size / 512)); + raw_part->length = PED_CPU_TO_LE32 (part->geom.length + / (part->disk->dev->sector_size / 512)); + + sector_to_chs (part->disk->dev, &bios_geom, part->geom.start, + &raw_part->chs_start); + sector_to_chs (part->disk->dev, &bios_geom, part->geom.end, + &raw_part->chs_end); + + if (dos_data->orig) { + DosRawPartition* orig_raw_part = &dos_data->orig->raw_part; + if (dos_data->orig->geom.start == part->geom.start) + raw_part->chs_start = orig_raw_part->chs_start; + if (dos_data->orig->geom.end == part->geom.end) + raw_part->chs_end = orig_raw_part->chs_end; + } + + return 1; +} + +static int +fill_ext_raw_part_geom (DosRawPartition* raw_part, PedCHSGeometry* bios_geom, + PedGeometry* geom, PedSector offset) +{ + PED_ASSERT (raw_part != NULL, return 0); + PED_ASSERT (geom != NULL, return 0); + PED_ASSERT (geom->dev != NULL, return 0); + + raw_part->boot_ind = 0; + raw_part->type = PARTITION_EXT; + raw_part->start = PED_CPU_TO_LE32 ((geom->start - offset) + / (geom->dev->sector_size / 512)); + raw_part->length = PED_CPU_TO_LE32 (geom->length + / (geom->dev->sector_size / 512)); + + sector_to_chs (geom->dev, bios_geom, geom->start, &raw_part->chs_start); + sector_to_chs (geom->dev, bios_geom, geom->start + geom->length - 1, + &raw_part->chs_end); + + return 1; +} + +static int +write_ext_table (PedDisk* disk, PedSector sector, PedPartition* logical) +{ + DosRawTable table; + PedPartition* part; + PedSector lba_offset; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (ped_disk_extended_partition (disk) != NULL, return 0); + PED_ASSERT (logical != NULL, return 0); + + lba_offset = ped_disk_extended_partition (disk)->geom.start; + + memset (&table, 0, sizeof (DosRawTable)); + table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC); + + if (!fill_raw_part (&table.partitions[0], logical, sector)) + return 0; + + part = ped_disk_get_partition (disk, logical->num + 1); + if (part) { + PedGeometry* geom; + PedCHSGeometry bios_geom; + + geom = ped_geometry_new (disk->dev, part->prev->geom.start, + part->geom.end - part->prev->geom.start + 1); + if (!geom) + return 0; + partition_probe_bios_geometry (part, &bios_geom); + fill_ext_raw_part_geom (&table.partitions[1], &bios_geom, + geom, lba_offset); + ped_geometry_destroy (geom); + + if (!write_ext_table (disk, part->prev->geom.start, part)) + return 0; + } + + return ped_device_write (disk->dev, (void*) &table, sector, 1); +} + +static int +write_empty_table (PedDisk* disk, PedSector sector) +{ + DosRawTable table; + + PED_ASSERT (disk != NULL, return 0); + + memset (&table, 0, sizeof (DosRawTable)); + table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC); + + return ped_device_write (disk->dev, (void*) &table, sector, 1); +} + +/* Find the first logical partition, and write the partition table for it. + */ +static int +write_extended_partitions (PedDisk* disk) +{ + PedPartition* ext_part; + PedPartition* part; + PedCHSGeometry bios_geom; + + PED_ASSERT (disk != NULL, return 0); + + ext_part = ped_disk_extended_partition (disk); + partition_probe_bios_geometry (ext_part, &bios_geom); + part = ped_disk_get_partition (disk, 5); + if (part) + return write_ext_table (disk, ext_part->geom.start, part); + else + return write_empty_table (disk, ext_part->geom.start); +} + +static inline uint32_t generate_random_id() +{ + struct timeval tv; + int rc; + rc = gettimeofday(&tv, NULL); + if (rc == -1) + return 0; + return (uint32_t)(tv.tv_usec & 0xFFFFFFFFUL); +} + +static int +msdos_write (PedDisk* disk) +{ + DosRawTable table; + PedPartition* part; + int i; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + ped_device_read (disk->dev, &table, 0, 1); + + if (!table.boot_code[0]) { + memset (table.boot_code, 0, 512); + memcpy (table.boot_code, MBR_BOOT_CODE, sizeof (MBR_BOOT_CODE)); + } + + /* If there is no unique identifier, generate a random one */ + if (!table.mbr_signature) + table.mbr_signature = generate_random_id(); + + memset (table.partitions, 0, sizeof (DosRawPartition) * 4); + table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC); + + for (i=1; i<=4; i++) { + part = ped_disk_get_partition (disk, i); + if (!part) + continue; + + if (!fill_raw_part (&table.partitions [i - 1], part, 0)) + return 0; + + if (part->type == PED_PARTITION_EXTENDED) { + if (!write_extended_partitions (disk)) + return 0; + } + } + + if (!ped_device_write (disk->dev, (void*) &table, 0, 1)) + return 0; + return ped_device_sync (disk->dev); +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +msdos_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + DosPartitionData* dos_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = dos_data = ped_malloc (sizeof (DosPartitionData)); + if (!dos_data) + goto error_free_part; + dos_data->orig = NULL; + dos_data->system = PARTITION_LINUX; + dos_data->hidden = 0; + dos_data->boot = 0; + dos_data->raid = 0; + dos_data->lvm = 0; + dos_data->lba = 0; + dos_data->palo = 0; + dos_data->prep = 0; + } else { + part->disk_specific = NULL; + } + return part; + +error_free_dos_data: + ped_free (dos_data); +error_free_part: + ped_free (part); +error: + return 0; +} + +static PedPartition* +msdos_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + DosPartitionData* new_dos_data; + DosPartitionData* old_dos_data; + + new_part = ped_partition_new (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_dos_data = (DosPartitionData*) part->disk_specific; + new_dos_data = (DosPartitionData*) new_part->disk_specific; + new_dos_data->system = old_dos_data->system; + new_dos_data->boot = old_dos_data->boot; + new_dos_data->hidden = old_dos_data->hidden; + new_dos_data->raid = old_dos_data->raid; + new_dos_data->lvm = old_dos_data->lvm; + new_dos_data->lba = old_dos_data->lba; + new_dos_data->palo = old_dos_data->palo; + new_dos_data->prep = old_dos_data->prep; + + if (old_dos_data->orig) { + new_dos_data->orig = ped_malloc (sizeof (OrigState)); + if (!new_dos_data->orig) { + ped_partition_destroy (new_part); + return NULL; + } + new_dos_data->orig->geom = old_dos_data->orig->geom; + new_dos_data->orig->raw_part = old_dos_data->orig->raw_part; + new_dos_data->orig->lba_offset = old_dos_data->orig->lba_offset; + } + return new_part; +} + +static void +msdos_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + + if (ped_partition_is_active (part)) { + DosPartitionData* dos_data; + dos_data = (DosPartitionData*) part->disk_specific; + if (dos_data->orig) + ped_free (dos_data->orig); + ped_free (part->disk_specific); + } + ped_free (part); +} + +static int +msdos_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type) +{ + DosPartitionData* dos_data = part->disk_specific; + + part->fs_type = fs_type; + + if (dos_data->hidden + && fs_type + && strncmp (fs_type->name, "fat", 3) != 0 + && strcmp (fs_type->name, "ntfs") != 0) + dos_data->hidden = 0; + + if (part->type & PED_PARTITION_EXTENDED) { + dos_data->raid = 0; + dos_data->lvm = 0; + dos_data->palo = 0; + dos_data->prep = 0; + if (dos_data->lba) + dos_data->system = PARTITION_EXT_LBA; + else + dos_data->system = PARTITION_EXT; + return 1; + } + + if (dos_data->lvm) { + dos_data->system = PARTITION_LINUX_LVM; + return 1; + } + if (dos_data->raid) { + dos_data->system = PARTITION_LINUX_RAID; + return 1; + } + if (dos_data->palo) { + dos_data->system = PARTITION_PALO; + return 1; + } + if (dos_data->prep) { + dos_data->system = PARTITION_PREP; + return 1; + } + + if (!fs_type) + dos_data->system = PARTITION_LINUX; + else if (!strcmp (fs_type->name, "fat16")) { + dos_data->system = dos_data->lba + ? PARTITION_FAT16_LBA : PARTITION_FAT16; + dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0; + } else if (!strcmp (fs_type->name, "fat32")) { + dos_data->system = dos_data->lba + ? PARTITION_FAT32_LBA : PARTITION_FAT32; + dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0; + } else if (!strcmp (fs_type->name, "ntfs") + || !strcmp (fs_type->name, "hpfs")) { + dos_data->system = PARTITION_NTFS; + dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0; + } else if (!strcmp (fs_type->name, "sun-ufs")) + dos_data->system = PARTITION_SUN_UFS; + else if (!strcmp (fs_type->name, "linux-swap")) + dos_data->system = PARTITION_LINUX_SWAP; + else + dos_data->system = PARTITION_LINUX; + + return 1; +} + +static int +msdos_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + PedDisk* disk; + PedPartition* walk; + DosPartitionData* dos_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + + dos_data = part->disk_specific; + disk = part->disk; + + switch (flag) { + case PED_PARTITION_HIDDEN: + if (part->type == PED_PARTITION_EXTENDED) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Extended partitions cannot be hidden on " + "msdos disk labels.")); + return 0; + } + dos_data->hidden = state; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_BOOT: + dos_data->boot = state; + if (!state) + return 1; + + walk = ped_disk_next_partition (disk, NULL); + for (; walk; walk = ped_disk_next_partition (disk, walk)) { + if (walk == part || !ped_partition_is_active (walk)) + continue; + msdos_partition_set_flag (walk, PED_PARTITION_BOOT, 0); + } + return 1; + + case PED_PARTITION_RAID: + if (state) { + dos_data->hidden = 0; + dos_data->lvm = 0; + dos_data->palo = 0; + dos_data->prep = 0; + } + dos_data->raid = state; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_LVM: + if (state) { + dos_data->hidden = 0; + dos_data->raid = 0; + dos_data->palo = 0; + dos_data->prep = 0; + } + dos_data->lvm = state; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_LBA: + dos_data->lba = state; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_PALO: + if (state) { + dos_data->hidden = 0; + dos_data->raid = 0; + dos_data->lvm = 0; + } + dos_data->palo = state; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_PREP: + if (state) { + dos_data->hidden = 0; + dos_data->raid = 0; + dos_data->lvm = 0; + } + dos_data->prep = state; + return ped_partition_set_system (part, part->fs_type); + + default: + return 0; + } +} + +static int +msdos_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + DosPartitionData* dos_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + dos_data = part->disk_specific; + switch (flag) { + case PED_PARTITION_HIDDEN: + return dos_data->hidden; + + case PED_PARTITION_BOOT: + return dos_data->boot; + + case PED_PARTITION_RAID: + return dos_data->raid; + + case PED_PARTITION_LVM: + return dos_data->lvm; + + case PED_PARTITION_LBA: + return dos_data->lba; + + case PED_PARTITION_PALO: + return dos_data->palo; + + case PED_PARTITION_PREP: + return dos_data->prep; + + default: + return 0; + } +} + +static int +msdos_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_HIDDEN: + case PED_PARTITION_BOOT: + case PED_PARTITION_RAID: + case PED_PARTITION_LVM: + case PED_PARTITION_LBA: + case PED_PARTITION_PALO: + case PED_PARTITION_PREP: + return 1; + + default: + return 0; + } +} + +static PedGeometry* +_try_constraint (PedPartition* part, const PedConstraint* external, + PedConstraint* internal) +{ + PedConstraint* intersection; + PedGeometry* solution; + + intersection = ped_constraint_intersect (external, internal); + ped_constraint_destroy (internal); + if (!intersection) + return NULL; + + solution = ped_constraint_solve_nearest (intersection, &part->geom); + ped_constraint_destroy (intersection); + return solution; +} + +static PedGeometry* +_best_solution (const PedPartition* part, const PedCHSGeometry* bios_geom, + PedGeometry* a, PedGeometry* b) +{ + PedDevice* dev = part->disk->dev; + PedSector cyl_size = bios_geom->heads * bios_geom->sectors; + int a_cylinder; + int b_cylinder; + + if (!a) + return b; + if (!b) + return a; + + a_cylinder = a->start / cyl_size; + b_cylinder = b->start / cyl_size; + + if (a_cylinder == b_cylinder) { + if ( (a->start / bios_geom->sectors) % bios_geom->heads + < (b->start / bios_geom->sectors) % bios_geom->heads) + goto choose_a; + else + goto choose_b; + } else { + PedSector a_delta; + PedSector b_delta; + + a_delta = abs (part->geom.start - a->start); + b_delta = abs (part->geom.start - b->start); + + if (a_delta < b_delta) + goto choose_a; + else + goto choose_b; + } + + return NULL; /* never get here! */ + +choose_a: + ped_geometry_destroy (b); + return a; + +choose_b: + ped_geometry_destroy (a); + return b; +} + +/* This constraint is for "normal" primary partitions, that start at the + * beginning of a cylinder, and end at the end of a cylinder. + * Note: you can't start a partition at the beginning of the 1st + * cylinder, because that's where the partition table is! There are different + * rules for that - see the _primary_start_constraint. + */ +static PedConstraint* +_primary_constraint (PedDisk* disk, const PedCHSGeometry* bios_geom, + PedGeometry* min_geom) +{ + PedDevice* dev = disk->dev; + PedSector cylinder_size = bios_geom->sectors * bios_geom->heads; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry start_geom; + PedGeometry end_geom; + + if (!ped_alignment_init (&start_align, 0, cylinder_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, cylinder_size)) + return NULL; + + if (min_geom) { + if (min_geom->start < cylinder_size) + return NULL; + if (!ped_geometry_init (&start_geom, dev, cylinder_size, + min_geom->start + 1 - cylinder_size)) + return NULL; + if (!ped_geometry_init (&end_geom, dev, min_geom->end, + dev->length - min_geom->end)) + return NULL; + } else { + if (!ped_geometry_init (&start_geom, dev, cylinder_size, + dev->length - cylinder_size)) + return NULL; + if (!ped_geometry_init (&end_geom, dev, 0, dev->length)) + return NULL; + } + + return ped_constraint_new (&start_align, &end_align, &start_geom, + &end_geom, 1, dev->length); +} + +/* This constraint is for partitions starting on the first cylinder. They + * must start on the 2nd head of the 1st cylinder. + */ +static PedConstraint* +_primary_start_constraint (PedDisk* disk, const PedCHSGeometry* bios_geom, + PedGeometry* min_geom) +{ + PedDevice* dev = disk->dev; + PedSector cylinder_size = bios_geom->sectors * bios_geom->heads; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry start_geom; + PedGeometry end_geom; + + if (!ped_alignment_init (&start_align, bios_geom->sectors, 0)) + return NULL; + if (!ped_alignment_init (&end_align, -1, cylinder_size)) + return NULL; + if (min_geom) { + if (!ped_geometry_init (&start_geom, dev, + bios_geom->sectors, 1)) + return NULL; + if (!ped_geometry_init (&end_geom, dev, min_geom->end, + dev->length - min_geom->end)) + return NULL; + } else { + if (!ped_geometry_init (&start_geom, dev, bios_geom->sectors, + dev->length - bios_geom->sectors)) + return NULL; + if (!ped_geometry_init (&end_geom, dev, 0, dev->length)) + return NULL; + } + + return ped_constraint_new (&start_align, &end_align, &start_geom, + &end_geom, 1, dev->length); +} + +/* constraints for logical partitions: + * - start_offset is the offset in the start alignment. "normally", + * this is bios_geom->sectors. exceptions: MINOR > 5 at the beginning of the + * extended partition, or MINOR == 5 in the middle of the extended partition + * - is_start_part == 1 if the constraint is for the first cylinder of + * the extended partition, or == 0 if the constraint is for the second cylinder + * onwards of the extended partition. + */ +static PedConstraint* +_logical_constraint (PedDisk* disk, const PedCHSGeometry* bios_geom, + PedSector start_offset, int is_start_part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedDevice* dev = disk->dev; + PedSector cylinder_size = bios_geom->sectors * bios_geom->heads; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + + PED_ASSERT (ext_part != NULL, return NULL); + + if (!ped_alignment_init (&start_align, start_offset, cylinder_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, cylinder_size)) + return NULL; + if (is_start_part) { + if (!ped_geometry_init (&max_geom, dev, + ext_part->geom.start, + ext_part->geom.length)) + return NULL; + } else { + PedSector min_start; + PedSector max_length; + + min_start = ped_round_up_to (ext_part->geom.start + 1, + cylinder_size); + max_length = ext_part->geom.end - min_start + 1; + if (min_start >= ext_part->geom.end) + return NULL; + + if (!ped_geometry_init (&max_geom, dev, min_start, max_length)) + return NULL; + } + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, dev->length); +} + +/* returns the minimum geometry for the extended partition, given that the + * extended partition must contain: + * * all logical partitions + * * all partition tables for all logical partitions (except the first) + * * the extended partition table + */ +static PedGeometry* +_get_min_extended_part_geom (const PedPartition* ext_part, + const PedCHSGeometry* bios_geom) +{ + PedDisk* disk = ext_part->disk; + PedSector head_size = bios_geom ? bios_geom->sectors : 1; + PedPartition* walk; + PedGeometry* min_geom; + + walk = ped_disk_get_partition (disk, 5); + if (!walk) + return NULL; + + min_geom = ped_geometry_duplicate (&walk->geom); + if (!min_geom) + return NULL; + ped_geometry_set_start (min_geom, walk->geom.start - 1 * head_size); + + for (walk = ext_part->part_list; walk; walk = walk->next) { + if (!ped_partition_is_active (walk) || walk->num == 5) + continue; + if (walk->geom.start < min_geom->start) + ped_geometry_set_start (min_geom, + walk->geom.start - 2 * head_size); + if (walk->geom.end > min_geom->end) + ped_geometry_set_end (min_geom, walk->geom.end); + } + + return min_geom; +} + +static int +_align_primary (PedPartition* part, const PedCHSGeometry* bios_geom, + const PedConstraint* constraint) +{ + PedDisk* disk = part->disk; + PedDevice* dev = disk->dev; + PedGeometry* min_geom = NULL; + PedGeometry* solution = NULL; + + if (part->type == PED_PARTITION_EXTENDED) + min_geom = _get_min_extended_part_geom (part, bios_geom); + + solution = _best_solution (part, bios_geom, solution, + _try_constraint (part, constraint, + _primary_start_constraint (disk, + bios_geom, min_geom))); + solution = _best_solution (part, bios_geom, solution, + _try_constraint (part, constraint, + _primary_constraint (disk, bios_geom, + min_geom))); + + if (min_geom) + ped_geometry_destroy (min_geom); + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + + return 0; +} + +static int +_logical_min_start_head (PedPartition* part, const PedCHSGeometry* bios_geom, + PedPartition* ext_part, int is_start_ext_part) +{ + PedDevice* dev = part->disk->dev; + PedSector cylinder_size = bios_geom->sectors * bios_geom->heads; + PedSector base_head; + + if (is_start_ext_part) + base_head = 1 + (ext_part->geom.start % cylinder_size) + / bios_geom->sectors; + else + base_head = 0; + + if (part->num == 5) + return base_head + 0; + else + return base_head + 1; +} + +/* Shamelessly copied and adapted from _partition_get_overlap_constraint + * (in disk.c) + * This should get ride of the infamous Assertion (metadata_length > 0) failed + * bug for extended msdos disklabels generated by Parted. + * 1) There always is a partition table at the start of ext_part, so we leave + * a one sector gap there. + * 2)*The partition table of part5 is always at the beginning of the ext_part + * so there is no need to leave a one sector gap before part5. + * *There always is a partition table at the beginning of each partition != 5. + * We don't need to worry to much about consistency with + * _partition_get_overlap_constraint because missing it means we are in edge + * cases anyway, and we don't lose anything by just refusing to do the job in + * those cases. + */ +static PedConstraint* +_log_meta_overlap_constraint (PedPartition* part, PedGeometry* geom) +{ + PedGeometry safe_space; + PedSector min_start; + PedSector max_end; + PedPartition* ext_part = ped_disk_extended_partition (part->disk); + PedPartition* walk; + int not_5 = (part->num != 5); + + PED_ASSERT (ext_part != NULL, return NULL); + + walk = ext_part->part_list; + + /* 1) 2) */ + min_start = ext_part->geom.start + 1 + not_5; + max_end = ext_part->geom.end; + + while (walk != NULL /* 2) 2) */ + && ( walk->geom.start - (walk->num != 5) < geom->start - not_5 + || walk->geom.start - (walk->num != 5) <= min_start )) { + if (walk != part && ped_partition_is_active (walk)) + min_start = walk->geom.end + 1 + not_5; /* 2) */ + walk = walk->next; + } + + while (walk && (walk == part || !ped_partition_is_active (walk))) + walk = walk->next; + + if (walk) + max_end = walk->geom.start - 1 - (walk->num != 5); /* 2) */ + + if (min_start >= max_end) + return NULL; + + ped_geometry_init (&safe_space, part->disk->dev, + min_start, max_end - min_start + 1); + return ped_constraint_new_from_max (&safe_space); +} + +static int +_align_logical (PedPartition* part, const PedCHSGeometry* bios_geom, + const PedConstraint* constraint) +{ + PedDisk* disk = part->disk; + PedDevice* dev = disk->dev; + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedSector cyl_size = bios_geom->sectors * bios_geom->heads; + PedSector start_base; + int head; + PedGeometry* solution = NULL; + PedConstraint *intersect, *log_meta_overlap; + + PED_ASSERT (ext_part != NULL, return 0); + + log_meta_overlap = _log_meta_overlap_constraint(part, &part->geom); + intersect = ped_constraint_intersect (constraint, log_meta_overlap); + ped_constraint_destroy (log_meta_overlap); + if (!intersect) + return 0; + + start_base = ped_round_down_to (part->geom.start, cyl_size); + + for (head = _logical_min_start_head (part, bios_geom, ext_part, 0); + head < PED_MIN (5, bios_geom->heads); head++) { + PedConstraint* disk_constraint; + PedSector start = start_base + head * bios_geom->sectors; + + if (head >= _logical_min_start_head (part, bios_geom, + ext_part, 1)) + disk_constraint = + _logical_constraint (disk, bios_geom, start, 1); + else + disk_constraint = + _logical_constraint (disk, bios_geom, start, 0); + + solution = _best_solution (part, bios_geom, solution, + _try_constraint (part, intersect, + disk_constraint)); + } + + ped_constraint_destroy (intersect); + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + + return 0; +} + +static int +_align (PedPartition* part, const PedCHSGeometry* bios_geom, + const PedConstraint* constraint) +{ + if (part->type == PED_PARTITION_LOGICAL) + return _align_logical (part, bios_geom, constraint); + else + return _align_primary (part, bios_geom, constraint); +} + +static PedConstraint* +_no_geom_constraint (PedDisk* disk, PedSector start, PedSector end) +{ + PedGeometry max; + + ped_geometry_init (&max, disk->dev, start, end - start + 1); + return ped_constraint_new_from_max (&max); +} + +static PedConstraint* +_no_geom_extended_constraint (PedPartition* part) +{ + PedDevice* dev = part->disk->dev; + PedGeometry* min = _get_min_extended_part_geom (part, NULL); + PedGeometry start_range; + PedGeometry end_range; + PedConstraint* constraint; + + if (min) { + ped_geometry_init (&start_range, dev, 1, min->start); + ped_geometry_init (&end_range, dev, min->end, + dev->length - min->end); + ped_geometry_destroy (min); + } else { + ped_geometry_init (&start_range, dev, 1, dev->length - 1); + ped_geometry_init (&end_range, dev, 1, dev->length - 1); + } + constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any, + &start_range, &end_range, 1, dev->length); + return constraint; +} + +static int +_align_primary_no_geom (PedPartition* part, const PedConstraint* constraint) +{ + PedDisk* disk = part->disk; + PedGeometry* solution; + + if (part->type == PED_PARTITION_EXTENDED) { + solution = _try_constraint (part, constraint, + _no_geom_extended_constraint (part)); + } else { + solution = _try_constraint (part, constraint, + _no_geom_constraint (disk, 1, + disk->dev->length - 1)); + } + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + return 0; +} + +static int +_align_logical_no_geom (PedPartition* part, const PedConstraint* constraint) +{ + PedGeometry* solution; + + solution = _try_constraint (part, constraint, + _log_meta_overlap_constraint (part, &part->geom)); + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + return 0; +} + +static int +_align_no_geom (PedPartition* part, const PedConstraint* constraint) +{ + if (part->type == PED_PARTITION_LOGICAL) + return _align_logical_no_geom (part, constraint); + else + return _align_primary_no_geom (part, constraint); +} + +static int +msdos_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PedCHSGeometry bios_geom; + DosPartitionData* dos_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + dos_data = part->disk_specific; + if (dos_data->system == PARTITION_LDM && dos_data->orig) { + PedGeometry *orig_geom = &dos_data->orig->geom; + + if (ped_geometry_test_equal (&part->geom, orig_geom) + && ped_constraint_is_solution (constraint, &part->geom)) + return 1; + + ped_geometry_set (&part->geom, orig_geom->start, + orig_geom->length); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Parted can't resize partitions managed by " + "Windows Dynamic Disk.")); + return 0; + } + + partition_probe_bios_geometry (part, &bios_geom); + + if (_align (part, &bios_geom, constraint)) + return 1; + if (_align_no_geom (part, constraint)) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +add_metadata_part (PedDisk* disk, PedPartitionType type, PedSector start, + PedSector end) +{ + PedPartition* new_part; + + PED_ASSERT (disk != NULL, return 0); + + new_part = ped_partition_new (disk, type | PED_PARTITION_METADATA, NULL, + start, end); + if (!new_part) + goto error; + if (!ped_disk_add_partition (disk, new_part, NULL)) + goto error_destroy_new_part; + + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + return 0; +} + +/* There are a few objectives here: + * - avoid having lots of "free space" partitions lying around, to confuse + * the front end. + * - ensure that there's enough room to put in the extended partition + * tables, etc. + */ +static int +add_logical_part_metadata (PedDisk* disk, PedPartition* log_part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedPartition* prev = log_part->prev; + PedDevice* dev = disk->dev; + PedCHSGeometry bios_geom; + PedSector cyl_size; + PedSector metadata_start; + PedSector metadata_end; + PedSector metadata_length; + + partition_probe_bios_geometry (ext_part, &bios_geom); + cyl_size = bios_geom.sectors * bios_geom.heads; + + /* if there's metadata shortly before the partition (on the same + * cylinder), then make this new metadata partition touch the end of + * the other. No point having 63 bytes (or whatever) of free space + * partition - just confuses front-ends, etc. + * Otherwise, start the metadata at the start of the cylinder + */ + + metadata_end = log_part->geom.start - 1; + metadata_start = ped_round_down_to (metadata_end, cyl_size); + if (prev) + metadata_start = PED_MAX (metadata_start, prev->geom.end + 1); + else + metadata_start = PED_MAX (metadata_start, + ext_part->geom.start + 1); + metadata_length = metadata_end - metadata_start + 1; + + /* partition 5 doesn't need to have any metadata */ + if (log_part->num == 5 && metadata_length < bios_geom.sectors) + return 1; + + PED_ASSERT (metadata_length > 0, return 0); + + return add_metadata_part (disk, PED_PARTITION_LOGICAL, + metadata_start, metadata_end); +} + +static PedPartition* +get_last_part (PedDisk* disk) +{ + PedPartition* first_part = disk->part_list; + PedPartition* walk; + + if (!first_part) + return NULL; + for (walk = first_part; walk->next; walk = walk->next); + return walk; +} + +/* Adds metadata placeholder partitions to cover the partition table (and + * "free" space after it that often has bootloader stuff), and the last + * incomplete cylinder at the end of the disk. + * Parted has to be mindful of the uncertainty of dev->bios_geom. + * It therefore makes sure this metadata doesn't overlap with partitions. + */ +static int +add_startend_metadata (PedDisk* disk) +{ + PedDevice* dev = disk->dev; + PedSector cyl_size = dev->bios_geom.sectors * dev->bios_geom.heads; + PedPartition* first_part = disk->part_list; + PedPartition* last_part = get_last_part (disk); + PedSector start, end; + + if (!first_part) + return 1; + + start = 0; + end = PED_MIN (dev->bios_geom.sectors - 1, first_part->geom.start - 1); + if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end)) + return 0; + + start = PED_MAX (last_part->geom.end + 1, + ped_round_down_to (dev->length, cyl_size)); + end = dev->length - 1; + if (start < end) { + if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end)) + return 0; + } + + return 1; +} + +static int +msdos_alloc_metadata (PedDisk* disk) +{ + PedPartition* ext_part; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + if (!add_startend_metadata (disk)) + return 0; + + ext_part = ped_disk_extended_partition (disk); + if (ext_part) { + int i; + PedSector start, end; + PedCHSGeometry bios_geom; + + for (i=5; 1; i++) { + PedPartition* log_part; + log_part = ped_disk_get_partition (disk, i); + if (!log_part) + break; + if (!add_logical_part_metadata (disk, log_part)) + return 0; + } + + partition_probe_bios_geometry (ext_part, &bios_geom); + start = ext_part->geom.start; + end = start + bios_geom.sectors - 1; + if (ext_part->part_list) + end = PED_MIN (end, + ext_part->part_list->geom.start - 1); + if (!add_metadata_part (disk, PED_PARTITION_LOGICAL, + start, end)) + return 0; + } + + return 1; +} + +static int +next_primary (PedDisk* disk) +{ + int i; + for (i=1; i<=4; i++) { + if (!ped_disk_get_partition (disk, i)) + return i; + } + return 0; +} + +static int +next_logical (PedDisk* disk) +{ + int i; + for (i=5; 1; i++) { + if (!ped_disk_get_partition (disk, i)) + return i; + } +} + +static int +msdos_partition_enumerate (PedPartition* part) +{ + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + + /* don't re-number a primary partition */ + if (part->num != -1 && part->num <= 4) + return 1; + + part->num = -1; + + if (part->type & PED_PARTITION_LOGICAL) + part->num = next_logical (part->disk); + else + part->num = next_primary (part->disk); + + return 1; +} + +static int +msdos_get_max_primary_partition_count (const PedDisk* disk) +{ + return 4; +} + +static PedDiskOps msdos_disk_ops = { + probe: msdos_probe, +#ifndef DISCOVER_ONLY + clobber: msdos_clobber, +#else + clobber: NULL, +#endif + alloc: msdos_alloc, + duplicate: msdos_duplicate, + free: msdos_free, + read: msdos_read, +#ifndef DISCOVER_ONLY + write: msdos_write, +#else + write: NULL, +#endif + + partition_new: msdos_partition_new, + partition_duplicate: msdos_partition_duplicate, + partition_destroy: msdos_partition_destroy, + partition_set_system: msdos_partition_set_system, + partition_set_flag: msdos_partition_set_flag, + partition_get_flag: msdos_partition_get_flag, + partition_is_flag_available: msdos_partition_is_flag_available, + partition_set_name: NULL, + partition_get_name: NULL, + partition_align: msdos_partition_align, + partition_enumerate: msdos_partition_enumerate, + + alloc_metadata: msdos_alloc_metadata, + get_max_primary_partition_count: + msdos_get_max_primary_partition_count +}; + +static PedDiskType msdos_disk_type = { + next: NULL, + name: "msdos", + ops: &msdos_disk_ops, + features: PED_DISK_TYPE_EXTENDED +}; + +void +ped_disk_msdos_init () +{ + PED_ASSERT (sizeof (DosRawPartition) == 16, return); + PED_ASSERT (sizeof (DosRawTable) == 512, return); + + ped_register_disk_type (&msdos_disk_type); +} + +void +ped_disk_msdos_done () +{ + ped_unregister_disk_type (&msdos_disk_type); +} + diff --git a/libparted/labels/dvh.c b/libparted/labels/dvh.c new file mode 100644 index 0000000..12eece1 --- /dev/null +++ b/libparted/labels/dvh.c @@ -0,0 +1,916 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001, 2002, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <string.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "dvh.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* Default size for volhdr part, same val as IRIX's fx uses */ +#define PTYPE_VOLHDR_DFLTSZ 4096 + +/* Partition numbers that seem to be strongly held convention */ +#define PNUM_VOLHDR 8 +#define PNUM_VOLUME 10 + +/* Other notes of interest: + * PED_PARTITION_EXTENDED is used for volume headers + * PED_PARTITION_LOGICAL is used for bootfiles + * PED_PARTITION_NORMAL is used for all else + */ + +typedef struct _DVHDiskData { + struct device_parameters dev_params; + int swap; /* part num of swap, 0=none */ + int root; /* part num of root, 0=none */ + int boot; /* part num of boot, 0=none */ +} DVHDiskData; + +typedef struct _DVHPartData { + int type; + char name[VDNAMESIZE + 1]; /* boot volumes only */ + int real_file_size; /* boot volumes only */ +} DVHPartData; + +static PedDiskType dvh_disk_type; + +static int +dvh_probe (const PedDevice *dev) +{ + struct volume_header vh; + + if (dev->sector_size != 512) + return 0; + + if (!ped_device_read (dev, &vh, 0, 1)) + return 0; + + return PED_BE32_TO_CPU (vh.vh_magic) == VHMAGIC; +} + +#ifndef DISCOVER_ONLY +static int +dvh_clobber (PedDevice* dev) +{ + char zeros[512]; + + memset (zeros, 0, 512); + return ped_device_write (dev, zeros, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + +static PedDisk* +dvh_alloc (const PedDevice* dev) +{ + PedDisk* disk; + DVHDiskData* dvh_disk_data; + PedPartition* volume_part; + PedConstraint* constraint_any; + + disk = _ped_disk_alloc (dev, &dvh_disk_type); + if (!disk) + goto error; + + disk->disk_specific = dvh_disk_data + = ped_malloc (sizeof (DVHDiskData)); + if (!dvh_disk_data) + goto error_free_disk; + + memset (&dvh_disk_data->dev_params, 0, + sizeof (struct device_parameters)); + dvh_disk_data->swap = 0; + dvh_disk_data->root = 0; + dvh_disk_data->boot = 0; + + volume_part = ped_partition_new (disk, PED_PARTITION_EXTENDED, NULL, + 0, PTYPE_VOLHDR_DFLTSZ - 1); + if (!volume_part) + goto error_free_disk_specific; + volume_part->num = PNUM_VOLHDR + 1; + constraint_any = ped_constraint_any (dev); + if (!ped_disk_add_partition (disk, volume_part, constraint_any)) + goto error_destroy_constraint_any; + ped_constraint_destroy (constraint_any); + return disk; + +error_destroy_constraint_any: + ped_constraint_destroy (constraint_any); +error_destroy_volume_part: + ped_partition_destroy (volume_part); +error_free_disk_specific: + ped_free (disk->disk_specific); +error_free_disk: + ped_free (disk); +error: + return NULL; +} + +static PedDisk* +dvh_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + DVHDiskData* new_dvh_disk_data; + DVHDiskData* old_dvh_disk_data = disk->disk_specific; + + PED_ASSERT (old_dvh_disk_data != NULL, goto error); + + new_disk = _ped_disk_alloc (disk->dev, &dvh_disk_type); + if (!new_disk) + goto error; + + new_disk->disk_specific = new_dvh_disk_data + = ped_malloc (sizeof (DVHDiskData)); + if (!new_dvh_disk_data) + goto error_free_new_disk; + + new_dvh_disk_data->dev_params = old_dvh_disk_data->dev_params; + return new_disk; + +error_free_new_disk: + ped_free (new_disk); +error: + return NULL; +} + +static void +dvh_free (PedDisk* disk) +{ + ped_free (disk->disk_specific); + _ped_disk_free (disk); +} + +/* two's complement 32-bit checksum */ +static uint32_t +_checksum (const uint32_t* base, size_t size) +{ + uint32_t sum = 0; + size_t i; + + for (i = 0; i < size / sizeof (uint32_t); i++) + sum = sum - PED_BE32_TO_CPU (base[i]); + + return sum; +} + +/* try to make a reasonable volume header partition... */ +static PedExceptionOption +_handle_no_volume_header (PedDisk* disk) +{ + PedExceptionOption ret; + PedPartition* part; + PedConstraint* constraint; + + switch (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX + PED_EXCEPTION_CANCEL, + _("%s has no extended partition (volume header partition)."), + disk->dev->path)) { + case PED_EXCEPTION_UNHANDLED: + case PED_EXCEPTION_FIX: + default: + part = ped_partition_new ( + disk, PED_PARTITION_EXTENDED, NULL, + 0, PTYPE_VOLHDR_DFLTSZ - 1); + if (!part) + goto error; + part->num = PNUM_VOLHDR + 1; + constraint = ped_constraint_any (part->disk->dev); + if (!constraint) + goto error_destroy_part; + if (!ped_disk_add_partition (disk, part, constraint)) + goto error_destroy_constraint; + ped_constraint_destroy (constraint); + ret = PED_EXCEPTION_FIX; + break; + + case PED_EXCEPTION_CANCEL: + goto error; + } + return ret; + +error_destroy_constraint: + ped_constraint_destroy (constraint); +error_destroy_part: + ped_partition_destroy (part); +error: + return PED_EXCEPTION_CANCEL; +} + +static PedPartition* +_parse_partition (PedDisk* disk, struct partition_table* pt) +{ + PedPartition* part; + DVHPartData* dvh_part_data; + PedSector start = PED_BE32_TO_CPU (pt->pt_firstlbn); + PedSector length = PED_BE32_TO_CPU (pt->pt_nblks); + + part = ped_partition_new (disk, + pt->pt_type ? 0 : PED_PARTITION_EXTENDED, + NULL, + start, start + length - 1); + if (!part) + return NULL; + + dvh_part_data = part->disk_specific; + dvh_part_data->type = PED_BE32_TO_CPU (pt->pt_type); + strcpy (dvh_part_data->name, ""); + + return part; +} + +static PedPartition* +_parse_boot_file (PedDisk* disk, struct volume_directory* vd) +{ + PedPartition* part; + DVHPartData* dvh_part_data; + PedSector start = PED_BE32_TO_CPU (vd->vd_lbn); + int length = PED_BE32_TO_CPU (vd->vd_nbytes); + + part = ped_partition_new (disk, PED_PARTITION_LOGICAL, NULL, + start, start + length/512 - 1); + if (!part) + return NULL; + + dvh_part_data = part->disk_specific; + dvh_part_data->real_file_size = length; + + strncpy (dvh_part_data->name, vd->vd_name, VDNAMESIZE); + dvh_part_data->name[VDNAMESIZE] = 0; + return part; +} + +static int dvh_write (PedDisk* disk); + +/* YUCK + * + * If you remove a boot/root/swap partition, the disk->disk_specific + * thing isn't updated. (Probably reflects a design bug somewhere...) + * Anyway, the workaround is: flush stale flags whenever we allocate + * new partition numbers, and before we write to disk. + */ +static void +_flush_stale_flags (PedDisk* disk) +{ + DVHDiskData* dvh_disk_data = disk->disk_specific; + + if (dvh_disk_data->root + && !ped_disk_get_partition (disk, dvh_disk_data->root)) + dvh_disk_data->root = 0; + if (dvh_disk_data->swap + && !ped_disk_get_partition (disk, dvh_disk_data->swap)) + dvh_disk_data->swap = 0; + if (dvh_disk_data->boot + && !ped_disk_get_partition (disk, dvh_disk_data->boot)) + dvh_disk_data->boot = 0; +} + +static int +dvh_read (PedDisk* disk) +{ + DVHDiskData* dvh_disk_data = disk->disk_specific; + int i; + struct volume_header vh; + char boot_name [BFNAMESIZE + 1]; +#ifndef DISCOVER_ONLY + int write_back = 0; +#endif + + PED_ASSERT (dvh_disk_data != NULL, return 0); + + ped_disk_delete_all (disk); + + if (!ped_device_read (disk->dev, &vh, 0, 1)) + return 0; + + if (_checksum ((uint32_t*) &vh, sizeof (struct volume_header))) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Checksum is wrong, indicating the partition " + "table is corrupt.")) + == PED_EXCEPTION_CANCEL) + return 0; + } + + PED_ASSERT (PED_BE32_TO_CPU (vh.vh_magic) == VHMAGIC, return 0); + + dvh_disk_data->dev_params = vh.vh_dp; + strncpy (boot_name, vh.vh_bootfile, BFNAMESIZE); + boot_name[BFNAMESIZE] = 0; + + /* normal partitions */ + for (i = 0; i < NPARTAB; i++) { + PedPartition* part; + PedConstraint* constraint_exact; + + if (!vh.vh_pt[i].pt_nblks) + continue; + /* Skip the whole-disk partition, parted disklikes overlap */ + if (PED_BE32_TO_CPU (vh.vh_pt[i].pt_type) == PTYPE_VOLUME) + continue; + + part = _parse_partition (disk, &vh.vh_pt[i]); + if (!part) + goto error_delete_all; + + part->fs_type = ped_file_system_probe (&part->geom); + part->num = i + 1; + + if (PED_BE16_TO_CPU (vh.vh_rootpt) == i) + ped_partition_set_flag (part, PED_PARTITION_ROOT, 1); + if (PED_BE16_TO_CPU (vh.vh_swappt) == i) + ped_partition_set_flag (part, PED_PARTITION_SWAP, 1); + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition(disk, part, constraint_exact)) { + ped_partition_destroy (part); + goto error_delete_all; + } + ped_constraint_destroy (constraint_exact); + } + + if (!ped_disk_extended_partition (disk)) { +#ifdef DISCOVER_ONLY + return 1; +#else + switch (_handle_no_volume_header (disk)) { + case PED_EXCEPTION_CANCEL: + return 0; + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_FIX: + write_back = 1; + } +#endif + } + + /* boot partitions */ + for (i = 0; i < NVDIR; i++) { + PedPartition* part; + PedConstraint* constraint_exact; + + if (!vh.vh_vd[i].vd_nbytes) + continue; + + part = _parse_boot_file (disk, &vh.vh_vd[i]); + if (!part) + goto error_delete_all; + + part->fs_type = ped_file_system_probe (&part->geom); + part->num = NPARTAB + i + 1; + + if (!strcmp (boot_name, ped_partition_get_name (part))) + ped_partition_set_flag (part, PED_PARTITION_BOOT, 1); + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition(disk, part, constraint_exact)) { + ped_partition_destroy (part); + goto error_delete_all; + } + ped_constraint_destroy (constraint_exact); + } +#ifndef DISCOVER_ONLY + if (write_back) + dvh_write (disk); +#endif + return 1; + +error_delete_all: + ped_disk_delete_all (disk); +error: + return 0; +} + +#ifndef DISCOVER_ONLY +static void +_generate_partition (PedPartition* part, struct partition_table* pt) +{ + DVHPartData* dvh_part_data = part->disk_specific; + + /* Assert not a bootfile */ + PED_ASSERT ((part->type & PED_PARTITION_LOGICAL) == 0, return); + + pt->pt_nblks = PED_CPU_TO_BE32 (part->geom.length); + pt->pt_firstlbn = PED_CPU_TO_BE32 (part->geom.start); + pt->pt_type = PED_CPU_TO_BE32 (dvh_part_data->type); +} + +static void +_generate_boot_file (PedPartition* part, struct volume_directory* vd) +{ + DVHPartData* dvh_part_data = part->disk_specific; + + /* Assert it's a bootfile */ + PED_ASSERT ((part->type & PED_PARTITION_LOGICAL) != 0, return); + + vd->vd_nbytes = PED_CPU_TO_BE32 (dvh_part_data->real_file_size); + vd->vd_lbn = PED_CPU_TO_BE32 (part->geom.start); + + memset (vd->vd_name, 0, VDNAMESIZE); + strncpy (vd->vd_name, dvh_part_data->name, VDNAMESIZE); +} + +static int +dvh_write (PedDisk* disk) +{ + DVHDiskData* dvh_disk_data = disk->disk_specific; + struct volume_header vh; + int i; + + PED_ASSERT (dvh_disk_data != NULL, return 0); + + _flush_stale_flags (disk); + + memset (&vh, 0, sizeof (struct volume_header)); + + vh.vh_magic = PED_CPU_TO_BE32 (VHMAGIC); + vh.vh_rootpt = PED_CPU_TO_BE16 (dvh_disk_data->root - 1); + vh.vh_swappt = PED_CPU_TO_BE16 (dvh_disk_data->swap - 1); + + if (dvh_disk_data->boot) { + PedPartition* boot_part; + boot_part = ped_disk_get_partition (disk, dvh_disk_data->boot); + strcpy (vh.vh_bootfile, ped_partition_get_name (boot_part)); + } + + vh.vh_dp = dvh_disk_data->dev_params; + /* Set up rudimentary device geometry */ + vh.vh_dp.dp_cyls + = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.cylinders); + vh.vh_dp.dp_trks0 = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.heads); + vh.vh_dp.dp_secs + = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.sectors); + vh.vh_dp.dp_secbytes = PED_CPU_TO_BE16 ((short)disk->dev->sector_size); + + for (i = 0; i < NPARTAB; i++) { + PedPartition* part = ped_disk_get_partition (disk, i + 1); + if (part) + _generate_partition (part, &vh.vh_pt[i]); + } + + /* whole disk partition + * This is only ever written here, and never modified + * (or even shown) as it must contain the entire disk, + * and parted does not like overlapping partitions + */ + vh.vh_pt[PNUM_VOLUME].pt_nblks = PED_CPU_TO_BE32 (disk->dev->length); + vh.vh_pt[PNUM_VOLUME].pt_firstlbn = PED_CPU_TO_BE32 (0); + vh.vh_pt[PNUM_VOLUME].pt_type = PED_CPU_TO_BE32 (PTYPE_VOLUME); + + for (i = 0; i < NVDIR; i++) { + PedPartition* part = ped_disk_get_partition (disk, + i + 1 + NPARTAB); + if (part) + _generate_boot_file (part, &vh.vh_vd[i]); + } + + vh.vh_csum = 0; + vh.vh_csum = PED_CPU_TO_BE32 (_checksum ((uint32_t*) &vh, + sizeof (struct volume_header))); + + return ped_device_write (disk->dev, &vh, 0, 1) + && ped_device_sync (disk->dev); +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +dvh_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + DVHPartData* dvh_part_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (!ped_partition_is_active (part)) { + part->disk_specific = NULL; + return part; + } + + dvh_part_data = part->disk_specific = + ped_malloc (sizeof (DVHPartData)); + if (!dvh_part_data) + goto error_free_part; + + dvh_part_data->type = (part_type == PED_PARTITION_EXTENDED) + ? PTYPE_VOLHDR + : PTYPE_RAW; + strcpy (dvh_part_data->name, ""); + dvh_part_data->real_file_size = part->geom.length * 512; + return part; + +error_free_part: + _ped_partition_free (part); +error: + return NULL; +} + +static PedPartition* +dvh_partition_duplicate (const PedPartition* part) +{ + PedPartition* result; + DVHPartData* part_data = part->disk_specific; + DVHPartData* result_data; + + result = _ped_partition_alloc (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + if (!result) + goto error; + result->num = part->num; + + if (!ped_partition_is_active (part)) { + result->disk_specific = NULL; + return result; + } + + result_data = result->disk_specific = + ped_malloc (sizeof (DVHPartData)); + if (!result_data) + goto error_free_part; + + result_data->type = part_data->type; + strcpy (result_data->name, part_data->name); + result_data->real_file_size = part_data->real_file_size; + return result; + +error_free_part: + _ped_partition_free (result); +error: + return NULL; +} + +static void +dvh_partition_destroy (PedPartition* part) +{ + if (ped_partition_is_active (part)) { + PED_ASSERT (part->disk_specific != NULL, return); + ped_free (part->disk_specific); + } + _ped_partition_free (part); +} + +static int +dvh_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + DVHPartData* dvh_part_data = part->disk_specific; + + part->fs_type = fs_type; + + if (part->type == PED_PARTITION_EXTENDED) { + dvh_part_data->type = PTYPE_VOLHDR; + return 1; + } + + /* Is this a bootfile? */ + if (part->type == PED_PARTITION_LOGICAL) + return 1; + + if (fs_type && !strcmp (fs_type->name, "xfs")) + dvh_part_data->type = PTYPE_XFS; + else + dvh_part_data->type = PTYPE_RAW; + return 1; +} + +static int +dvh_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + DVHDiskData* dvh_disk_data = part->disk->disk_specific; + + switch (flag) { + case PED_PARTITION_ROOT: + if (part->type != 0 && state) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Only primary partitions can be root " + "partitions.")); +#endif + return 0; + } + dvh_disk_data->root = state ? part->num : 0; + break; + + case PED_PARTITION_SWAP: + if (part->type != 0 && state) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Only primary partitions can be swap " + "partitions.")); + return 0; +#endif + } + dvh_disk_data->swap = state ? part->num : 0; + break; + + case PED_PARTITION_BOOT: + if (part->type != PED_PARTITION_LOGICAL && state) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Only logical partitions can be a boot " + "file.")); +#endif + return 0; + } + dvh_disk_data->boot = state ? part->num : 0; + break; + + case PED_PARTITION_LVM: + case PED_PARTITION_LBA: + case PED_PARTITION_HIDDEN: + case PED_PARTITION_RAID: + default: + return 0; + } + return 1; +} + +static int +dvh_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + DVHDiskData* dvh_disk_data = part->disk->disk_specific; + + switch (flag) { + case PED_PARTITION_ROOT: + return dvh_disk_data->root == part->num; + + case PED_PARTITION_SWAP: + return dvh_disk_data->swap == part->num; + + case PED_PARTITION_BOOT: + return dvh_disk_data->boot == part->num; + + case PED_PARTITION_LVM: + case PED_PARTITION_LBA: + case PED_PARTITION_HIDDEN: + case PED_PARTITION_RAID: + default: + return 0; + } + return 1; +} + +static int +dvh_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + DVHDiskData* dvh_disk_data = part->disk->disk_specific; + + switch (flag) { + case PED_PARTITION_ROOT: + case PED_PARTITION_SWAP: + case PED_PARTITION_BOOT: + return 1; + + case PED_PARTITION_LVM: + case PED_PARTITION_LBA: + case PED_PARTITION_HIDDEN: + case PED_PARTITION_RAID: + default: + return 0; + } + return 1; +} + +static void +dvh_partition_set_name (PedPartition* part, const char* name) +{ + DVHPartData* dvh_part_data = part->disk_specific; + + if (part->type == PED_PARTITION_LOGICAL) { + /* Bootfile */ + strncpy (dvh_part_data->name, name, VDNAMESIZE); + dvh_part_data->name[VDNAMESIZE] = 0; + } else { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Only logical partitions (boot files) have a name.")); +#endif + } +} + +static const char* +dvh_partition_get_name (const PedPartition* part) +{ + DVHPartData* dvh_part_data = part->disk_specific; + return dvh_part_data->name; +} + +/* The constraint for the volume header partition is different, because it must + * contain the first sector of the disk. + */ +static PedConstraint* +_get_extended_constraint (PedDisk* disk) +{ + PedGeometry min_geom; + if (!ped_geometry_init (&min_geom, disk->dev, 0, 1)) + return NULL; + return ped_constraint_new_from_min (&min_geom); +} + +static PedConstraint* +_get_primary_constraint (PedDisk* disk) +{ + PedGeometry max_geom; + if (!ped_geometry_init (&max_geom, disk->dev, 1, disk->dev->length - 1)) + return NULL; + return ped_constraint_new_from_max (&max_geom); +} + +static int +dvh_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL, return 0); + + if (_ped_partition_attempt_align ( + part, constraint, + (part->type == PED_PARTITION_EXTENDED) + ? _get_extended_constraint (part->disk) + : _get_primary_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +dvh_partition_enumerate (PedPartition* part) +{ + int i; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + + _flush_stale_flags (part->disk); + + if (part->type & PED_PARTITION_LOGICAL) { + /* Bootfile */ + for (i = 1 + NPARTAB; i <= NPARTAB + NVDIR; i++) { + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + return 1; + } + } + PED_ASSERT (0, return 0); + } else if (part->type & PED_PARTITION_EXTENDED) { + /* Volheader */ + part->num = PNUM_VOLHDR + 1; + } else { + for (i = 1; i <= NPARTAB; i++) { + /* reserved for full volume partition */ + if (i == PNUM_VOLUME + 1) + continue; + + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + return 1; + } + } + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Too many primary partitions")); + } + + return 0; +} + +static int +dvh_get_max_primary_partition_count (const PedDisk* disk) +{ + return NPARTAB; +} + +static int +dvh_alloc_metadata (PedDisk* disk) +{ + PedPartition* part; + PedPartition* extended_part; + PedConstraint* constraint_exact; + PedPartitionType metadata_type; + PED_ASSERT(disk != NULL, return 0); + + /* We don't need to "protect" the start of the disk from the volume + * header. + */ + extended_part = ped_disk_extended_partition (disk); + if (extended_part && extended_part->geom.start == 0) + metadata_type = PED_PARTITION_METADATA | PED_PARTITION_LOGICAL; + else + metadata_type = PED_PARTITION_METADATA; + + part = ped_partition_new (disk, metadata_type, NULL, 0, 0); + if (!part) + goto error; + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) + goto error_destroy_part; + ped_constraint_destroy (constraint_exact); + return 1; + +error_destroy_constraint: + ped_constraint_destroy (constraint_exact); +error_destroy_part: + ped_partition_destroy (part); +error: + return 0; +} + +static PedDiskOps dvh_disk_ops = { + probe: dvh_probe, +#ifndef DISCOVER_ONLY + clobber: dvh_clobber, +#else + clobber: NULL, +#endif + alloc: dvh_alloc, + duplicate: dvh_duplicate, + free: dvh_free, + read: dvh_read, +#ifndef DISCOVER_ONLY + write: dvh_write, +#else + write: NULL, +#endif + + partition_new: dvh_partition_new, + partition_duplicate: dvh_partition_duplicate, + partition_destroy: dvh_partition_destroy, + partition_set_system: dvh_partition_set_system, + partition_set_flag: dvh_partition_set_flag, + partition_get_flag: dvh_partition_get_flag, + partition_is_flag_available: dvh_partition_is_flag_available, + partition_set_name: dvh_partition_set_name, + partition_get_name: dvh_partition_get_name, + partition_align: dvh_partition_align, + partition_enumerate: dvh_partition_enumerate, + + alloc_metadata: dvh_alloc_metadata, + get_max_primary_partition_count: + dvh_get_max_primary_partition_count +}; + +static PedDiskType dvh_disk_type = { + next: NULL, + name: "dvh", + ops: &dvh_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME | PED_DISK_TYPE_EXTENDED +}; + +void +ped_disk_dvh_init () +{ + PED_ASSERT (sizeof (struct volume_header) == 512, return); + + ped_register_disk_type (&dvh_disk_type); +} + +void +ped_disk_dvh_done () +{ + ped_unregister_disk_type (&dvh_disk_type); +} + diff --git a/libparted/labels/dvh.h b/libparted/labels/dvh.h new file mode 100644 index 0000000..a075da8 --- /dev/null +++ b/libparted/labels/dvh.h @@ -0,0 +1,179 @@ +/* + Copyright (C) 1985 MIPS Computer Systems, Inc. + Copyright (C) 2000 Silicon Graphics Computer Systems, 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#ifndef _SYS_DVH_H +#define _SYS_DVH_H + +/* + * Format for volume header information + * + * The volume header is a block located at the beginning of all disk + * media (sector 0). It contains information pertaining to physical + * device parameters and logical partition information. + * + * The volume header is manipulated by disk formatters/verifiers, + * partition builders (e.g. fx, dvhtool, and mkfs), and disk drivers. + * + * Previous versions of IRIX wrote a copy of the volume header is + * located at sector 0 of each track of cylinder 0. These copies were + * never used, and reduced the capacity of the volume header to hold large + * files, so this practice was discontinued. + * The volume header is constrained to be less than or equal to 512 + * bytes long. A particular copy is assumed valid if no drive errors + * are detected, the magic number is correct, and the 32 bit 2's complement + * of the volume header is correct. The checksum is calculated by initially + * zeroing vh_csum, summing the entire structure and then storing the + * 2's complement of the sum. Thus a checksum to verify the volume header + * should be 0. + * + * The error summary table, bad sector replacement table, and boot blocks are + * located by searching the volume directory within the volume header. + * + * Tables are sized simply by the integral number of table records that + * will fit in the space indicated by the directory entry. + * + * The amount of space allocated to the volume header, replacement blocks, + * and other tables is user defined when the device is formatted. + */ + +/* + * device parameters are in the volume header to determine mapping + * from logical block numbers to physical device addresses + * + * Linux doesn't care ... + */ +struct device_parameters { + unsigned char dp_skew; /* spiral addressing skew */ + unsigned char dp_gap1; /* words of 0 before header */ + unsigned char dp_gap2; /* words of 0 between hdr and data */ + unsigned char dp_spares_cyl; /* This is for drives (such as SCSI + that support zone oriented sparing, where the zone is larger + than one track. It gets subracteded from the cylinder size + ( dp_trks0 * dp_sec) when doing partition size calculations */ + unsigned short dp_cyls; /* number of usable cylinders (i.e., + doesn't include cylinders reserved by the drive for badblocks, + etc.). For drives with variable geometry, this number may be + decreased so that: + dp_cyls * ((dp_heads * dp_trks0) - dp_spares_cyl) <= actualcapacity + This happens on SCSI drives such as the Wren IV and Toshiba 156 + Also see dp_cylshi below */ + unsigned short dp_shd0; /* starting head vol 0 */ + unsigned short dp_trks0; /* number of tracks / cylinder vol 0*/ + unsigned char dp_ctq_depth; /* Depth of CTQ queue */ + unsigned char dp_cylshi; /* high byte of 24 bits of cylinder count */ + unsigned short dp_unused; /* not used */ + unsigned short dp_secs; /* number of sectors/track */ + unsigned short dp_secbytes; /* length of sector in bytes */ + unsigned short dp_interleave; /* sector interleave */ + int dp_flags; /* controller characteristics */ + int dp_datarate; /* bytes/sec for kernel stats */ + int dp_nretries; /* max num retries on data error */ + int dp_mspw; /* ms per word to xfer, for iostat */ + unsigned short dp_xgap1; /* Gap 1 for xylogics controllers */ + unsigned short dp_xsync; /* sync delay for xylogics controllers */ + unsigned short dp_xrdly; /* read delay for xylogics controllers */ + unsigned short dp_xgap2; /* gap 2 for xylogics controllers */ + unsigned short dp_xrgate; /* read gate for xylogics controllers */ + unsigned short dp_xwcont; /* write continuation for xylogics */ +}; + +/* + * Device characterization flags + * (dp_flags) + */ +#define DP_SECTSLIP 0x00000001 /* sector slip to spare sector */ +#define DP_SECTFWD 0x00000002 /* forward to replacement sector */ +#define DP_TRKFWD 0x00000004 /* forward to replacement track */ +#define DP_MULTIVOL 0x00000008 /* multiple volumes per spindle */ +#define DP_IGNOREERRORS 0x00000010 /* transfer data regardless of errors */ +#define DP_RESEEK 0x00000020 /* recalibrate as last resort */ +#define DP_CTQ_EN 0x00000040 /* enable command tag queueing */ + +/* + * Boot blocks, bad sector tables, and the error summary table, are located + * via the volume_directory. + */ +#define VDNAMESIZE 8 + +struct volume_directory { + char vd_name[VDNAMESIZE]; /* name */ + int vd_lbn; /* logical block number */ + int vd_nbytes; /* file length in bytes */ +}; + +/* + * partition table describes logical device partitions + * (device drivers examine this to determine mapping from logical units + * to cylinder groups, device formatters/verifiers examine this to determine + * location of replacement tracks/sectors, etc) + * + * NOTE: pt_firstlbn SHOULD BE CYLINDER ALIGNED + */ +struct partition_table { /* one per logical partition */ + int pt_nblks; /* # of logical blks in partition */ + int pt_firstlbn; /* first lbn of partition */ + int pt_type; /* use of partition */ +}; + +#define PTYPE_VOLHDR 0 /* partition is volume header */ +#define PTYPE_TRKREPL 1 /* partition is used for repl trks */ +#define PTYPE_SECREPL 2 /* partition is used for repl secs */ +#define PTYPE_RAW 3 /* partition is used for data */ +#define PTYPE_BSD42 4 /* partition is 4.2BSD file system */ +#define PTYPE_BSD 4 /* partition is 4.2BSD file system */ +#define PTYPE_SYSV 5 /* partition is SysV file system */ +#define PTYPE_VOLUME 6 /* partition is entire volume */ +#define PTYPE_EFS 7 /* partition is sgi EFS */ +#define PTYPE_LVOL 8 /* partition is part of a logical vol */ +#define PTYPE_RLVOL 9 /* part of a "raw" logical vol */ +#define PTYPE_XFS 10 /* partition is sgi XFS */ +#define PTYPE_XFSLOG 11 /* partition is sgi XFS log */ +#define PTYPE_XLV 12 /* partition is part of an XLV vol */ +#define PTYPE_XVM 13 /* partition is sgi XVM */ +#define NPTYPES 16 + +#define VHMAGIC 0xbe5a941 /* randomly chosen value */ +#define NPARTAB 16 /* 16 unix partitions */ +#define NVDIR 15 /* max of 15 directory entries */ +#define BFNAMESIZE 16 /* max 16 chars in boot file name */ + +/* Partition types for ARCS */ +#define NOT_USED 0 /* Not used */ +#define FAT_SHORT 1 /* FAT file system, 12-bit FAT entries */ +#define FAT_LONG 4 /* FAT file system, 16-bit FAT entries */ +#define EXTENDED 5 /* extended partition */ +#define HUGE 6 /* huge partition- MS/DOS 4.0 and later */ + +/* Active flags for ARCS */ +#define BOOTABLE 0x00; +#define NOT_BOOTABLE 0x80; + +struct volume_header { + int vh_magic; /* identifies volume header */ + short vh_rootpt; /* root partition number */ + short vh_swappt; /* swap partition number */ + char vh_bootfile[BFNAMESIZE]; /* name of file to boot */ + struct device_parameters vh_dp; /* device parameters */ + struct volume_directory vh_vd[NVDIR]; /* other vol hdr contents */ + struct partition_table vh_pt[NPARTAB]; /* device partition layout */ + int vh_csum; /* volume header checksum */ + int vh_fill; /* fill out to 512 bytes */ +}; + +#endif /* _SYS_DVH_H */ diff --git a/libparted/labels/efi_crc32.c b/libparted/labels/efi_crc32.c new file mode 100644 index 0000000..a44f262 --- /dev/null +++ b/libparted/labels/efi_crc32.c @@ -0,0 +1,125 @@ +/* + * Dec 5, 2000 Matt Domsch <Matt_Domsch@dell.com> + * - Copied crc32.c from the linux/drivers/net/cipe directory. + * - Now pass seed as an arg + * - changed unsigned long to uint32_t, added #include<stdint.h> + * - changed len to be an unsigned long + * - changed crc32val to be a register + * - License remains unchanged! It's still GPL-compatable! + */ + + /* ============================================================= */ + /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ + /* code or tables extracted from it, as desired without restriction. */ + /* */ + /* First, the polynomial itself and its table of feedback terms. The */ + /* polynomial is */ + /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ + /* */ + /* Note that we take it "backwards" and put the highest-order term in */ + /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ + /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ + /* the MSB being 1. */ + /* */ + /* Note that the usual hardware shift register implementation, which */ + /* is what we're using (we're merely optimizing it by doing eight-bit */ + /* chunks at a time) shifts bits into the lowest-order term. In our */ + /* implementation, that means shifting towards the right. Why do we */ + /* do it this way? Because the calculated CRC must be transmitted in */ + /* order from highest-order term to lowest-order term. UARTs transmit */ + /* characters in order from LSB to MSB. By storing the CRC this way, */ + /* we hand it to the UART in the order low-byte to high-byte; the UART */ + /* sends each low-bit to hight-bit; and the result is transmission bit */ + /* by bit from highest- to lowest-order term without requiring any bit */ + /* shuffling on our part. Reception works similarly. */ + /* */ + /* The feedback terms table consists of 256, 32-bit entries. Notes: */ + /* */ + /* The table can be generated at runtime if desired; code to do so */ + /* is shown later. It might not be obvious, but the feedback */ + /* terms simply represent the results of eight shift/xor opera- */ + /* tions for all combinations of data and CRC register values. */ + /* */ + /* The values must be right-shifted by eight bits by the "updcrc" */ + /* logic; the shift must be unsigned (bring in zeroes). On some */ + /* hardware you could probably optimize the shift in assembler by */ + /* using byte-swap instructions. */ + /* polynomial $edb88320 */ + /* */ + /* -------------------------------------------------------------------- */ + +#include <stdint.h> + +static uint32_t crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL + }; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +uint32_t +__efi_crc32(const void *buf, unsigned long len, uint32_t seed) +{ + unsigned long i; + register uint32_t crc32val; + const unsigned char *s = buf; + + crc32val = seed; + for (i = 0; i < len; i ++) + { + crc32val = + crc32_tab[(crc32val ^ s[i]) & 0xff] ^ + (crc32val >> 8); + } + return crc32val; +} diff --git a/libparted/labels/fdasd.c b/libparted/labels/fdasd.c new file mode 100644 index 0000000..df32205 --- /dev/null +++ b/libparted/labels/fdasd.c @@ -0,0 +1,1153 @@ +/* + * File...........: arch/s390/tools/fdasd.c + * Author(s)......: Volker Sameske <sameske@de.ibm.com> + * Bugreports.to..: <Linux390@de.ibm.com> + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2001 + * + * History of changes (starts March 2001) + * 2001-04-11 possibility to change volume serial added + * possibility to change partition type added + * some changes to DS4HPCHR and DS4DSREC + * 2001-05-03 check for invalid partition numbers added + * wrong free_space calculation bug fixed + * 2001-06-26 '-a' option added, it is now possible to add a single + * partition in non-interactive mode + * 2001-06-26 long parameter support added + * + */ + +#include <parted/vtoc.h> +#include <parted/fdasd.h> + +#include <parted/parted.h> + +#include <libintl.h> +#if ENABLE_NLS +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define GETARG(x) {int k=strlen(optarg);x=malloc(k);strncpy(x,optarg,k);} + +static int +getpos (fdasd_anchor_t *anc, int dsn) +{ + PDEBUG + return anc->partno[dsn]; +} + +static int +getdsn (fdasd_anchor_t *anc, int pos) +{ + PDEBUG + int i; + + for (i=0; i<USABLE_PARTITIONS; i++) { + if (anc->partno[i] == pos) + return i; + } + + return -1; +} + +static void +setpos (fdasd_anchor_t *anc, int dsn, int pos) +{ + PDEBUG + anc->partno[dsn] = pos; +} + +static void +fdasd_check_volser (char *s, int devno) +{ + PDEBUG + int i, j; + + for (i = 0; i < 6; i++) { + if ((s[i] < 0x20) || (s[i] > 0x7a) + || ((s[i] >= 0x21) && (s[i] <= 0x22)) + || /* !" */ ((s[i] >= 0x26) && (s[i] <= 0x2f)) + || /* &'()*+,-./ */ ((s[i] >= 0x3a) && (s[i] <= 0x3f)) + || /* :;<=>? */ ((s[i] >= 0x5b) && (s[i] <= 0x60))) + /* \]^_´ */ s[i] = ' '; + s[i] = toupper (s[i]); + } + + s[6] = 0x00; + + for (i = 0; i < 6; i++) { + if (s[i] == ' ') + for (j = i; j < 6; j++) + if (s[j] != ' ') { + s[i] = s[j]; + s[j] = ' '; + break; + } + } + + if (s[0] == ' ') { + printf ("Usage error, switching to default.\n"); + sprintf (s, "0X%04x", devno); + for (i = 0; i < 6; i++) + s[i] = toupper (s[i]); + } +} + +void +fdasd_cleanup (fdasd_anchor_t *anchor) +{ + PDEBUG + int i; + partition_info_t *p, *q; + + if (anchor == NULL) + return; + + if (anchor->f4 != NULL) + free(anchor->f4); + + if (anchor->f5 != NULL) + free(anchor->f5); + + if (anchor->f7 != NULL) + free(anchor->f7); + + if (anchor->vlabel != NULL) + free(anchor->vlabel); + + p = anchor->first; + if (p == NULL) + return; + + for (i=1; i <= USABLE_PARTITIONS; i++) { + if (p == NULL) + return; + q = p->next; + free(p); + p = q; + } +} + +static void +fdasd_error (fdasd_anchor_t *anc, enum fdasd_failure why, char * str) +{ + PDEBUG + char error[2*LINE_LENGTH], *message = error; + + switch (why) { + case unable_to_open_disk: + sprintf(error, _("%s open error\n%s\n"), + FDASD_ERROR, str); + break; + case unable_to_seek_disk: + sprintf(error, _("%s seek error\n%s\n"), FDASD_ERROR, str); + break; + case unable_to_read_disk: + sprintf(error, _("%s read error\n%s\n"), FDASD_ERROR, str); + break; + case read_only_disk: + sprintf(error, _("%s write error\n%s\n"), FDASD_ERROR, str); + break; + case unable_to_ioctl: + sprintf(error, _("%s IOCTL error\n%s\n"), FDASD_ERROR, str); + break; + case api_version_mismatch: + sprintf(error, _("%s API version mismatch\n%s\n"), FDASD_ERROR,str); + break; + case wrong_disk_type: + sprintf(error, _("%s Unsupported disk type\n%s\n"), + FDASD_ERROR, str); + break; + case wrong_disk_format: + sprintf(error, _("%s Unsupported disk format\n%s\n"), + FDASD_ERROR, str); + break; + case disk_in_use: + sprintf(error, _("%s Disk in use\n%s\n"), FDASD_ERROR, str); + break; + case config_syntax_error: + sprintf(error, _("%s Config file syntax error\n%s\n"), + FDASD_ERROR, str); + break; + case vlabel_corrupted: + sprintf(error, _("%s Volume label is corrupted.\n%s\n"), + FDASD_ERROR, str); + break; + case dsname_corrupted: + sprintf(error, _("%s a data set name is corrupted.\n%s\n"), + FDASD_ERROR, str); + break; + case malloc_failed: + sprintf(error, _("%s space allocation\n%s\n"), + FDASD_ERROR, str); + break; + case device_verification_failed: + sprintf(error, _("%s device verification failed\n" \ + "The specified device is not a valid DASD device\n"), + FDASD_ERROR); + break; + default: + sprintf(error, _("%s Fatal error\n%s\n"), FDASD_ERROR, str); + } + + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, message); +} + +/* + * converts cyl-cyl-head-head-blk to blk + */ +static unsigned long +cchhb2blk (cchhb_t *p, struct fdasd_hd_geometry *geo) +{ + PDEBUG + return (unsigned long) (p->cc * geo->heads * geo->sectors + + p->hh * geo->sectors + p->b); +} + +static char *fdasd_partition_type (char *str) +{ + PDEBUG + + if (strncmp("NATIVE", str, 6) == 0) + strcpy(str, "Linux native"); + else if (strncmp("NEW ", str, 6) == 0) + strcpy(str, "Linux native"); + else if (strncmp("SWAP ", str, 6) == 0) + strcpy(str, "Linux swap"); + else if (strncmp("RAID ", str, 6) == 0) + strcpy(str, "Linux Raid"); + else + strcpy(str, "unknown"); + + return str; +} + +/* + * initializes the anchor structure and allocates some + * memory for the labels + */ +void +fdasd_initialize_anchor (fdasd_anchor_t * anc) +{ + PDEBUG + int i; + volume_label_t *v; + partition_info_t *p = NULL; + partition_info_t *q = NULL; + + anc->devno = 0; + anc->dev_type = 0; + anc->used_partitions = 0; + + anc->silent = 0; + anc->verbose = 0; + anc->big_disk = 0; + anc->volid_specified = 0; + anc->config_specified = 0; + anc->auto_partition = 0; + anc->devname_specified = 0; + anc->print_table = 0; + + anc->option_reuse = 0; + anc->option_recreate = 0; + + anc->vlabel_changed = 0; + anc->vtoc_changed = 0; + anc->blksize = 0; + anc->fspace_trk = 0; + anc->label_pos = 0; + + for (i=0; i<USABLE_PARTITIONS; i++) + setpos(anc, i, -1); + + bzero(anc->confdata, sizeof(config_data_t)); + + anc->f4 = malloc(sizeof(format4_label_t)); + if (anc->f4 == NULL) + fdasd_error(anc, malloc_failed, + "FMT4 DSCB memory allocation failed."); + + anc->f5 = malloc(sizeof(format5_label_t)); + if (anc->f5 == NULL) + fdasd_error(anc, malloc_failed, + "FMT5 DSCB memory allocation failed."); + + anc->f7 = malloc(sizeof(format7_label_t)); + if (anc->f7 == NULL) + fdasd_error(anc, malloc_failed, + "FMT7 DSCB memory allocation failed."); + + bzero(anc->f4, sizeof(format4_label_t)); + bzero(anc->f5, sizeof(format5_label_t)); + bzero(anc->f7, sizeof(format7_label_t)); + + v = malloc(sizeof(volume_label_t)); + if (v == NULL) + fdasd_error(anc, malloc_failed, + "Volume label memory allocation failed."); + bzero(v, sizeof(volume_label_t)); + anc->vlabel = v; + + for (i=1; i<=USABLE_PARTITIONS; i++) { + p = malloc(sizeof(partition_info_t)); + if (p == NULL) + fdasd_error(anc, malloc_failed, + "Partition info memory allocation failed."); + p->used = 0x00; + p->len_trk = 0; + p->start_trk = 0; + p->fspace_trk = 0; + p->type = 0; + + /* add p to double pointered list */ + if (i == 1) { + anc->first = p; + p->prev = NULL; + } else if (i == USABLE_PARTITIONS) { + anc->last = p; + p->next = NULL; + p->prev = q; + q->next = p; + } else { + p->prev = q; + q->next = p; + } + + p->f1 = malloc(sizeof(format1_label_t)); + if (p->f1 == NULL) + fdasd_error(anc, malloc_failed, + "FMT1 DSCB memory allocation failed."); + bzero(p->f1, sizeof(format1_label_t)); + + q = p; + } +} + +/* + * call IOCTL to re-read the partition table + */ +static void +fdasd_reread_partition_table (fdasd_anchor_t * anc, int fd) +{ + PDEBUG + char str[LINE_LENGTH]; + int f; + + if (ioctl (fd, BLKRRPART, NULL) != 0) + fdasd_error (anc, unable_to_ioctl, "Error while rereading " + "partition table.\nPlease reboot!"); +} + +/* + * writes all changes to dasd + */ +static void +fdasd_write_vtoc_labels (fdasd_anchor_t * anc, int fd) +{ + PDEBUG + partition_info_t *p; + unsigned long b; + char dsno[6], s1[7], s2[45], *c1, *c2, *ch; + int i = 0, k = 0; + + b = (cchhb2blk (&anc->vlabel->vtoc, &anc->geo) - 1) * anc->blksize; + if (b <= 0) + fdasd_error (anc, vlabel_corrupted, ""); + + /* write FMT4 DSCB */ + vtoc_write_label (fd, b, NULL, anc->f4, NULL, NULL); + + /* write FMT5 DSCB */ + b += anc->blksize; + vtoc_write_label (fd, b, NULL, NULL, anc->f5, NULL); + + /* write FMT7 DSCB */ + if (anc->big_disk) { + b += anc->blksize; + vtoc_write_label (fd, b, NULL, NULL, NULL, anc->f7); + } + + /* loop over all FMT1 DSCBs */ + p = anc->first; + for (i = 0; i < USABLE_PARTITIONS; i++) { + b += anc->blksize; + + if (p->used != 0x01) { + vtoc_write_label (fd, b, p->f1, NULL, NULL, NULL); + continue; + } + + strncpy (p->f1->DS1DSSN, anc->vlabel->volid, 6); + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + c1 = ch + 7; + + if (getdsn (anc, i) > -1) { + /* re-use the existing data set name */ + c2 = strchr (c1, '.'); + if (c2 != NULL) + strncpy (s2, c2, 31); + else + fdasd_error (anc, dsname_corrupted, ""); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + s1[6] = ' '; + strncpy (c1, s1, 7); + c1 = strchr (ch, ' '); + strncpy (c1, s2, 31); + } else { + /* create a new data set name */ + while (getpos (anc, k) > -1) + k++; + + setpos (anc, k, i); + + strncpy (s2, ch, 44); + s2[44] = 0; + vtoc_ebcdic_dec (s2, s2, 44); + + strncpy (ch, "LINUX.V " " ", 44); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + strncpy (c1, s1, 6); + + c1 = strchr (ch, ' '); + strncpy (c1, ".PART", 5); + c1 += 5; + + sprintf (dsno, "%04d.", k + 1); + strncpy (c1, dsno, 5); + + c1 += 5; + switch(p->type) { + case PARTITION_LINUX_LVM: + strncpy(c1, PART_TYPE_LVM, 6); + break; + case PARTITION_LINUX_RAID: + strncpy(c1, PART_TYPE_RAID, 6); + break; + case PARTITION_LINUX: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + case PARTITION_LINUX_SWAP: + strncpy(c1, PART_TYPE_SWAP, 6); + break; + default: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + } + } + + vtoc_ebcdic_enc (ch, ch, 44); + + vtoc_write_label (fd, b, p->f1, NULL, NULL, NULL); + p = p->next; + } +} + +/* + * writes all changes to dasd + */ +int +fdasd_write_labels (fdasd_anchor_t * anc, int fd) +{ + PDEBUG + if (anc->vlabel_changed) + vtoc_write_volume_label (fd, anc->label_pos, anc->vlabel); + + if (anc->vtoc_changed) + fdasd_write_vtoc_labels (anc, fd); + + return 1; +} + +/* + * writes all changes to dasd + */ +int +fdasd_prepare_labels (fdasd_anchor_t *anc, int fd) +{ + PDEBUG + partition_info_t *p = anc->first; + char dsno[6], s1[7], s2[45], *c1, *c2, *ch; + int i = 0, k = 0; + + /* loop over all FMT1 DSCBs */ + p = anc->first; + for (i = 0; i < USABLE_PARTITIONS; i++) { + strncpy (p->f1->DS1DSSN, anc->vlabel->volid, 6); + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + c1 = ch + 7; + + if (getdsn (anc, i) > -1) { + /* re-use the existing data set name */ + c2 = strchr (c1, '.'); + if (c2 != NULL) + strncpy (s2, c2, 31); + else + fdasd_error (anc, dsname_corrupted, ""); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + s1[6] = ' '; + strncpy (c1, s1, 7); + c1 = strchr (ch, ' '); + strncpy (c1, s2, 31); + } else { + /* create a new data set name */ + while (getpos (anc, k) > -1) + k++; + + setpos (anc, k, i); + + strncpy (s2, ch, 44); + s2[44] = 0; + vtoc_ebcdic_dec (s2, s2, 44); + + strncpy (ch, "LINUX.V " " ", 44); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + strncpy (c1, s1, 6); + + c1 = strchr (ch, ' '); + strncpy (c1, ".PART", 5); + c1 += 5; + + sprintf (dsno, "%04d.", k + 1); + strncpy (c1, dsno, 5); + + c1 += 5; + switch(p->type) { + case PARTITION_LINUX_LVM: + strncpy(c1, PART_TYPE_LVM, 6); + break; + case PARTITION_LINUX_RAID: + strncpy(c1, PART_TYPE_RAID, 6); + break; + case PARTITION_LINUX: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + case PARTITION_LINUX_SWAP: + strncpy(c1, PART_TYPE_SWAP, 6); + break; + default: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + } + } + + vtoc_ebcdic_enc (ch, ch, 44); + p = p->next; + } + + return 1; +} + +void +fdasd_recreate_vtoc (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *p = anc->first; + int i; + + vtoc_init_format4_label(anc->f4, + USABLE_PARTITIONS, + anc->geo.cylinders, + anc->geo.heads, + anc->geo.sectors, + anc->blksize, + anc->dev_type); + + vtoc_init_format5_label(anc->f5); + vtoc_init_format7_label(anc->f7); + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, + '+', anc->verbose, + FIRST_USABLE_TRK, + anc->geo.cylinders * anc->geo.heads - 1, + anc->geo.cylinders, anc->geo.heads); + + for (i = 0; i < USABLE_PARTITIONS; i++) { + bzero(p->f1, sizeof(format1_label_t)); + p->used = 0x00; + p->start_trk = 0; + p->end_trk = 0; + p->len_trk = 0; + p->fspace_trk = 0; + p->type = 0; + p = p->next; + } + + anc->used_partitions = 0; + anc->fspace_trk = anc->geo.cylinders * anc->geo.heads - FIRST_USABLE_TRK; + + for (i=0; i<USABLE_PARTITIONS; i++) + setpos(anc, i, -1); + + anc->vtoc_changed++; +} + +/* + * changes the volume serial + */ +static void +fdasd_change_volser (fdasd_anchor_t *anc, char *line_ptr) +{ + PDEBUG + char str[10]; + + if (strcmp(line_ptr, "") != 0) { + int i; + + /* fill with blanks if necessary and remove the linebreak */ + i = strlen(line_ptr); + if (i <= 6) + strncpy(line_ptr + i - 1, " ", 6); + + strncpy(str, line_ptr, 6); + + for (i=0; i<6; i++) str[i] = toupper(str[i]); + str[6] = 0x00; + + fdasd_check_volser (str, anc->devno); + vtoc_volume_label_set_volser(anc->vlabel, str); + + vtoc_set_cchhb(&anc->vlabel->vtoc, 0x0000, 0x0001, 0x01); + anc->vlabel_changed++; + anc->vtoc_changed++; + } +} + +/* + * sets some important partition data + * (like used, start_trk, end_trk, len_trk) + * by calculating these values with the + * information provided in the labels + */ +static void +fdasd_update_partition_info (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *q = NULL, *p = anc->first; + unsigned int h = anc->geo.heads; + unsigned long max = anc->geo.cylinders * h - 1; + int i; + char *ch; + + anc->used_partitions = anc->geo.sectors - 2 - anc->f4->DS4DSREC; + + for (i = 1; i <= USABLE_PARTITIONS; i++) { + if (p->f1->DS1FMTID != 0xf1) { + if (i == 1) + /* there is no partition at all */ + anc->fspace_trk = max - FIRST_USABLE_TRK + 1; + else + /* previous partition was the last one */ + q->fspace_trk = max - q->end_trk; + break; + } + + /* this is a valid format 1 label */ + p->used = 0x01; + p->start_trk = p->f1->DS1EXT1.llimit.cc * h + p->f1->DS1EXT1.llimit.hh; + p->end_trk = p->f1->DS1EXT1.ulimit.cc * h + p->f1->DS1EXT1.ulimit.hh; + p->len_trk = p->end_trk - p->start_trk + 1; + + if (i == 1) { + /* first partition, there is at least one */ + anc->fspace_trk = p->start_trk - FIRST_USABLE_TRK; + } else { + if (i == USABLE_PARTITIONS) + /* last possible partition */ + p->fspace_trk = max - p->end_trk; + + /* set free space values of previous partition */ + q->fspace_trk = p->start_trk - q->end_trk - 1; + } + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + if (strstr(ch, PART_TYPE_LVM)) + p->type = PARTITION_LINUX_LVM; + else if (strstr(ch, PART_TYPE_RAID)) + p->type = PARTITION_LINUX_RAID; + else if (strstr(ch, PART_TYPE_NATIVE)) + p->type = PARTITION_LINUX; + else if (strstr(ch, PART_TYPE_SWAP)) + p->type = PARTITION_LINUX_SWAP; + else + p->type = PARTITION_LINUX; + vtoc_ebcdic_enc (ch, ch, 44); + + q = p; + p = p->next; + } +} + +/* + * reorganizes all FMT1s, after that all used FMT1s should be right in + * front of all unused FMT1s + */ +static void +fdasd_reorganize_FMT1s (fdasd_anchor_t *anc) +{ + PDEBUG + int i, j; + format1_label_t *ltmp; + partition_info_t *ptmp; + + for (i=1; i<=USABLE_PARTITIONS - 1; i++) { + ptmp = anc->first; + + for (j=1; j<=USABLE_PARTITIONS - i; j++) { + if (ptmp->f1->DS1FMTID < ptmp->next->f1->DS1FMTID) { + ltmp = ptmp->f1; + ptmp->f1 = ptmp->next->f1; + ptmp->next->f1 = ltmp; + } + + ptmp=ptmp->next; + } + } +} + +static void +fdasd_process_valid_vtoc (fdasd_anchor_t * anc, unsigned long b, int fd) +{ + PDEBUG + int f5_counter = 0, f7_counter = 0, f1_counter = 0, oldfmt = 0; + int i, n, f1size = sizeof (format1_label_t); + partition_info_t *p = anc->first; + format1_label_t q; + char s[5], *ch; + + b += anc->blksize; + + for (i = 1; i <= anc->geo.sectors; i++) { + bzero (&q, f1size); + vtoc_read_label (fd, b, &q, NULL, NULL, NULL); + + switch (q.DS1FMTID) { + case 0xf1: + if (p == NULL) + break; + memcpy (p->f1, &q, f1size); + + n = -1; + vtoc_ebcdic_dec (p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + ch = strstr (p->f1->DS1DSNAM, "PART"); + if (ch != NULL) { + strncpy (s, ch + 4, 4); + s[4] = '\0'; + n = atoi (s) - 1; + } + + vtoc_ebcdic_enc (p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + + /* this dasd has data set names 0000-0002 + but we use now 0001-0003 */ + if (n == -1) + oldfmt++; + + if (((oldfmt == 0) && (n < 0)) || (n >= USABLE_PARTITIONS)) { + /* no op */ + } else { + if (oldfmt) { + /* correct +1 */ + setpos (anc, n + 1, f1_counter); + } else { + setpos (anc, n, f1_counter); + } + } + + p = p->next; + f1_counter++; + break; + case 0xf5: + memcpy (anc->f5, &q, f1size); + f5_counter++; + break; + case 0xf7: + if (f7_counter == 0) + memcpy (anc->f7, &q, f1size); + f7_counter++; + break; + } + + b += anc->blksize; + } + + if (oldfmt > 0) { + /* this is the old format PART0000 - PART0002 */ + anc->vtoc_changed++; + } + + if ((f5_counter == 0) || (anc->big_disk)) + vtoc_init_format5_label (anc->f5); + + if (f7_counter == 0) + vtoc_init_format7_label (anc->f7); + + fdasd_reorganize_FMT1s (anc); + fdasd_update_partition_info (anc); +} + +static int +fdasd_valid_vtoc_pointer(fdasd_anchor_t *anc, unsigned long b, int fd) +{ + PDEBUG + char str[LINE_LENGTH]; + + /* VOL1 label contains valid VTOC pointer */ + vtoc_read_label (fd, b, NULL, anc->f4, NULL, NULL); + + if (anc->f4->DS4IDFMT != 0xf4) { + if (strncmp(anc->vlabel->volkey,vtoc_ebcdic_enc("LNX1",str,4),4) == 0) + return 0; + fdasd_error(anc, wrong_disk_format, "Invalid VTOC"); + } else { + fdasd_process_valid_vtoc (anc, b, fd); + } + + return 0; +} + +/* + * check the dasd for a volume label + */ +int +fdasd_check_volume (fdasd_anchor_t *anc, int fd) +{ + PDEBUG + volume_label_t *v = anc->vlabel; + unsigned long b = -1; + char str[LINE_LENGTH]; + + vtoc_read_volume_label (fd, anc->label_pos, v); + + if (strncmp(v->vollbl, vtoc_ebcdic_enc ("VOL1", str, 4), 4) == 0) { + /* found VOL1 volume label */ + b = (cchhb2blk (&v->vtoc, &anc->geo) - 1) * anc->blksize; + + if (b > 0) { + int rc; + rc = fdasd_valid_vtoc_pointer (anc, b, fd); + + if (rc < 0) + return 1; + else + return 0; + } else { + return 1; + } + } else if (strncmp (v->volkey, vtoc_ebcdic_enc ("LNX1", str, 4), 4) == 0) { + return 0; + } + + return 1; +} + +/* + * checks the current API version with the API version of the dasd driver + */ +void +fdasd_check_api_version (fdasd_anchor_t *anc, int f) +{ + PDEBUG + int api; + char s[LINE_LENGTH]; + + if (ioctl(f, DASDAPIVER, &api) != 0) + fdasd_error(anc, unable_to_ioctl, "Could not retrieve API version."); + + if (api != DASD_MIN_API_VERSION) { + sprintf(s, "The current API version '%d' doesn't " \ + "match dasd driver API version " \ + "'%d'!", api, DASD_MIN_API_VERSION); + fdasd_error(anc, api_version_mismatch, s); + } +} + +/* + * reads dasd geometry data + */ +void +fdasd_get_geometry (fdasd_anchor_t *anc, int f) +{ + PDEBUG + int blksize = 0; + dasd_information_t dasd_info; + char s[LINE_LENGTH]; + + if (ioctl(f, HDIO_GETGEO, &anc->geo) != 0) + fdasd_error(anc, unable_to_ioctl, + "Could not retrieve disk geometry information."); + + if (ioctl(f, BLKSSZGET, &blksize) != 0) + fdasd_error(anc, unable_to_ioctl, + "Could not retrieve blocksize information."); + + /* get disk type */ + if (ioctl(f, BIODASDINFO, &dasd_info) != 0) + fdasd_error(anc, unable_to_ioctl, + "Could not retrieve disk information."); + + if (strncmp(dasd_info.type, "ECKD", 4) != 0) { + sprintf(s, "This is not an ECKD disk! This disk type " \ + "is not supported!"); + fdasd_error(anc,wrong_disk_type, s); + } + + anc->dev_type = dasd_info.dev_type; + anc->blksize = blksize; + anc->label_pos = dasd_info.label_block * blksize; + anc->devno = dasd_info.devno; + anc->fspace_trk = anc->geo.cylinders * anc->geo.heads - FIRST_USABLE_TRK; +} + +/* + * returns unused partition info pointer if there + * is a free partition, otherwise NULL + */ +static partition_info_t * +fdasd_get_empty_f1_label (fdasd_anchor_t * anc) +{ + PDEBUG + if (anc->used_partitions < USABLE_PARTITIONS) + return anc->last; + else + return NULL; +} + +/* + * asks for and sets some important partition data + */ +static int +fdasd_get_partition_data (fdasd_anchor_t *anc, extent_t *part_extent, + partition_info_t *p, unsigned int *start_ptr, + unsigned int *stop_ptr) +{ + PDEBUG + unsigned int limit, cc, hh; + cchh_t llimit, ulimit; + partition_info_t *q; + char mesg[48]; + u_int8_t b1, b2; + u_int16_t c, h; + unsigned int start = *start_ptr, stop = *stop_ptr; + int i; + char *ch; + + if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED) + c = anc->f4->DS4DEVCT.DS4DSCYL - (u_int16_t) anc->f4->DS4DEVAC; + else + c = anc->f4->DS4DEVCT.DS4DSCYL; + + h = anc->f4->DS4DEVCT.DS4DSTRK; + limit = (h * c - 1); + + /* check start value from user */ + q = anc->first; + for (i = 0; i < USABLE_PARTITIONS; i++) { + if ( q->next == NULL ) + break; + + if (start >= q->start_trk && start <= q->end_trk) { + /* start is within another partition */ + start = q->end_trk + 1; + + if (start > limit) { + start = FIRST_USABLE_TRK; + q = anc->first; + } + } + + if (start < q->start_trk) { + limit = q->start_trk - 1; + break; + } + + q = q->next; + } + + if (start == limit) + stop = start; + + /* update partition info */ + p->len_trk = stop - start + 1; + p->start_trk = start; + p->end_trk = stop; + + cc = start / anc->geo.heads; + hh = start - (cc * anc->geo.heads); + vtoc_set_cchh(&llimit, cc, hh); + + /* check for cylinder boundary */ + if (hh == 0) + b1 = 0x81; + else + b1 = 0x01; + + cc = stop / anc->geo.heads; + hh = stop - cc * anc->geo.heads; + vtoc_set_cchh(&ulimit, cc, hh); + + /* it is always the 1st extent */ + b2 = 0x00; + + vtoc_set_extent(part_extent, b1, b2, &llimit, &ulimit); + + *start_ptr = start; + *stop_ptr = stop; + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + + if (strstr(ch, PART_TYPE_LVM)) + p->type = PARTITION_LINUX_LVM; + else if (strstr(ch, PART_TYPE_RAID)) + p->type = PARTITION_LINUX_RAID; + else if (strstr(ch, PART_TYPE_NATIVE)) + p->type = PARTITION_LINUX; + else if (strstr(ch, PART_TYPE_SWAP)) + p->type = PARTITION_LINUX_SWAP; + else + p->type = PARTITION_LINUX; + + vtoc_ebcdic_enc (ch, ch, 44); + + return 0; +} + +static void +fdasd_enqueue_new_partition (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *q = anc->first, *p = anc->last; + int i, k=0; + + for (i=1; i<USABLE_PARTITIONS; i++) { + if ((q->end_trk == 0) || (p->start_trk < q->start_trk)) { + break; + } else { + q = q->next; + k++; + } + } + + if (anc->first == q) + anc->first = p; + + if (p != q) { + anc->last->prev->next = NULL; + anc->last = anc->last->prev; + + p->next = q; + p->prev = q->prev; + q->prev = p; + + if (p->prev != NULL) + p->prev->next = p; + } + + p->used = 0x01; + p->type = PARTITION_LINUX; + + for (i=0; i<USABLE_PARTITIONS; i++) { + int j = getpos(anc, i); + if (j >= k) + setpos(anc, i, j + 1); + } + + /* update free-space counters */ + if (anc->first == p) { + /* partition is the first used partition */ + if (p->start_trk == FIRST_USABLE_TRK) { + /* partition starts right behind VTOC */ + p->fspace_trk = anc->fspace_trk - p->len_trk; + anc->fspace_trk = 0; + } else { + /* there is some space between VTOC and partition */ + p->fspace_trk = anc->fspace_trk - p->len_trk - p->start_trk + + FIRST_USABLE_TRK; + anc->fspace_trk = p->start_trk - FIRST_USABLE_TRK; + } + } else { + /* there are partitons in front of the new one */ + if (p->start_trk == p->prev->end_trk + 1) { + /* new partition is right behind the previous one */ + p->fspace_trk = p->prev->fspace_trk - p->len_trk; + p->prev->fspace_trk = 0; + } else { + /* there is some space between new and prev. part. */ + p->fspace_trk = p->prev->fspace_trk - p->len_trk + - p->start_trk + p->prev->end_trk + 1; + p->prev->fspace_trk = p->start_trk - p->prev->end_trk - 1; + } + } +} + +/* + * adds a new partition to the 'partition table' + */ +partition_info_t * +fdasd_add_partition (fdasd_anchor_t *anc, unsigned int start, + unsigned int stop) +{ + PDEBUG + cchhb_t hf1; + partition_info_t *p; + extent_t ext; + int i; + + PDEBUG; + + if ((p = fdasd_get_empty_f1_label(anc)) == NULL) { + PDEBUG; + return 0; + } + + PDEBUG; + if (fdasd_get_partition_data(anc, &ext, p, &start, &stop) != 0) + return 0; + + PDEBUG; + vtoc_init_format1_label(anc->vlabel->volid, anc->blksize, &ext, p->f1); + + PDEBUG; + fdasd_enqueue_new_partition(anc); + + PDEBUG; + anc->used_partitions += 1; + + i = anc->used_partitions + 2; + if (anc->big_disk) + i++; + PDEBUG; + + vtoc_set_cchhb(&hf1, VTOC_START_CC, VTOC_START_HH, i); + + vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1); + + PDEBUG; + + start = ext.llimit.cc * anc->geo.heads + ext.llimit.hh; + stop = ext.ulimit.cc * anc->geo.heads + ext.ulimit.hh; + + PDEBUG; + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '-', anc->verbose, + start, stop, anc->geo.cylinders, anc->geo.heads); + + anc->vtoc_changed++; + + PDEBUG; + return p; +} + +/* vim:set tabstop=4 shiftwidth=4 softtabstop=4: */ diff --git a/libparted/labels/gpt.c b/libparted/labels/gpt.c new file mode 100644 index 0000000..80b4d9d --- /dev/null +++ b/libparted/labels/gpt.c @@ -0,0 +1,1504 @@ +/* + libparted - a library for manipulating disk partitions + + original version by Matt Domsch <Matt_Domsch@dell.com> + Disclaimed into the Public Domain + + Portions Copyright (C) 2001, 2002, 2003, 2005, 2006 + Free Software Foundation, Inc. + + EFI GUID Partition Table handling + Per Intel EFI Specification v1.02 + http://developer.intel.com/technology/efi/efi.htm + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <parted/crc32.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <uuid/uuid.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) gettext (String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define EFI_PMBR_OSTYPE_EFI 0xEE +#define MSDOS_MBR_SIGNATURE 0xaa55 + +#define GPT_HEADER_SIGNATURE 0x5452415020494645LL + +/* NOTE: the document that describes revision 1.00 is labelled "version 1.02", + * so some implementors got confused... + */ +#define GPT_HEADER_REVISION_V1_02 0x00010200 +#define GPT_HEADER_REVISION_V1_00 0x00010000 +#define GPT_HEADER_REVISION_V0_99 0x00009900 + +typedef uint16_t efi_char16_t; /* UNICODE character */ + +typedef struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} /* __attribute__ ((packed)) */ efi_guid_t; +/* commented out "__attribute__ ((packed))" to work around gcc bug (fixed + * in gcc3.1): __attribute__ ((packed)) breaks addressing on initialized + * data. It turns out we don't need it in this case, so it doesn't break + * anything :) + */ + +#define UNUSED_ENTRY_GUID \ + ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}) +#define PARTITION_SYSTEM_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xC12A7328), PED_CPU_TO_LE16 (0xF81F), \ + PED_CPU_TO_LE16 (0x11d2), 0xBA, 0x4B, \ + { 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B }}) +#define LEGACY_MBR_PARTITION_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x024DEE41), PED_CPU_TO_LE16 (0x33E7), \ + PED_CPU_TO_LE16 (0x11d3, 0x9D, 0x69, \ + { 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F }}) +#define PARTITION_MSFT_RESERVED_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xE3C9E316), PED_CPU_TO_LE16 (0x0B5C), \ + PED_CPU_TO_LE16 (0x4DB8), 0x81, 0x7D, \ + { 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE }}) +#define PARTITION_BASIC_DATA_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xEBD0A0A2), PED_CPU_TO_LE16 (0xB9E5), \ + PED_CPU_TO_LE16 (0x4433), 0x87, 0xC0, \ + { 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 }}) +#define PARTITION_RAID_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xa19d880f), PED_CPU_TO_LE16 (0x05fc), \ + PED_CPU_TO_LE16 (0x4d3b), 0xa0, 0x06, \ + { 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e }}) +#define PARTITION_SWAP_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x0657fd6d), PED_CPU_TO_LE16 (0xa4ab), \ + PED_CPU_TO_LE16 (0x43c4), 0x84, 0xe5, \ + { 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f }}) +#define PARTITION_LVM_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xe6d6d379), PED_CPU_TO_LE16 (0xf507), \ + PED_CPU_TO_LE16 (0x44c2), 0xa2, 0x3c, \ + { 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28 }}) +#define PARTITION_RESERVED_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x8da63339), PED_CPU_TO_LE16 (0x0007), \ + PED_CPU_TO_LE16 (0x60c0), 0xc4, 0x36, \ + { 0x08, 0x3a, 0xc8, 0x23, 0x09, 0x08 }}) +#define PARTITION_HPSERVICE_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xe2a1e728), PED_CPU_TO_LE16 (0x32e3), \ + PED_CPU_TO_LE16 (0x11d6), 0xa6, 0x82, \ + { 0x7b, 0x03, 0xa0, 0x00, 0x00, 0x00 }}) +#define PARTITION_APPLE_HFS_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x48465300), PED_CPU_TO_LE16 (0x0000), \ + PED_CPU_TO_LE16 (0x11AA), 0xaa, 0x11, \ + { 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }}) + +typedef struct _GuidPartitionTableHeader_t { + uint64_t Signature; + uint32_t Revision; + uint32_t HeaderSize; + uint32_t HeaderCRC32; + uint32_t Reserved1; + uint64_t MyLBA; + uint64_t AlternateLBA; + uint64_t FirstUsableLBA; + uint64_t LastUsableLBA; + efi_guid_t DiskGUID; + uint64_t PartitionEntryLBA; + uint32_t NumberOfPartitionEntries; + uint32_t SizeOfPartitionEntry; + uint32_t PartitionEntryArrayCRC32; + uint8_t* Reserved2; +} __attribute__ ((packed)) GuidPartitionTableHeader_t; + +typedef struct _GuidPartitionEntryAttributes_t { +#ifdef __GNUC__ /* XXX narrow this down to !TinyCC */ + uint64_t RequiredToFunction:1; + uint64_t Reserved:47; + uint64_t GuidSpecific:16; +#else +# warning "Using crippled partition entry type" + uint32_t RequiredToFunction:1; + uint32_t Reserved:32; + uint32_t LOST:5; + uint32_t GuidSpecific:16; +#endif +} __attribute__ ((packed)) GuidPartitionEntryAttributes_t; + +typedef struct _GuidPartitionEntry_t { + efi_guid_t PartitionTypeGuid; + efi_guid_t UniquePartitionGuid; + uint64_t StartingLBA; + uint64_t EndingLBA; + GuidPartitionEntryAttributes_t Attributes; + efi_char16_t PartitionName[72 / sizeof(efi_char16_t)]; +} __attribute__ ((packed)) GuidPartitionEntry_t; + +#define GPT_PMBR_LBA 0 +#define GPT_PMBR_SECTORS 1 +#define GPT_PRIMARY_HEADER_LBA 1 +#define GPT_HEADER_SECTORS 1 +#define GPT_PRIMARY_PART_TABLE_LBA 2 + +/* + These values are only defaults. The actual on-disk structures + may define different sizes, so use those unless creating a new GPT disk! +*/ + +#define GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE 16384 + +/* Number of actual partition entries should be calculated as: */ +#define GPT_DEFAULT_PARTITION_ENTRIES \ + (GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / \ + sizeof(GuidPartitionEntry_t)) + + +typedef struct _PartitionRecord_t { + /* Not used by EFI firmware. Set to 0x80 to indicate that this + is the bootable legacy partition. */ + uint8_t BootIndicator; + + /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t StartHead; + + /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t StartSector; + + /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t StartTrack; + + /* OS type. A value of 0xEF defines an EFI system partition. + Other values are reserved for legacy operating systems, and + allocated independently of the EFI specification. */ + uint8_t OSType; + + /* End of partition in CHS address, not used by EFI firmware. */ + uint8_t EndHead; + + /* End of partition in CHS address, not used by EFI firmware. */ + uint8_t EndSector; + + /* End of partition in CHS address, not used by EFI firmware. */ + uint8_t EndTrack; + + /* Starting LBA address of the partition on the disk. Used by + EFI firmware to define the start of the partition. */ + uint32_t StartingLBA; + + /* Size of partition in LBA. Used by EFI firmware to determine + the size of the partition. */ + uint32_t SizeInLBA; +} __attribute__ ((packed)) PartitionRecord_t; + +/* Protected Master Boot Record & Legacy MBR share same structure */ +/* Needs to be packed because the u16s force misalignment. */ +typedef struct _LegacyMBR_t { + uint8_t BootCode[440]; + uint32_t UniqueMBRSignature; + uint16_t Unknown; + PartitionRecord_t PartitionRecord[4]; + uint16_t Signature; +} __attribute__ ((packed)) LegacyMBR_t; + +/* uses libparted's disk_specific field in PedDisk, to store our info */ +typedef struct _GPTDiskData { + PedGeometry data_area; + int entry_count; + efi_guid_t uuid; +} GPTDiskData; + +/* uses libparted's disk_specific field in PedPartition, to store our info */ +typedef struct _GPTPartitionData { + efi_guid_t type; + efi_guid_t uuid; + char name[37]; + int lvm; + int raid; + int boot; + int hp_service; + int hidden; + int msftres; +} GPTPartitionData; + +static PedDiskType gpt_disk_type; + + +static inline uint32_t +pth_get_size (const PedDevice* dev) +{ + return GPT_HEADER_SECTORS * dev->sector_size; +} + + +static inline uint32_t +pth_get_size_static (const PedDevice* dev) +{ + return sizeof (GuidPartitionTableHeader_t) - sizeof (uint8_t*); +} + + +static inline uint32_t +pth_get_size_rsv2 (const PedDevice* dev) +{ + return pth_get_size(dev) - pth_get_size_static(dev); +} + + +static GuidPartitionTableHeader_t* +pth_new (const PedDevice* dev) +{ + GuidPartitionTableHeader_t* pth = ped_malloc ( + sizeof (GuidPartitionTableHeader_t) + + sizeof (uint8_t)); + + pth->Reserved2 = ped_malloc ( pth_get_size_rsv2 (dev) ); + + return pth; +} + + +static GuidPartitionTableHeader_t* +pth_new_zeroed (const PedDevice* dev) +{ + GuidPartitionTableHeader_t* pth = pth_new (dev); + + memset (pth, 0, pth_get_size_static (dev)); + memset (pth->Reserved2, 0, pth_get_size_rsv2 (dev)); + + return (pth); +} + + +static GuidPartitionTableHeader_t* +pth_new_from_raw (const PedDevice* dev, const uint8_t* pth_raw) +{ + GuidPartitionTableHeader_t* pth = pth_new (dev); + + PED_ASSERT (pth_raw != NULL, return 0); + + memcpy (pth, pth_raw, pth_get_size_static (dev)); + memcpy (pth->Reserved2, pth_raw + pth_get_size_static (dev), + pth_get_size_rsv2 (dev)); + + return pth; +} + +static void +pth_free (GuidPartitionTableHeader_t* pth) +{ + PED_ASSERT (pth != NULL, return); + PED_ASSERT (pth->Reserved2 != NULL, return); + + ped_free (pth->Reserved2); + ped_free (pth); +} + +static uint8_t* +pth_get_raw (const PedDevice* dev, const GuidPartitionTableHeader_t* pth) +{ + uint8_t* pth_raw = ped_malloc (pth_get_size (dev)); + int size_static = pth_get_size_static (dev); + + PED_ASSERT (pth != NULL, return 0); + PED_ASSERT (pth->Reserved2 != NULL, return 0); + + memcpy (pth_raw, pth, size_static); + memcpy (pth_raw + size_static, pth->Reserved2, pth_get_size_rsv2 (dev)); + + return pth_raw; +} + + +/** + * swap_uuid_and_efi_guid() - converts between uuid formats + * @uuid - uuid_t in either format (converts it to the other) + * + * There are two different representations for Globally Unique Identifiers + * (GUIDs or UUIDs). + * + * The RFC specifies a UUID as a string of 16 bytes, essentially + * a big-endian array of char. + * Intel, in their EFI Specification, references the same RFC, but + * then defines a GUID as a structure of little-endian fields. + * Coincidentally, both structures have the same format when unparsed. + * + * When read from disk, EFI GUIDs are in struct of little endian format, + * and need to be converted to be treated as uuid_t in memory. + * + * When writing to disk, uuid_ts need to be converted into EFI GUIDs. + * + * Blame Intel. + */ +static void +swap_uuid_and_efi_guid(uuid_t uuid) +{ + int i; + efi_guid_t *guid = (efi_guid_t *)uuid; + + PED_ASSERT(uuid != NULL, return); + guid->time_low = PED_SWAP32(guid->time_low); + guid->time_mid = PED_SWAP16(guid->time_mid); + guid->time_hi_and_version = PED_SWAP16(guid->time_hi_and_version); +} + +/* returns the EFI-style CRC32 value for buf + * This function uses the crc32 function by Gary S. Brown, + * but seeds the function with ~0, and xor's with ~0 at the end. + */ +static inline uint32_t +efi_crc32(const void *buf, unsigned long len) +{ + return (__efi_crc32(buf, len, ~0L) ^ ~0L); +} + +static inline uint32_t +pth_crc32(const PedDevice* dev, const GuidPartitionTableHeader_t* pth) +{ + uint8_t* pth_raw = pth_get_raw (dev, pth); + uint32_t crc32 = 0; + + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (pth != NULL, return 0); + + crc32 = efi_crc32 (pth_raw, pth_get_size_static (dev)); + + ped_free (pth_raw); + + return crc32; +} + +static inline int +guid_cmp (efi_guid_t left, efi_guid_t right) +{ + return memcmp(&left, &right, sizeof(efi_guid_t)); +} + +/* checks if 'mbr' is a protective MBR partition table */ +static inline int +_pmbr_is_valid (const LegacyMBR_t* mbr) +{ + int i; + + PED_ASSERT(mbr != NULL, return 0); + + if (mbr->Signature != PED_CPU_TO_LE16(MSDOS_MBR_SIGNATURE)) + return 0; + for (i = 0; i < 4; i++) { + if (mbr->PartitionRecord[i].OSType == EFI_PMBR_OSTYPE_EFI) + return 1; + } + return 0; +} + +static int +gpt_probe (const PedDevice * dev) +{ + GuidPartitionTableHeader_t* gpt = NULL; + uint8_t* pth_raw = ped_malloc (pth_get_size (dev)); + LegacyMBR_t legacy_mbr; + int gpt_sig_found = 0; + + PED_ASSERT (dev != NULL, return 0); + + if (ped_device_read(dev, pth_raw, 1, GPT_HEADER_SECTORS) + || ped_device_read(dev, pth_raw, dev->length - 1, GPT_HEADER_SECTORS)) { + gpt = pth_new_from_raw (dev, pth_raw); + if (gpt->Signature == PED_CPU_TO_LE64(GPT_HEADER_SIGNATURE)) + gpt_sig_found = 1; + } + + ped_free (pth_raw); + + if (gpt) + pth_free (gpt); + + + if (!gpt_sig_found) + return 0; + + if (ped_device_read(dev, &legacy_mbr, 0, GPT_HEADER_SECTORS)) { + if (!_pmbr_is_valid (&legacy_mbr)) { + int ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_YES_NO, + _("%s contains GPT signatures, indicating that it has " + "a GPT table. However, it does not have a valid " + "fake msdos partition table, as it should. Perhaps " + "it was corrupted -- possibly by a program that " + "doesn't understand GPT partition tables. Or " + "perhaps you deleted the GPT table, and are now " + "using an msdos partition table. Is this a GPT " + "partition table?"), + dev->path); + if (ex_status == PED_EXCEPTION_NO) + return 0; + } + } + + return 1; +} + +#ifndef DISCOVER_ONLY +/* writes zeros to the PMBR and the primary and alternate GPTHs and PTEs */ +static int +gpt_clobber(PedDevice * dev) +{ + LegacyMBR_t pmbr; + uint8_t* zeroed_pth_raw = ped_malloc (pth_get_size (dev)); + uint8_t* pth_raw = ped_malloc (pth_get_size (dev)); + GuidPartitionTableHeader_t* gpt; + GuidPartitionEntry_t ptes[GPT_DEFAULT_PARTITION_ENTRIES]; + + PED_ASSERT (dev != NULL, return 0); + + memset(&pmbr, 0, sizeof(pmbr)); + memset(zeroed_pth_raw, 0, pth_get_size (dev)); + + /* + * TO DISCUSS: check whether checksum is correct? + * If not, we might get a wrong AlternateLBA field and destroy + * one sector of random data. + */ + if (!ped_device_read(dev, pth_raw, + GPT_PRIMARY_HEADER_LBA, GPT_HEADER_SECTORS)) + goto error_free; + + gpt = pth_new_from_raw (dev, pth_raw); + + if (!ped_device_write(dev, &pmbr, GPT_PMBR_LBA, GPT_PMBR_SECTORS)) + goto error_free_with_gpt; + if (!ped_device_write(dev, &zeroed_pth_raw, + GPT_PRIMARY_HEADER_LBA, GPT_HEADER_SECTORS)) + goto error_free_with_gpt; + if (!ped_device_write(dev, &zeroed_pth_raw, dev->length - GPT_HEADER_SECTORS, + GPT_HEADER_SECTORS)) + goto error_free_with_gpt; + + if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA) < dev->length - 1) { + if (!ped_device_write(dev, gpt, + PED_LE64_TO_CPU (gpt->AlternateLBA), + GPT_HEADER_SECTORS)) + return 0; + } + + pth_free (gpt); + + return 1; + +error_free_with_gpt: + pth_free (gpt); +error_free: + ped_free (pth_raw); + ped_free (zeroed_pth_raw); + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedDisk * +gpt_alloc (const PedDevice * dev) +{ + PedDisk* disk; + GPTDiskData *gpt_disk_data; + PedSector data_start, data_end; + + disk = _ped_disk_alloc ((PedDevice*)dev, &gpt_disk_type); + if (!disk) + goto error; + disk->disk_specific = gpt_disk_data = ped_malloc (sizeof (GPTDiskData)); + if (!disk->disk_specific) + goto error_free_disk; + + data_start = 2 + GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size; + data_end = dev->length - 2 + - GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size; + ped_geometry_init (&gpt_disk_data->data_area, dev, data_start, + data_end - data_start + 1); + gpt_disk_data->entry_count = GPT_DEFAULT_PARTITION_ENTRIES; + uuid_generate ((unsigned char*) &gpt_disk_data->uuid); + swap_uuid_and_efi_guid((unsigned char*)(&gpt_disk_data->uuid)); + return disk; + +error_free_disk: + ped_free (disk); +error: + return NULL; +} + +static PedDisk* +gpt_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + GPTDiskData* new_disk_data; + GPTDiskData* old_disk_data; + + new_disk = ped_disk_new_fresh (disk->dev, &gpt_disk_type); + if (!new_disk) + return NULL; + + old_disk_data = disk->disk_specific; + new_disk_data = new_disk->disk_specific; + + ped_geometry_init (&new_disk_data->data_area, disk->dev, + old_disk_data->data_area.start, + old_disk_data->data_area.length); + new_disk_data->entry_count = old_disk_data->entry_count; + new_disk_data->uuid = old_disk_data->uuid; + return new_disk; +} + +static void +gpt_free(PedDisk * disk) +{ + ped_disk_delete_all (disk); + ped_free (disk->disk_specific); + _ped_disk_free (disk); +} + +static int +_header_is_valid (const PedDevice* dev, GuidPartitionTableHeader_t* gpt) +{ + uint32_t crc, origcrc; + + if (PED_LE64_TO_CPU (gpt->Signature) != GPT_HEADER_SIGNATURE) + return 0; + if (PED_LE32_TO_CPU (gpt->HeaderSize) + > pth_get_size_static (dev)) + return 0; + + origcrc = gpt->HeaderCRC32; + gpt->HeaderCRC32 = 0; + crc = pth_crc32 (dev, gpt); + gpt->HeaderCRC32 = origcrc; + + return crc == PED_LE32_TO_CPU (origcrc); +} + +static int +_read_header (const PedDevice* dev, GuidPartitionTableHeader_t** gpt, + PedSector where) +{ + uint8_t* pth_raw = ped_malloc (pth_get_size (dev)); + + PED_ASSERT (dev != NULL, return 0); + + if (!ped_device_read (dev, pth_raw, where, GPT_HEADER_SECTORS)) { + ped_free (pth_raw); + return 0; + } + + *gpt = pth_new_from_raw (dev, pth_raw); + + ped_free (pth_raw); + + if (_header_is_valid (dev, *gpt)) + return 1; + + pth_free (*gpt); + return 0; +} + +static int +_parse_header (PedDisk* disk, GuidPartitionTableHeader_t* gpt, + int *update_needed) +{ + GPTDiskData* gpt_disk_data = disk->disk_specific; + PedSector first_usable; + PedSector last_usable; + PedSector last_usable_if_grown, last_usable_min_default; + static int asked_already; + + PED_ASSERT (_header_is_valid (disk->dev, gpt), return 0); + +#ifndef DISCOVER_ONLY + if (PED_CPU_TO_LE32 (gpt->Revision) > GPT_HEADER_REVISION_V1_02 + || PED_CPU_TO_LE32 (gpt->HeaderSize) != pth_get_size_static ( + disk->dev)) { + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The format of the GPT partition table is version " + "%x, which is newer than what Parted can " + "recognise. Please tell us! bug-parted@gnu.org"), + PED_CPU_TO_LE32 (gpt->Revision)) + != PED_EXCEPTION_IGNORE) + return 0; + } +#endif + + first_usable = PED_CPU_TO_LE64 (gpt->FirstUsableLBA); + last_usable = PED_CPU_TO_LE64 (gpt->LastUsableLBA); + + +/* + Need to check whether the volume has grown, the LastUsableLBA is + normally set to disk->dev->length - 2 - ptes_size (at least for parted + created volumes), where ptes_size is the number of entries * + size of each entry / sector size or 16k / sector size, whatever the greater. + If the volume has grown, offer the user the chance to use the new + space or continue with the current usable area. Only ask once per + parted invocation. +*/ + + last_usable_if_grown + = PED_CPU_TO_LE64 (disk->dev->length - 2 - + ((PedSector)(PED_CPU_TO_LE32(gpt->NumberOfPartitionEntries)) * + (PedSector)(PED_CPU_TO_LE32(gpt->SizeOfPartitionEntry)) / + disk->dev->sector_size)); + + last_usable_min_default = disk->dev->length - 2 - + GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / disk->dev->sector_size; + + if ( last_usable_if_grown > last_usable_min_default ) { + + last_usable_if_grown = last_usable_min_default; + } + + + PED_ASSERT (last_usable > first_usable, return 0); + PED_ASSERT (last_usable <= disk->dev->length, return 0); + + PED_ASSERT (last_usable_if_grown > first_usable, return 0); + PED_ASSERT (last_usable_if_grown <= disk->dev->length, return 0); + + if ( !asked_already && last_usable < last_usable_if_grown ) { + + PedExceptionOption q; + + q = ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE, + _("Not all of the space available to %s appears " + "to be used, you can fix the GPT to use all of the " + "space (an extra %llu blocks) or continue with the " + "current setting? "), disk->dev->path, + (uint64_t)(last_usable_if_grown - last_usable)); + + + if (q == PED_EXCEPTION_FIX) { + + last_usable = last_usable_if_grown; + *update_needed = 1; + + } + else if (q != PED_EXCEPTION_UNHANDLED ) { + + asked_already = 1; + } + } + + ped_geometry_init (&gpt_disk_data->data_area, disk->dev, + first_usable, last_usable - first_usable + 1); + + + gpt_disk_data->entry_count + = PED_CPU_TO_LE32 (gpt->NumberOfPartitionEntries); + PED_ASSERT (gpt_disk_data->entry_count > 0, return 0); + PED_ASSERT (gpt_disk_data->entry_count <= 8192, return 0); + + gpt_disk_data->uuid = gpt->DiskGUID; + + return 1; +} + +static PedPartition* +_parse_part_entry (PedDisk* disk, GuidPartitionEntry_t* pte) +{ + PedPartition* part; + GPTPartitionData* gpt_part_data; + unsigned int i; + + part = ped_partition_new (disk, 0, NULL, + PED_LE64_TO_CPU(pte->StartingLBA), + PED_LE64_TO_CPU(pte->EndingLBA)); + if (!part) + return NULL; + + gpt_part_data = part->disk_specific; + gpt_part_data->type = pte->PartitionTypeGuid; + gpt_part_data->uuid = pte->UniquePartitionGuid; + for (i = 0; i < 72 / sizeof (efi_char16_t); i++) + gpt_part_data->name[i] = (efi_char16_t) PED_LE16_TO_CPU( + (uint16_t) pte->PartitionName[i]); + gpt_part_data->name[i] = 0; + + gpt_part_data->lvm = gpt_part_data->raid + = gpt_part_data->boot = gpt_part_data->hp_service + = gpt_part_data->hidden = gpt_part_data->msftres = 0; + + if (pte->Attributes.RequiredToFunction & 0x1) + gpt_part_data->hidden = 1; + + if (!guid_cmp (gpt_part_data->type, PARTITION_SYSTEM_GUID)) + gpt_part_data->boot = 1; + else if (!guid_cmp (gpt_part_data->type, PARTITION_RAID_GUID)) + gpt_part_data->raid = 1; + else if (!guid_cmp (gpt_part_data->type, PARTITION_LVM_GUID)) + gpt_part_data->lvm = 1; + else if (!guid_cmp (gpt_part_data->type, PARTITION_HPSERVICE_GUID)) + gpt_part_data->hp_service = 1; + else if (!guid_cmp (gpt_part_data->type, PARTITION_MSFT_RESERVED_GUID)) + gpt_part_data->msftres = 1; + + return part; +} + +/************************************************************ + * Intel is changing the EFI Spec. (after v1.02) to say that a + * disk is considered to have a GPT label only if the GPT + * structures are correct, and the MBR is actually a Protective + * MBR (has one 0xEE type partition). + * Problem occurs when a GPT-partitioned disk is then + * edited with a legacy (non-GPT-aware) application, such as + * fdisk (which doesn't generally erase the PGPT or AGPT). + * How should such a disk get handled? As a GPT disk (throwing + * away the fdisk changes), or as an MSDOS disk (throwing away + * the GPT information). Previously, I've taken the GPT-is-right, + * MBR is wrong, approach, to stay consistent with the EFI Spec. + * Intel disagrees, saying the disk should then be treated + * as having a msdos label, not a GPT label. If this is true, + * then what's the point of having an AGPT, since if the PGPT + * is screwed up, likely the PMBR is too, and the PMBR becomes + * a single point of failure. + * So, in the Linux kernel, I'm going to test for PMBR, and + * warn if it's not there, and treat the disk as MSDOS, with a note + * for users to use Parted to "fix up" their disk if they + * really want it to be considered GPT. + ************************************************************/ +static int +gpt_read (PedDisk * disk) +{ + GPTDiskData *gpt_disk_data = disk->disk_specific; + GuidPartitionTableHeader_t* gpt; + GuidPartitionEntry_t* ptes; + int ptes_size; + int i; +#ifndef DISCOVER_ONLY + int write_back = 0; +#endif + + ped_disk_delete_all (disk); + + /* + * motivation: let the user decide about the pmbr... during + * ped_disk_probe(), they probably didn't get a choice... + */ + if (!gpt_probe (disk->dev)) + goto error; + + if (_read_header (disk->dev, &gpt, 1)) { + PED_ASSERT ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA) + <= disk->dev->length - 1, goto error_free_gpt); + if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA) + < disk->dev->length - 1) { + char* zeros = ped_malloc (pth_get_size (disk->dev)); + +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL, + _("The backup GPT table is not at the end of the disk, as it " + "should be. This might mean that another operating system " + "believes the disk is smaller. Fix, by moving the backup " + "to the end (and removing the old backup)?")) + == PED_EXCEPTION_CANCEL) + goto error_free_gpt; + + write_back = 1; + memset (zeros, 0, disk->dev->sector_size); + ped_device_write (disk->dev, zeros, + PED_LE64_TO_CPU (gpt->AlternateLBA), + 1); +#endif /* !DISCOVER_ONLY */ + } + } else { /* primary GPT *not* ok */ + int alternate_ok = 0; + +#ifndef DISCOVER_ONLY + write_back = 1; +#endif + + if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA) + < disk->dev->length - 1) { + alternate_ok = _read_header (disk->dev, &gpt, + PED_LE64_TO_CPU(gpt->AlternateLBA)); + } + if (!alternate_ok) { + alternate_ok = _read_header (disk->dev, &gpt, + disk->dev->length - 1); + } + + if (alternate_ok) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_OK_CANCEL, + _("The primary GPT table is corrupt, but the " + "backup appears OK, so that will be used.")) + == PED_EXCEPTION_CANCEL) + goto error_free_gpt; + } else { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Both the primary and backup GPT tables " + "are corrupt. Try making a fresh table, " + "and using Parted's rescue feature to " + "recover partitions.")); + goto error; + } + } + + if (!_parse_header (disk, gpt, &write_back)) + goto error_free_gpt; + + + ptes_size = sizeof (GuidPartitionEntry_t) * gpt_disk_data->entry_count; + ptes = (GuidPartitionEntry_t*) ped_malloc (ptes_size); + if (!ped_device_read (disk->dev, ptes, + PED_LE64_TO_CPU(gpt->PartitionEntryLBA), + ptes_size / disk->dev->sector_size)) + goto error_free_ptes; + + for (i = 0; i < gpt_disk_data->entry_count; i++) { + PedPartition* part; + PedConstraint* constraint_exact; + + if (!guid_cmp (ptes[i].PartitionTypeGuid, UNUSED_ENTRY_GUID)) + continue; + + part = _parse_part_entry (disk, &ptes[i]); + if (!part) + goto error_delete_all; + + part->fs_type = ped_file_system_probe (&part->geom); + part->num = i + 1; + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition(disk, part, constraint_exact)) { + ped_partition_destroy (part); + goto error_delete_all; + } + ped_constraint_destroy (constraint_exact); + } + ped_free (ptes); + +#ifndef DISCOVER_ONLY + if (write_back) + ped_disk_commit_to_dev (disk); +#endif + + return 1; + +error_delete_all: + ped_disk_delete_all (disk); +error_free_ptes: + ped_free (ptes); +error_free_gpt: + pth_free (gpt); +error: + return 0; +} + +#ifndef DISCOVER_ONLY +/* Writes the protective MBR (to keep DOS happy) */ +static int +_write_pmbr (PedDevice * dev) +{ + LegacyMBR_t pmbr; + + memset(&pmbr, 0, sizeof(pmbr)); + pmbr.Signature = PED_CPU_TO_LE16(MSDOS_MBR_SIGNATURE); + pmbr.PartitionRecord[0].OSType = EFI_PMBR_OSTYPE_EFI; + pmbr.PartitionRecord[0].StartSector = 1; + pmbr.PartitionRecord[0].EndHead = 0xFE; + pmbr.PartitionRecord[0].EndSector = 0xFF; + pmbr.PartitionRecord[0].EndTrack = 0xFF; + pmbr.PartitionRecord[0].StartingLBA = PED_CPU_TO_LE32(1); + if ((dev->length - 1ULL) > 0xFFFFFFFFULL) + pmbr.PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32(0xFFFFFFFF); + else + pmbr.PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32(dev->length - 1UL); + + return ped_device_write (dev, &pmbr, GPT_PMBR_LBA, GPT_PMBR_SECTORS); +} + +static void +_generate_header (PedDisk* disk, int alternate, uint32_t ptes_crc, + GuidPartitionTableHeader_t** gpt_p) +{ + GPTDiskData* gpt_disk_data = disk->disk_specific; + GuidPartitionTableHeader_t* gpt; + + *gpt_p = pth_new_zeroed (disk->dev); + + gpt = *gpt_p; + + gpt->Signature = PED_CPU_TO_LE64 (GPT_HEADER_SIGNATURE); + gpt->Revision = PED_CPU_TO_LE32 (GPT_HEADER_REVISION_V1_00); + + /* per 1.00 spec */ + gpt->HeaderSize = PED_CPU_TO_LE32 (pth_get_size_static (disk->dev)); + gpt->HeaderCRC32 = 0; + gpt->Reserved1 = 0; + + if (alternate) { + PedSector ptes_size = gpt_disk_data->entry_count + * sizeof (GuidPartitionEntry_t) / disk->dev->sector_size; + + gpt->MyLBA = PED_CPU_TO_LE64 (disk->dev->length - 1); + gpt->AlternateLBA = PED_CPU_TO_LE64 (1); + gpt->PartitionEntryLBA + = PED_CPU_TO_LE64 (disk->dev->length - 1 - ptes_size); + } else { + gpt->MyLBA = PED_CPU_TO_LE64 (1); + gpt->AlternateLBA = PED_CPU_TO_LE64 (disk->dev->length - 1); + gpt->PartitionEntryLBA = PED_CPU_TO_LE64 (2); + } + + gpt->FirstUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.start); + gpt->LastUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.end); + gpt->DiskGUID = gpt_disk_data->uuid; + gpt->NumberOfPartitionEntries + = PED_CPU_TO_LE32 (gpt_disk_data->entry_count); + gpt->SizeOfPartitionEntry + = PED_CPU_TO_LE32 (sizeof (GuidPartitionEntry_t)); + gpt->PartitionEntryArrayCRC32 = PED_CPU_TO_LE32 (ptes_crc); + gpt->HeaderCRC32 = PED_CPU_TO_LE32 (pth_crc32 (disk->dev, gpt)); +} + +static void +_partition_generate_part_entry (PedPartition* part, GuidPartitionEntry_t* pte) +{ + GPTPartitionData* gpt_part_data = part->disk_specific; + unsigned int i; + + PED_ASSERT (gpt_part_data != NULL, return); + + pte->PartitionTypeGuid = gpt_part_data->type; + pte->UniquePartitionGuid = gpt_part_data->uuid; + pte->StartingLBA = PED_CPU_TO_LE64(part->geom.start); + pte->EndingLBA = PED_CPU_TO_LE64(part->geom.end); + memset (&pte->Attributes, 0, sizeof (GuidPartitionEntryAttributes_t)); + + if (gpt_part_data->hidden) + pte->Attributes.RequiredToFunction = 1; + + for (i = 0; i < 72 / sizeof(efi_char16_t); i++) + pte->PartitionName[i] + = (efi_char16_t) PED_CPU_TO_LE16( + (uint16_t) gpt_part_data->name[i]); +} + +static int +gpt_write(PedDisk * disk) +{ + GPTDiskData* gpt_disk_data; + GuidPartitionEntry_t* ptes; + uint32_t ptes_crc; + uint8_t* pth_raw = ped_malloc (pth_get_size (disk->dev)); + GuidPartitionTableHeader_t* gpt; + PedPartition* part; + int ptes_size; + unsigned int i; + + PED_ASSERT (disk != NULL, goto error); + PED_ASSERT (disk->dev != NULL, goto error); + PED_ASSERT (disk->disk_specific != NULL, goto error); + + gpt_disk_data = disk->disk_specific; + + ptes_size = sizeof (GuidPartitionEntry_t) * gpt_disk_data->entry_count; + ptes = (GuidPartitionEntry_t*) ped_malloc (ptes_size); + if (!ptes) + goto error; + memset (ptes, 0, ptes_size); + for (part = ped_disk_next_partition (disk, NULL); part; + part = ped_disk_next_partition (disk, part)) { + if (part->type != 0) + continue; + _partition_generate_part_entry (part, &ptes[part->num - 1]); + } + + ptes_crc = efi_crc32 (ptes, ptes_size); + + /* Write protective MBR */ + if (!_write_pmbr (disk->dev)) + goto error_free_ptes; + + /* Write PTH and PTEs */ + _generate_header (disk, 0, ptes_crc, &gpt); + pth_raw = pth_get_raw (disk->dev, gpt); + if (!ped_device_write (disk->dev, pth_raw, 1, 1)) + goto error_free_ptes; + if (!ped_device_write (disk->dev, ptes, 2, ptes_size / disk->dev->sector_size)) + goto error_free_ptes; + + /* Write Alternate PTH & PTEs */ + _generate_header (disk, 1, ptes_crc, &gpt); + pth_raw = pth_get_raw (disk->dev, gpt); + if (!ped_device_write (disk->dev, pth_raw, disk->dev->length - 1, 1)) + goto error_free_ptes; + if (!ped_device_write (disk->dev, ptes, + disk->dev->length - 1 - ptes_size / disk->dev->sector_size, + ptes_size / disk->dev->sector_size)) + goto error_free_ptes; + + ped_free (ptes); + return ped_device_sync (disk->dev); + +error_free_ptes: + ped_free (ptes); +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static int +add_metadata_part(PedDisk * disk, PedSector start, PedSector length) +{ + PedPartition* part; + PedConstraint* constraint_exact; + PED_ASSERT(disk != NULL, return 0); + + part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + start, start + length - 1); + if (!part) + goto error; + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) + goto error_destroy_constraint; + ped_constraint_destroy (constraint_exact); + return 1; + +error_destroy_constraint: + ped_constraint_destroy (constraint_exact); +error_destroy_part: + ped_partition_destroy (part); +error: + return 0; +} + +static PedPartition* +gpt_partition_new (const PedDisk* disk, + PedPartitionType part_type, const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + GPTPartitionData* gpt_part_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (part_type != 0) + return part; + + gpt_part_data = part->disk_specific = + ped_malloc (sizeof (GPTPartitionData)); + if (!gpt_part_data) + goto error_free_part; + + gpt_part_data->type = PARTITION_BASIC_DATA_GUID; + gpt_part_data->lvm = 0; + gpt_part_data->raid = 0; + gpt_part_data->boot = 0; + gpt_part_data->hp_service = 0; + gpt_part_data->hidden = 0; + gpt_part_data->msftres = 0; + uuid_generate ((unsigned char*) &gpt_part_data->uuid); + swap_uuid_and_efi_guid((unsigned char*)(&gpt_part_data->uuid)); + strcpy (gpt_part_data->name, ""); + return part; + +error_free_part: + _ped_partition_free (part); +error: + return NULL; +} + +static PedPartition* +gpt_partition_duplicate (const PedPartition* part) +{ + PedPartition* result; + GPTPartitionData* part_data = part->disk_specific; + GPTPartitionData* result_data; + + result = _ped_partition_alloc (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + if (!result) + goto error; + result->num = part->num; + + if (result->type != 0) + return result; + + result_data = result->disk_specific = + ped_malloc (sizeof (GPTPartitionData)); + if (!result_data) + goto error_free_part; + + result_data->type = part_data->type; + result_data->uuid = part_data->uuid; + strcpy (result_data->name, part_data->name); + return result; + +error_free_part: + _ped_partition_free (result); +error: + return NULL; +} + +static void +gpt_partition_destroy (PedPartition *part) +{ + if (part->type == 0) { + PED_ASSERT (part->disk_specific != NULL, return); + ped_free (part->disk_specific); + } + + _ped_partition_free (part); +} + +static int +gpt_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + GPTPartitionData* gpt_part_data = part->disk_specific; + + PED_ASSERT (gpt_part_data != NULL, return 0); + + part->fs_type = fs_type; + + if (gpt_part_data->lvm) { + gpt_part_data->type = PARTITION_LVM_GUID; + return 1; + } + if (gpt_part_data->raid) { + gpt_part_data->type = PARTITION_RAID_GUID; + return 1; + } + if (gpt_part_data->boot) { + gpt_part_data->type = PARTITION_SYSTEM_GUID; + return 1; + } + if (gpt_part_data->hp_service) { + gpt_part_data->type = PARTITION_HPSERVICE_GUID; + return 1; + } + if (gpt_part_data->msftres) { + gpt_part_data->type = PARTITION_MSFT_RESERVED_GUID; + return 1; + } + + if (fs_type) { + if (strncmp (fs_type->name, "fat", 3) == 0 + || strcmp (fs_type->name, "ntfs") == 0) { + gpt_part_data->type = PARTITION_MSFT_RESERVED_GUID; + return 1; + } + if (strncmp (fs_type->name, "hfs", 3) == 0) { + gpt_part_data->type = PARTITION_APPLE_HFS_GUID; + return 1; + } + if (strstr (fs_type->name, "swap")) { + gpt_part_data->type = PARTITION_SWAP_GUID; + return 1; + } + } + + gpt_part_data->type = PARTITION_BASIC_DATA_GUID; + return 1; +} + +/* Allocate metadata partitions for the GPTH and PTES */ +static int +gpt_alloc_metadata (PedDisk * disk) +{ + PedSector gptlength, pteslength = 0; + GPTDiskData *gpt_disk_data; + + PED_ASSERT(disk != NULL, return 0); + PED_ASSERT(disk->dev != NULL, return 0); + PED_ASSERT(disk->disk_specific != NULL, return 0); + gpt_disk_data = disk->disk_specific; + + gptlength = ped_div_round_up (sizeof (GuidPartitionTableHeader_t), + disk->dev->sector_size); + pteslength = ped_div_round_up (gpt_disk_data->entry_count + * sizeof (GuidPartitionEntry_t), disk->dev->sector_size); + + /* metadata at the start of the disk includes the MBR */ + if (!add_metadata_part(disk, GPT_PMBR_LBA, + GPT_PMBR_SECTORS + gptlength + pteslength)) + return 0; + + /* metadata at the end of the disk */ + if (!add_metadata_part(disk, disk->dev->length - gptlength - pteslength, + gptlength + pteslength)) + return 0; + + return 1; +} + +/* Does nothing, as the read/new/destroy functions maintain part->num */ +static int +gpt_partition_enumerate (PedPartition* part) +{ + GPTDiskData* gpt_disk_data = part->disk->disk_specific; + int i; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + + for (i = 1; i <= gpt_disk_data->entry_count; i++) { + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + return 1; + } + } + + PED_ASSERT (0, return 0); return 0; +} + +static int +gpt_partition_set_flag(PedPartition *part, + PedPartitionFlag flag, + int state) +{ + GPTPartitionData *gpt_part_data; + PED_ASSERT(part != NULL, return 0); + PED_ASSERT(part->disk_specific != NULL, return 0); + gpt_part_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + gpt_part_data->boot = state; + if (state) + gpt_part_data->raid + = gpt_part_data->lvm + = gpt_part_data->hp_service + = gpt_part_data->msftres = 0; + return gpt_partition_set_system (part, part->fs_type); + case PED_PARTITION_RAID: + gpt_part_data->raid = state; + if (state) + gpt_part_data->boot + = gpt_part_data->lvm + = gpt_part_data->hp_service + = gpt_part_data->msftres = 0; + return gpt_partition_set_system (part, part->fs_type); + case PED_PARTITION_LVM: + gpt_part_data->lvm = state; + if (state) + gpt_part_data->boot + = gpt_part_data->raid + = gpt_part_data->hp_service + = gpt_part_data->msftres = 0; + return gpt_partition_set_system (part, part->fs_type); + case PED_PARTITION_HPSERVICE: + gpt_part_data->hp_service = state; + if (state) + gpt_part_data->boot + = gpt_part_data->raid + = gpt_part_data->lvm + = gpt_part_data->msftres = 0; + return gpt_partition_set_system (part, part->fs_type); + case PED_PARTITION_MSFT_RESERVED: + gpt_part_data->msftres = state; + if (state) + gpt_part_data->boot + = gpt_part_data->raid + = gpt_part_data->lvm + = gpt_part_data->hp_service = 0; + return gpt_partition_set_system (part, part->fs_type); + case PED_PARTITION_HIDDEN: + gpt_part_data->hidden = state; + return 1; + case PED_PARTITION_SWAP: + case PED_PARTITION_ROOT: + case PED_PARTITION_LBA: + default: + return 0; + } + return 1; +} + +static int +gpt_partition_get_flag(const PedPartition *part, PedPartitionFlag flag) +{ + GPTPartitionData *gpt_part_data; + PED_ASSERT(part->disk_specific != NULL, return 0); + gpt_part_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_RAID: + return gpt_part_data->raid; + case PED_PARTITION_LVM: + return gpt_part_data->lvm; + case PED_PARTITION_BOOT: + return gpt_part_data->boot; + case PED_PARTITION_HPSERVICE: + return gpt_part_data->hp_service; + case PED_PARTITION_MSFT_RESERVED: + return gpt_part_data->msftres; + case PED_PARTITION_HIDDEN: + return gpt_part_data->hidden; + case PED_PARTITION_SWAP: + case PED_PARTITION_LBA: + case PED_PARTITION_ROOT: + default: + return 0; + } + return 0; +} + +static int +gpt_partition_is_flag_available(const PedPartition * part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_RAID: + case PED_PARTITION_LVM: + case PED_PARTITION_BOOT: + case PED_PARTITION_HPSERVICE: + case PED_PARTITION_MSFT_RESERVED: + case PED_PARTITION_HIDDEN: + return 1; + case PED_PARTITION_SWAP: + case PED_PARTITION_ROOT: + case PED_PARTITION_LBA: + default: + return 0; + } + return 0; +} + +static void +gpt_partition_set_name (PedPartition *part, const char *name) +{ + GPTPartitionData *gpt_part_data = part->disk_specific; + + strncpy (gpt_part_data->name, name, 36); + gpt_part_data->name [36] = 0; +} + +static const char * +gpt_partition_get_name (const PedPartition * part) +{ + GPTPartitionData* gpt_part_data = part->disk_specific; + return gpt_part_data->name; +} + +static int +gpt_get_max_primary_partition_count (const PedDisk *disk) +{ + const GPTDiskData* gpt_disk_data = disk->disk_specific; + return gpt_disk_data->entry_count; +} + +static PedConstraint* +_non_metadata_constraint (const PedDisk* disk) +{ + GPTDiskData* gpt_disk_data = disk->disk_specific; + + return ped_constraint_new_from_max (&gpt_disk_data->data_area); +} + +static int +gpt_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL, return 0); + + if (_ped_partition_attempt_align (part, constraint, + _non_metadata_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static PedDiskOps gpt_disk_ops = { + probe: gpt_probe, +#ifndef DISCOVER_ONLY + clobber: gpt_clobber, +#else + clobber: NULL, +#endif + alloc: gpt_alloc, + duplicate: gpt_duplicate, + free: gpt_free, + read: gpt_read, +#ifndef DISCOVER_ONLY + write: gpt_write, +#else + write: NULL, +#endif + partition_new: gpt_partition_new, + partition_duplicate: gpt_partition_duplicate, + partition_destroy: gpt_partition_destroy, + partition_set_system: gpt_partition_set_system, + partition_set_flag: gpt_partition_set_flag, + partition_get_flag: gpt_partition_get_flag, + partition_is_flag_available: gpt_partition_is_flag_available, + partition_set_name: gpt_partition_set_name, + partition_get_name: gpt_partition_get_name, + partition_align: gpt_partition_align, + partition_enumerate: gpt_partition_enumerate, + alloc_metadata: gpt_alloc_metadata, + get_max_primary_partition_count: gpt_get_max_primary_partition_count +}; + +static PedDiskType gpt_disk_type = { + next: NULL, + name: "gpt", + ops: &gpt_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME +}; + +void +ped_disk_gpt_init() +{ + PED_ASSERT(sizeof(GuidPartitionEntryAttributes_t) == 8, return); + PED_ASSERT(sizeof(GuidPartitionEntry_t) == 128, return); + + ped_register_disk_type(&gpt_disk_type); +} + +void +ped_disk_gpt_done() +{ + ped_unregister_disk_type(&gpt_disk_type); +} + diff --git a/libparted/labels/loop.c b/libparted/labels/loop.c new file mode 100644 index 0000000..272d61b --- /dev/null +++ b/libparted/labels/loop.c @@ -0,0 +1,338 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <string.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define LOOP_SIGNATURE "GNU Parted Loopback 0" + +static PedDiskType loop_disk_type; + +static PedDisk* loop_alloc (const PedDevice* dev); +static void loop_free (PedDisk* disk); + +static int +loop_probe (const PedDevice* dev) +{ + PedDisk* disk; + char buf [512]; + int result; + + if (dev->sector_size != 512) + return 0; + + disk = loop_alloc (dev); + if (!disk) + goto error; + + if (!ped_device_read (dev, buf, 0, 1)) + goto error_destroy_disk; + if (strncmp (buf, LOOP_SIGNATURE, strlen (LOOP_SIGNATURE)) == 0) { + result = 1; + } else { + PedGeometry* geom; + + geom = ped_geometry_new (dev, 0, disk->dev->length); + if (!geom) + goto error_destroy_disk; + result = ped_file_system_probe (geom) != NULL; + ped_geometry_destroy (geom); + } + loop_free (disk); + return result; + +error_destroy_disk: + loop_free (disk); +error: + return 0; +} + +#ifndef DISCOVER_ONLY +static int +loop_clobber (PedDevice* dev) +{ + char buf [512]; + PedSector i = 0; + + PED_ASSERT (dev != NULL, return 0); + + memset (buf, 0, 512); + + while (loop_probe (dev)) { + if (!ped_device_write (dev, buf, i++, 1)) + return 0; + } + return 1; +} +#endif /* !DISCOVER_ONLY */ + +static PedDisk* +loop_alloc (const PedDevice* dev) +{ + PedDisk* disk; + + PED_ASSERT (dev != NULL, return 0); + + if (dev->length < 256) + return NULL; + return _ped_disk_alloc ((PedDevice*)dev, &loop_disk_type); +} + +static PedDisk* +loop_duplicate (const PedDisk* disk) +{ + return ped_disk_new_fresh (disk->dev, &loop_disk_type); +} + +static void +loop_free (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, return); + + _ped_disk_free (disk); +} + +static int +loop_read (PedDisk* disk) +{ + PedDevice* dev = NULL; + char buf [512]; + PedGeometry* geom; + PedFileSystemType* fs_type; + PedPartition* part; + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL, return 0); + dev = disk->dev; + constraint_any = ped_constraint_any (dev); + + ped_disk_delete_all (disk); + + if (!ped_device_read (dev, buf, 0, 1)) + goto error; + if (!strncmp (buf, LOOP_SIGNATURE, strlen (LOOP_SIGNATURE))) + return 1; + + geom = ped_geometry_new (dev, 0, dev->length); + if (!geom) + goto error; + + fs_type = ped_file_system_probe (geom); + if (!fs_type) + goto error_free_geom; + + part = ped_partition_new (disk, 0, fs_type, geom->start, geom->end); + ped_geometry_destroy (geom); + if (!part) + goto error; + part->fs_type = fs_type; + + if (!ped_disk_add_partition (disk, part, constraint_any)) + goto error; + ped_constraint_destroy (constraint_any); + return 1; + +error_free_geom: + ped_geometry_destroy (geom); +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +#ifndef DISCOVER_ONLY +static int +loop_write (PedDisk* disk) +{ + char buf [512]; + + if (ped_disk_get_partition (disk, 1)) { + if (!ped_device_read (disk->dev, buf, 0, 1)) + return 0; + if (strncmp (buf, LOOP_SIGNATURE, strlen (LOOP_SIGNATURE)) != 0) + return 1; + memset (buf, 0, strlen (LOOP_SIGNATURE)); + return ped_device_write (disk->dev, buf, 0, 1); + } + + memset (buf, 0, 512); + strcpy (buf, LOOP_SIGNATURE); + + return ped_device_write (disk->dev, buf, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +loop_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + return NULL; + part->disk_specific = NULL; + return part; +} + +static PedPartition* +loop_partition_duplicate (const PedPartition* part) +{ + PedPartition* result; + + result = ped_partition_new (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + result->num = part->num; + return result; +} + +static void +loop_partition_destroy (PedPartition* part) +{ + ped_free (part); +} + +static int +loop_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + part->fs_type = fs_type; + return 1; +} + +static int +loop_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + return 0; +} + +static int +loop_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + return 0; +} + +static int +loop_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PedGeometry* new_geom; + + new_geom = ped_constraint_solve_nearest (constraint, &part->geom); + if (!new_geom) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the " + "partition.")); + return 0; + } + ped_geometry_set (&part->geom, new_geom->start, new_geom->length); + ped_geometry_destroy (new_geom); + return 1; +} + +static int +loop_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + return 0; +} + +static int +loop_partition_enumerate (PedPartition* part) +{ + part->num = 1; + return 1; +} + +static int +loop_alloc_metadata (PedDisk* disk) +{ + return 1; +} + +static int +loop_get_max_primary_partition_count (const PedDisk* disk) +{ + return 1; +} + +static PedDiskOps loop_disk_ops = { + probe: loop_probe, +#ifndef DISCOVER_ONLY + clobber: loop_clobber, +#else + clobber: NULL, +#endif + alloc: loop_alloc, + duplicate: loop_duplicate, + free: loop_free, + read: loop_read, +#ifndef DISCOVER_ONLY + write: loop_write, +#else + write: NULL, +#endif + + partition_new: loop_partition_new, + partition_duplicate: loop_partition_duplicate, + partition_destroy: loop_partition_destroy, + partition_set_system: loop_partition_set_system, + partition_set_flag: loop_partition_set_flag, + partition_get_flag: loop_partition_get_flag, + partition_is_flag_available: loop_partition_is_flag_available, + partition_set_name: NULL, + partition_get_name: NULL, + partition_align: loop_partition_align, + partition_enumerate: loop_partition_enumerate, + + alloc_metadata: loop_alloc_metadata, + get_max_primary_partition_count: + loop_get_max_primary_partition_count +}; + +static PedDiskType loop_disk_type = { + next: NULL, + name: "loop", + ops: &loop_disk_ops, + features: 0 +}; + +void +ped_disk_loop_init () +{ + ped_register_disk_type (&loop_disk_type); +} + +void +ped_disk_loop_done () +{ + ped_unregister_disk_type (&loop_disk_type); +} + diff --git a/libparted/labels/mac.c b/libparted/labels/mac.c new file mode 100644 index 0000000..d642e3c --- /dev/null +++ b/libparted/labels/mac.c @@ -0,0 +1,1616 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2002, 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <string.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* struct's hacked from Linux source: fs/partitions/mac.h + * I believe it was originally written by Paul Mackerras (from comments in + * Quik source) + * + * See also: + * http://developer.apple.com/documentation/mac/Devices/Devices-126.html + * http://developer.apple.com/documentation/mac/Devices/Devices-121.html + * http://devworld.apple.com/technotes/tn/tn1189.html + * + * Partition types: + * Apple_Bootstrap new-world (HFS) boot partition + * Apple_partition_map partition map (table) + * Apple_Driver device driver + * Apple_Driver43 SCSI Manager 4.3 device driver + * Apple_MFS original Macintosh File System + * Apple_HFS Hierarchical File System (and +) + * Apple_HFSX HFS+ with case sensitivity and more + * Apple_UNIX_SVR2 UNIX file system (UFS?) + * Apple_PRODOS ProDOS file system + * Apple_Free unused space + * Apple_Scratch empty + * Apple_Void padding for iso9660 + * Apple_Extra an unused partition map entry + * + * Quick explanation: + * ------------------ + * Terminology: + * + * Parted Apple + * ------ ----- + * device disk/device + * disk no equivalent. + * partition volume or partition + * sector block + * + * * All space must be accounted for, except block 0 (driver block) and + * block 1-X (the partition map: i.e. lots of MacRawPartitions) + * + * * It's really hard to grow/shrink the number of MacRawPartition + * entries in the partition map, because the first partition starts + * immediately after the partition map. When we can move the start of + * HFS and ext2 partitions, this problem will disappear ;-) + */ + +#define MAC_PARTITION_MAGIC_1 0x5453 /* old */ +#define MAC_PARTITION_MAGIC_2 0x504d +#define MAC_DISK_MAGIC 0x4552 + +#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */ + +typedef struct { + uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */ + uint16_t res1; + uint32_t map_count; /* # blocks in partition map */ + uint32_t start_block; /* absolute starting block # of partition */ + uint32_t block_count; /* number of blocks in partition */ + char name[32]; /* partition name */ + char type[32]; /* string type description */ + uint32_t data_start; /* rel block # of first data block */ + uint32_t data_count; /* number of data blocks */ + uint32_t status; /* partition status bits */ + uint32_t boot_start; + uint32_t boot_count; + uint32_t boot_load; + uint32_t boot_load2; + uint32_t boot_entry; + uint32_t boot_entry2; + uint32_t boot_cksum; + char processor[16]; /* Contains 680x0, x=0,2,3,4; or empty */ + uint32_t driver_sig; + char _padding[372]; +} __attribute__ ((packed)) MacRawPartition; + +/* Driver descriptor structure, in block 0 */ +typedef struct { + uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */ + uint16_t block_size; /* physical sector size */ + uint32_t block_count; /* size of device in blocks */ + uint16_t dev_type; /* reserved */ + uint16_t dev_id; /* reserved */ + uint32_t data; /* reserved */ + uint16_t driver_count; /* # of driver descriptor entries */ + uint8_t driverlist[488]; /* info about available drivers */ + uint16_t padding[3]; /* pad to 512 bytes */ +} __attribute__ ((packed)) MacRawDisk; + +typedef struct { + uint32_t block; /* startblock in MacRawDisk->block_size units */ + uint16_t size; /* size in 512 byte units */ + uint16_t type; /* operating system type (MacOS = 1) */ +} __attribute__ ((packed)) MacDeviceDriver; + +typedef struct { + char volume_name[33]; /* eg: "Games" */ + char system_name[33]; /* eg: "Apple_Unix_SVR2" */ + char processor_name[17]; + + int is_boot; + int is_driver; + int has_driver; + int is_root; + int is_swap; + int is_lvm; + int is_raid; + + PedSector data_region_length; + PedSector boot_region_length; + + uint32_t boot_base_address; + uint32_t boot_entry_address; + uint32_t boot_checksum; + + uint32_t status; + uint32_t driver_sig; +} MacPartitionData; + +typedef struct { + int ghost_size; /* sectors per "driver" block */ + int part_map_entry_count; /* # entries (incl. ghost) */ + int part_map_entry_num; /* partition map location */ + + int active_part_entry_count; /* # real partitions */ + int free_part_entry_count; /* # free space */ + int last_part_entry_num; /* last entry number */ + + uint16_t block_size; /* physical sector size */ + uint16_t driver_count; + MacDeviceDriver driverlist[1 + 60]; /* 488 bytes */ +} MacDiskData; + +static PedDiskType mac_disk_type; + +static int +_check_signature (MacRawDisk* raw_disk) +{ + if (PED_BE16_TO_CPU (raw_disk->signature) != MAC_DISK_MAGIC) { +#ifdef DISCOVER_ONLY + return 0; +#else + return ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid signature %x for Mac disk labels."), + (int) PED_BE16_TO_CPU (raw_disk->signature)) + == PED_EXCEPTION_IGNORE; +#endif + } + + return 1; +} + +static int +_rawpart_check_signature (MacRawPartition* raw_part) +{ + int sig = (int) PED_BE16_TO_CPU (raw_part->signature); + return sig == MAC_PARTITION_MAGIC_1 || sig == MAC_PARTITION_MAGIC_2; +} + +static int +mac_probe (const PedDevice * dev) +{ + MacRawDisk buf; + + PED_ASSERT (dev != NULL, return 0); + + if (dev->sector_size != 512) + return 0; + + if (!ped_device_read (dev, &buf, 0, 1)) + return 0; + + return _check_signature (&buf); +} + +static int +_disk_add_part_map_entry (PedDisk* disk, int warn) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* new_part; + MacPartitionData* mac_part_data; + PedSector part_map_size; + PedConstraint* constraint_any = ped_constraint_any (disk->dev); + +#ifndef DISCOVER_ONLY + if (warn && ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL, + _("Partition map has no partition map entry!")) + != PED_EXCEPTION_FIX) + goto error; +#endif /* !DISCOVER_ONLY */ + + part_map_size + = ped_round_up_to (mac_disk_data->last_part_entry_num, 64); + if (part_map_size == 0) + part_map_size = 64; + + new_part = ped_partition_new (disk, 0, NULL, 1, part_map_size - 1); + if (!new_part) + goto error; + + mac_part_data = new_part->disk_specific; + strcpy (mac_part_data->volume_name, "Apple"); + strcpy (mac_part_data->system_name, "Apple_partition_map"); + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) + goto error_destroy_new_part; + + mac_disk_data->part_map_entry_num = new_part->num; + mac_disk_data->part_map_entry_count + = new_part->geom.end - mac_disk_data->ghost_size; + ped_constraint_destroy (constraint_any); + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +PedDisk* +mac_alloc (const PedDevice* dev) +{ + PedDisk* disk; + MacDiskData* mac_disk_data; + + PED_ASSERT (dev != NULL, return NULL); + +#ifndef DISCOVER_ONLY + if (dev->length < 256) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s is too small for a Mac disk label!"), + dev->path); + goto error; + } +#endif + + disk = _ped_disk_alloc (dev, &mac_disk_type); + if (!disk) + goto error; + + mac_disk_data = (MacDiskData*) ped_malloc (sizeof (MacDiskData)); + if (!mac_disk_data) + goto error_free_disk; + disk->disk_specific = mac_disk_data; + mac_disk_data->ghost_size = disk->dev->sector_size / 512; + mac_disk_data->active_part_entry_count = 0; + mac_disk_data->free_part_entry_count = 1; + mac_disk_data->last_part_entry_num = 1; + mac_disk_data->block_size = 0; + mac_disk_data->driver_count = 0; + memset(&mac_disk_data->driverlist[0], 0, sizeof(mac_disk_data->driverlist)); + + if (!_disk_add_part_map_entry (disk, 0)) + goto error_free_disk; + return disk; + +error_free_disk: + _ped_disk_free (disk); +error: + return NULL; +} + +static PedDisk* +mac_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + MacDiskData* new_mac_data; + MacDiskData* old_mac_data = (MacDiskData*) disk->disk_specific; + PedPartition* partition_map; + + new_disk = ped_disk_new_fresh (disk->dev, &mac_disk_type); + if (!new_disk) + goto error; + + new_mac_data = (MacDiskData*) new_disk->disk_specific; + + /* remove the partition map partition - it will be duplicated + * later. + */ + partition_map = ped_disk_get_partition_by_sector (new_disk, 1); + PED_ASSERT (partition_map != NULL, return 0); + ped_disk_remove_partition (new_disk, partition_map); + + /* ugly, but C is ugly :p */ + memcpy (new_mac_data, old_mac_data, sizeof (MacDiskData)); + return new_disk; + +error_free_new_disk: + _ped_disk_free (new_disk); +error: + return NULL; +} + +static void +mac_free (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + _ped_disk_free (disk); + ped_free (mac_disk_data); +} + +#ifndef DISCOVER_ONLY +static int +_clobber_part_map (PedDevice* dev) +{ + MacRawPartition raw_part; + PedSector sector; + + for (sector=1; 1; sector++) { + if (!ped_device_read (dev, &raw_part, sector, 1)) + return 0; + if (!_rawpart_check_signature (&raw_part)) + return 1; + memset (&raw_part, 0, 512); + if (!ped_device_write (dev, &raw_part, sector, 1)) + return 0; + } +} + +static int +mac_clobber (PedDevice* dev) +{ + MacRawDisk raw_disk; + + if (!ped_device_read (dev, &raw_disk, 0, 1)) + return 0; + if (!_check_signature (&raw_disk)) + return 0; + memset (&raw_disk, 0, 512); + if (!ped_device_write (dev, &raw_disk, 0, 1)) + return 0; + + return _clobber_part_map (dev); +} +#endif /* !DISCOVER_ONLY */ + +static int +_rawpart_cmp_type (MacRawPartition* raw_part, char* type) +{ + return strncasecmp (raw_part->type, type, 32) == 0; +} + +static int +_rawpart_cmp_name (MacRawPartition* raw_part, char* name) +{ + return strncasecmp (raw_part->name, name, 32) == 0; +} + +static int +_rawpart_is_partition_map (MacRawPartition* raw_part) +{ + return _rawpart_cmp_type (raw_part, "Apple_partition_map"); +} + +static int +strncasestr (const char* haystack, const char* needle, int n) +{ + int needle_size = strlen (needle); + int i; + + for (i = 0; haystack[i] && i < n - needle_size; i++) { + if (strncasecmp (haystack + i, needle, needle_size) == 0) + return 1; + } + + return 0; +} + +static int +_rawpart_is_boot (MacRawPartition* raw_part) +{ + return !strcasecmp (raw_part->type, "Apple_Bootstrap"); +} + +static int +_rawpart_is_driver (MacRawPartition* raw_part) +{ + if (strncmp (raw_part->type, "Apple_", 6) != 0) + return 0; + if (!strncasestr (raw_part->type, "driver", 32)) + return 0; + return 1; +} + +static int +_rawpart_has_driver (MacRawPartition* raw_part, MacDiskData* mac_disk_data) +{ + MacDeviceDriver *driverlist; + uint16_t i, bsz; + uint32_t driver_bs, driver_be, part_be; + + driverlist = &mac_disk_data->driverlist[0]; + bsz = mac_disk_data->block_size / 512; + for (i = 0; i < mac_disk_data->driver_count; i++) { + driver_bs = driverlist->block * bsz; + driver_be = driver_bs + driverlist->size; + part_be = raw_part->start_block + raw_part->block_count; + if (driver_bs >= raw_part->start_block && driver_be <= part_be) + return 1; + driverlist++; + } + return 0; +} + +static int +_rawpart_is_root (MacRawPartition* raw_part) +{ + if (!_rawpart_cmp_type (raw_part, "Apple_UNIX_SVR2")) + return 0; + if (strcmp (raw_part->name, "root") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_swap (MacRawPartition* raw_part) +{ + if (!_rawpart_cmp_type (raw_part, "Apple_UNIX_SVR2")) + return 0; + if (strcmp (raw_part->name, "swap") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_lvm (MacRawPartition* raw_part) +{ + if (strcmp (raw_part->type, "Linux_LVM") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_raid (MacRawPartition* raw_part) +{ + if (strcmp (raw_part->type, "Linux_RAID") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_void (MacRawPartition* raw_part) +{ + return _rawpart_cmp_type (raw_part, "Apple_Void"); +} + +/* returns 1 if the raw_part represents a partition that is "unused space", or + * doesn't represent a partition at all. NOTE: some people make Apple_Free + * partitions with MacOS, because they can't select another type. So, if the + * name is anything other than "Extra" or "", it is treated as a "real" + * partition. + */ +static int +_rawpart_is_active (MacRawPartition* raw_part) +{ + if (_rawpart_cmp_type (raw_part, "Apple_Free") + && (_rawpart_cmp_name (raw_part, "Extra") + || _rawpart_cmp_name (raw_part, ""))) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Void")) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Scratch")) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Extra")) + return 0; + + return 1; +} + +static PedPartition* +_rawpart_analyse (MacRawPartition* raw_part, PedDisk* disk, int num) +{ + MacDiskData* mac_disk_data; + PedPartition* part; + MacPartitionData* mac_part_data; + PedSector block_size; + PedSector start, length; + + if (!_rawpart_check_signature (raw_part)) { +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Partition %d has an invalid signature %x."), + num, + (int) PED_BE16_TO_CPU (raw_part->signature)) + != PED_EXCEPTION_IGNORE) +#endif + goto error; + } + + mac_disk_data = (MacDiskData*) disk->disk_specific; + block_size = disk->dev->sector_size / 512; + + start = PED_BE32_TO_CPU (raw_part->start_block) * block_size; + length = PED_BE32_TO_CPU (raw_part->block_count) * block_size; + if (length == 0) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Partition %d has an invalid length of 0 bytes!"), + num); +#endif + return NULL; + } + part = ped_partition_new (disk, 0, NULL, start, start + length - 1); + if (!part) + goto error; + + mac_part_data = part->disk_specific; + + strncpy (mac_part_data->volume_name, raw_part->name, 32); + strncpy (mac_part_data->system_name, raw_part->type, 32); + strncpy (mac_part_data->processor_name, raw_part->processor, 16); + + mac_part_data->is_boot = _rawpart_is_boot (raw_part); + mac_part_data->is_driver = _rawpart_is_driver (raw_part); + if (mac_part_data->is_driver) + mac_part_data->has_driver = _rawpart_has_driver(raw_part, mac_disk_data); + mac_part_data->is_root = _rawpart_is_root (raw_part); + mac_part_data->is_swap = _rawpart_is_swap (raw_part); + mac_part_data->is_lvm = _rawpart_is_lvm (raw_part); + mac_part_data->is_raid = _rawpart_is_raid (raw_part); + + /* "data" region */ +#ifndef DISCOVER_ONLY + if (raw_part->data_start) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The data region doesn't start at the start " + "of the partition.")); + goto error_destroy_part; + } +#endif /* !DISCOVER_ONLY */ + mac_part_data->data_region_length + = PED_BE32_TO_CPU (raw_part->data_count) * block_size; + + /* boot region - we have no idea what this is for, but Mac OSX + * seems to put garbage here, and doesn't pay any attention to + * it afterwards. [clausen, dan burcaw] + */ +#if 0 + if (raw_part->boot_start) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The boot region doesn't start at the start " + "of the partition.")); + goto error_destroy_part; + } +#endif + mac_part_data->boot_region_length + = PED_BE32_TO_CPU (raw_part->boot_count) * block_size; + +#ifndef DISCOVER_ONLY + if (mac_part_data->has_driver) { + if (mac_part_data->boot_region_length < part->geom.length) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The partition's boot region doesn't occupy " + "the entire partition.")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_part; + } + } else { + if (mac_part_data->data_region_length < part->geom.length) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The partition's data region doesn't occupy " + "the entire partition.")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_part; + } + } +#endif /* !DISCOVER_ONLY */ + + mac_part_data->boot_base_address + = PED_BE32_TO_CPU (raw_part->boot_load); + mac_part_data->boot_entry_address + = PED_BE32_TO_CPU (raw_part->boot_entry); + mac_part_data->boot_checksum + = PED_BE32_TO_CPU (raw_part->boot_cksum); + + mac_part_data->status = PED_BE32_TO_CPU (raw_part->status); + mac_part_data->driver_sig = PED_BE32_TO_CPU (raw_part->driver_sig); + + return part; + +error_destroy_part: + ped_partition_destroy (part); +error: + return NULL; +} + +/* looks at the partition map size field in a mac raw partition, and calculates + * what the size of the partition map should be, from it + */ +static int +_rawpart_get_partmap_size (MacRawPartition* raw_part, PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedSector sector_size = disk->dev->sector_size / 512; + PedSector part_map_start; + PedSector part_map_end; + + part_map_start = mac_disk_data->ghost_size; + part_map_end = sector_size * PED_BE32_TO_CPU (raw_part->map_count); + + return part_map_end - part_map_start + 1; +} + +static int +_disk_analyse_block_size (PedDisk* disk, MacRawDisk* raw_disk) +{ + PedSector block_size; + + if (PED_BE16_TO_CPU (raw_disk->block_size) % 512) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Weird block size on device descriptor: %d bytes is " + "not divisible by 512."), + (int) PED_BE16_TO_CPU (raw_disk->block_size)); +#endif + goto error; + } + + block_size = PED_BE16_TO_CPU (raw_disk->block_size) / 512; + if (block_size != disk->dev->sector_size / 512) { +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The driver descriptor says the physical block size " + "is %d bytes, but Linux says it is %d bytes."), + (int) block_size * 512, + (int) disk->dev->sector_size) + != PED_EXCEPTION_IGNORE) + goto error; +#endif + disk->dev->sector_size = block_size * 512; + } + + return 1; + +error: + return 0; +} + +/* Tries to figure out the block size used by the drivers, for the ghost + * partitioning scheme. Ghost partitioning works like this: the OpenFirmware + * (OF) sees 512 byte blocks, but some drivers use 2048 byte blocks (and, + * perhaps, some other number?). To remain compatible, the partition map + * only has "real" partition map entries on ghost-aligned block numbers (and + * the others are padded with Apple_Void partitions). This function tries + * to figure out what the "ghost-aligned" size is... (which, believe-it-or-not, + * doesn't always equal 2048!!!) + */ +static int +_disk_analyse_ghost_size (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + MacRawPartition raw_part; + int i; + + for (i = 1; i < 64; i *= 2) { + if (!ped_device_read (disk->dev, &raw_part, i, 1)) + return 0; + if (_rawpart_check_signature (&raw_part) + && !_rawpart_is_void (&raw_part)) { + mac_disk_data->ghost_size = i; + PED_ASSERT (i <= disk->dev->sector_size / 512, + return 0); + return 1; + } + } + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No valid partition map found.")); +#endif + return 0; +} + +static int +mac_read (PedDisk* disk) +{ + MacRawDisk raw_disk; + MacRawPartition raw_part; + MacDiskData* mac_disk_data; + PedPartition* part; + int num; + PedSector ghost_size; + PedConstraint* constraint_exact; + int last_part_entry_num = 0; + + PED_ASSERT (disk != NULL, return 0); + + mac_disk_data = disk->disk_specific; + mac_disk_data->part_map_entry_num = 0; /* 0 == none */ + + if (!ped_device_read (disk->dev, &raw_disk, 0, 1)) + goto error; + if (!_check_signature (&raw_disk)) + goto error; + + if (!_disk_analyse_block_size (disk, &raw_disk)) + goto error; + if (!_disk_analyse_ghost_size (disk)) + goto error; + ghost_size = mac_disk_data->ghost_size; + + if (!ped_disk_delete_all (disk)) + goto error; + + if (raw_disk.driver_count && raw_disk.driver_count < 62) { + memcpy(&mac_disk_data->driverlist[0], &raw_disk.driverlist[0], + sizeof(mac_disk_data->driverlist)); + mac_disk_data->driver_count = raw_disk.driver_count; + mac_disk_data->block_size = raw_disk.block_size; + } + + for (num=1; num==1 || num <= last_part_entry_num; num++) { + if (!ped_device_read (disk->dev, &raw_part, + num * ghost_size, 1)) + goto error_delete_all; + + if (!_rawpart_check_signature (&raw_part)) + continue; + + if (num == 1) + last_part_entry_num + = _rawpart_get_partmap_size (&raw_part, disk); + if (_rawpart_get_partmap_size (&raw_part, disk) + != last_part_entry_num) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Conflicting partition map entry sizes! " + "Entry 1 says it is %d, but entry %d says " + "it is %d!"), + last_part_entry_num, + _rawpart_get_partmap_size (&raw_part, disk)) + != PED_EXCEPTION_IGNORE) + goto error_delete_all; + } + + if (!_rawpart_is_active (&raw_part)) + continue; + + part = _rawpart_analyse (&raw_part, disk, num); + if (!part) + goto error_delete_all; + part->num = num; + part->fs_type = ped_file_system_probe (&part->geom); + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) + goto error_delete_all; + ped_constraint_destroy (constraint_exact); + + if (_rawpart_is_partition_map (&raw_part)) { + if (mac_disk_data->part_map_entry_num + && ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Weird! There are 2 partitions " + "map entries!")) + != PED_EXCEPTION_IGNORE) + goto error_delete_all; + + mac_disk_data->part_map_entry_num = num; + mac_disk_data->part_map_entry_count + = part->geom.end - ghost_size + 1; + } + } + + if (!mac_disk_data->part_map_entry_num) { + if (!_disk_add_part_map_entry (disk, 1)) + goto error_delete_all; + ped_disk_commit_to_dev (disk); + } + return 1; + +error_delete_all: + ped_disk_delete_all (disk); +error: + return 0; +} + +#ifndef DISCOVER_ONLY +/* The Ghost partition: is a blank entry, used to pad out each block (where + * there physical block size > 512 bytes). This is because OpenFirmware uses + * 512 byte blocks, but device drivers Think Different TM, with a different + * lbock size, so we need to do this to avoid a clash (!) + */ +static int +_pad_raw_part (PedDisk* disk, int num, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + MacRawPartition ghost_entry; + int i; + + memset (&ghost_entry, 0, sizeof (ghost_entry)); + ghost_entry.signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + strcpy (ghost_entry.type, "Apple_Void"); + ghost_entry.map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + + for (i=0; i < mac_disk_data->ghost_size - 1; i++) + memcpy (&part_map [i + (num - 1) * mac_disk_data->ghost_size], + &ghost_entry, sizeof (MacRawPartition)); + + return 1; +} + +static void +_update_driver_count (MacRawPartition* part_map_entry, + MacDiskData *mac_driverdata, const MacDiskData* mac_disk_data) +{ + uint16_t i, count_orig, count_cur, bsz; + uint32_t driver_bs, driver_be, part_be; + + bsz = mac_disk_data->block_size / 512; + count_cur = mac_driverdata->driver_count; + count_orig = mac_disk_data->driver_count; + for (i = 0; i < count_orig; i++) { + driver_bs = mac_disk_data->driverlist[i].block * bsz; + driver_be = driver_bs + mac_disk_data->driverlist[i].size; + part_be = part_map_entry->start_block + part_map_entry->block_count; + if (driver_bs >= part_map_entry->start_block + && driver_be <= part_be) { + mac_driverdata->driverlist[count_cur].block + = mac_disk_data->driverlist[i].block; + mac_driverdata->driverlist[count_cur].size + = mac_disk_data->driverlist[i].size; + mac_driverdata->driverlist[count_cur].type + = mac_disk_data->driverlist[i].type; + mac_driverdata->driver_count++; + break; + } + } +} + +static int +_generate_raw_part (PedDisk* disk, PedPartition* part, + MacRawPartition* part_map, MacDiskData *mac_driverdata) +{ + MacDiskData* mac_disk_data; + MacPartitionData* mac_part_data; + MacRawPartition* part_map_entry; + PedSector block_size = disk->dev->sector_size / 512; + + PED_ASSERT (part->num > 0, goto error); + + mac_disk_data = disk->disk_specific; + mac_part_data = part->disk_specific; + + part_map_entry = &part_map [part->num * mac_disk_data->ghost_size - 1]; + + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + part_map_entry->start_block + = PED_CPU_TO_BE32 (part->geom.start / block_size); + part_map_entry->block_count + = PED_CPU_TO_BE32 (part->geom.length / block_size); + strcpy (part_map_entry->name, mac_part_data->volume_name); + strcpy (part_map_entry->type, mac_part_data->system_name); + + if (mac_part_data->is_driver) { + mac_part_data->boot_region_length = part->geom.length; + if (mac_part_data->has_driver) + _update_driver_count(part_map_entry, mac_driverdata, + mac_disk_data); + } else + mac_part_data->data_region_length = part->geom.length; + part_map_entry->data_count = PED_CPU_TO_BE32 ( + mac_part_data->data_region_length / block_size); + part_map_entry->boot_count = PED_CPU_TO_BE32 ( + mac_part_data->boot_region_length / block_size); + part_map_entry->status = PED_CPU_TO_BE32 (mac_part_data->status); + part_map_entry->driver_sig + = PED_CPU_TO_BE32 (mac_part_data->driver_sig); + + part_map_entry->boot_load = + PED_CPU_TO_BE32 (mac_part_data->boot_base_address); + part_map_entry->boot_entry = + PED_CPU_TO_BE32 (mac_part_data->boot_entry_address); + part_map_entry->boot_cksum = + PED_CPU_TO_BE32 (mac_part_data->boot_checksum); + + strncpy (part_map_entry->processor, mac_part_data->processor_name, 16); + + if (!_pad_raw_part (disk, part->num, part_map)) + goto error; + + return 1; + +error: + return 0; +} + +static int +_generate_raw_freespace_part (PedDisk* disk, PedGeometry* geom, int num, + MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + MacRawPartition* part_map_entry; + PedSector block_size = disk->dev->sector_size / 512; + + PED_ASSERT (num > 0, goto error); + + part_map_entry = &part_map [num * mac_disk_data->ghost_size - 1]; + + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + part_map_entry->start_block + = PED_CPU_TO_BE32 (geom->start / block_size); + part_map_entry->block_count + = PED_CPU_TO_BE32 (geom->length / block_size); + strcpy (part_map_entry->name, "Extra"); + strcpy (part_map_entry->type, "Apple_Free"); + + part_map_entry->data_count = PED_CPU_TO_BE32 (geom->length); + part_map_entry->status = 0; + part_map_entry->driver_sig = 0; + + if (!_pad_raw_part (disk, num, part_map)) + goto error; + + return 1; + +error: + return 0; +} + +static int +_generate_empty_part (PedDisk* disk, int num, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + MacRawPartition* part_map_entry; + + PED_ASSERT (num > 0, return 0); + + part_map_entry = &part_map [num * mac_disk_data->ghost_size - 1]; + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + strcpy (part_map_entry->type, "Apple_Void"); + + return _pad_raw_part (disk, num, part_map); +} + +/* returns the first empty entry in the partition map */ +static int +_get_first_empty_part_entry (PedDisk* disk, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + int i; + + for (i=1; i <= mac_disk_data->last_part_entry_num; i++) { + if (!part_map[i * mac_disk_data->ghost_size - 1].signature) + return i; + } + + return 0; +} + +static int +write_block_zero (PedDisk* disk, MacDiskData* mac_driverdata) +{ + PedDevice* dev = disk->dev; + MacRawDisk raw_disk; + + if (!ped_device_read (dev, &raw_disk, 0, 1)) + return 0; + + raw_disk.signature = PED_CPU_TO_BE16 (MAC_DISK_MAGIC); + raw_disk.block_size = PED_CPU_TO_BE16 (dev->sector_size); + raw_disk.block_count + = PED_CPU_TO_BE32 (dev->length / (dev->sector_size / 512)); + + raw_disk.driver_count = mac_driverdata->driver_count; + memcpy(&raw_disk.driverlist[0], &mac_driverdata->driverlist[0], + sizeof(raw_disk.driverlist)); + + return ped_device_write (dev, &raw_disk, 0, 1); +} + +static int +mac_write (PedDisk* disk) +{ + MacRawPartition* part_map; + MacDiskData* mac_disk_data; + MacDiskData* mac_driverdata; /* updated driver list */ + PedPartition* part; + int num; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->disk_specific != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + PED_ASSERT (!disk->update_mode, return 0); + + mac_disk_data = disk->disk_specific; + + if (!ped_disk_get_partition (disk, mac_disk_data->part_map_entry_num)) { + if (!_disk_add_part_map_entry (disk, 1)) + goto error; + } + + mac_driverdata = ped_malloc(sizeof(MacDiskData)); + if (!mac_driverdata) + goto error; + memset (mac_driverdata, 0, sizeof(MacDiskData)); + + part_map = (MacRawPartition*) + ped_malloc (mac_disk_data->part_map_entry_count * 512); + if (!part_map) + goto error_free_driverdata; + memset (part_map, 0, mac_disk_data->part_map_entry_count * 512); + +/* write (to memory) the "real" partitions */ + for (part = ped_disk_next_partition (disk, NULL); part; + part = ped_disk_next_partition (disk, part)) { + if (!ped_partition_is_active (part)) + continue; + if (!_generate_raw_part (disk, part, part_map, mac_driverdata)) + goto error_free_part_map; + } + +/* write the "free space" partitions */ + for (part = ped_disk_next_partition (disk, NULL); part; + part = ped_disk_next_partition (disk, part)) { + if (part->type != PED_PARTITION_FREESPACE) + continue; + num = _get_first_empty_part_entry (disk, part_map); + if (!_generate_raw_freespace_part (disk, &part->geom, num, + part_map)) + goto error_free_part_map; + } + +/* write the "void" (empty) partitions */ + for (num = _get_first_empty_part_entry (disk, part_map); num; + num = _get_first_empty_part_entry (disk, part_map)) + _generate_empty_part (disk, num, part_map); + +/* write to disk */ + if (!ped_device_write (disk->dev, part_map, 1, + mac_disk_data->part_map_entry_count)) + goto error_free_part_map; + ped_free (part_map); + return write_block_zero (disk, mac_driverdata); + +error_free_part_map: + ped_free (part_map); +error_free_driverdata: + ped_free (mac_driverdata); +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +mac_partition_new ( + const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, PedSector start, PedSector end) +{ + PedPartition* part; + MacPartitionData* mac_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = mac_data = ped_malloc (sizeof (MacPartitionData)); + if (!mac_data) + goto error_free_part; + + memset (mac_data, 0, sizeof (MacPartitionData)); + + mac_data->data_region_length = 0; + mac_data->boot_region_length = 0; + mac_data->is_driver = 0; + mac_data->has_driver = 0; + mac_data->is_boot = 0; + mac_data->is_root = 0; + mac_data->is_swap = 0; + mac_data->is_lvm = 0; + mac_data->is_raid = 0; + + strcpy (mac_data->volume_name, "untitled"); + } else { + part->disk_specific = NULL; + } + return part; + +error_free_mac_data: + ped_free (mac_data); +error_free_part: + ped_free (part); +error: + return 0; +} + +static PedPartition* +mac_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + MacPartitionData* new_mac_data; + MacPartitionData* old_mac_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_mac_data = (MacPartitionData*) part->disk_specific; + new_mac_data = (MacPartitionData*) new_part->disk_specific; + + /* ugly, but C is ugly :p */ + memcpy (new_mac_data, old_mac_data, sizeof (MacPartitionData)); + return new_part; +} + +static void +mac_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + + if (ped_partition_is_active (part)) + ped_free (part->disk_specific); + ped_free (part); +} + +static int +mac_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + MacPartitionData* mac_data = part->disk_specific; + + part->fs_type = fs_type; + + if (fs_type && !strcmp (fs_type->name, "linux-swap")) + ped_partition_set_flag (part, PED_PARTITION_SWAP, 1); + + if (mac_data->is_boot) { + strcpy (mac_data->system_name, "Apple_Bootstrap"); + mac_data->status = 0x33; + return 1; + } + + if (fs_type && (!strcmp (fs_type->name, "hfs") + || !strcmp (fs_type->name, "hfs+"))) { + strcpy (mac_data->system_name, "Apple_HFS"); + mac_data->status |= 0x7f; + } else if (fs_type && !strcmp (fs_type->name, "hfsx")) { + strcpy (mac_data->system_name, "Apple_HFSX"); + mac_data->status |= 0x7f; + } else { + strcpy (mac_data->system_name, "Apple_UNIX_SVR2"); + mac_data->status = 0x33; + } + + return 1; +} + +static int +mac_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + PedFileSystemType* hfs = ped_file_system_type_get ("hfs"); + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + mac_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + mac_data->is_boot = state; + + if (part->fs_type) + return mac_partition_set_system (part, part->fs_type); + + if (state) { + strcpy (mac_data->system_name, "Apple_Bootstrap"); + mac_data->status = 0x33; + } + return 1; + + case PED_PARTITION_ROOT: + if (state) { + strcpy (mac_data->volume_name, "root"); + mac_data->is_swap = 0; + } else { + if (mac_data->is_root) + strcpy (mac_data->volume_name, "untitled"); + } + mac_data->is_root = state; + return 1; + + case PED_PARTITION_SWAP: + if (state) { + strcpy (mac_data->volume_name, "swap"); + mac_data->is_root = 0; + } else { + if (mac_data->is_swap) + strcpy (mac_data->volume_name, "untitled"); + } + mac_data->is_swap = state; + return 1; + + case PED_PARTITION_LVM: + mac_data->is_lvm = state; + if (state) + strcpy (mac_data->system_name, "Linux_LVM"); + else + mac_partition_set_system (part, part->fs_type); + return 1; + + case PED_PARTITION_RAID: + mac_data->is_raid = state; + if (state) + strcpy (mac_data->system_name, "Linux_RAID"); + else + mac_partition_set_system (part, part->fs_type); + return 1; + + default: + return 0; + } +} + +static int +mac_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + mac_data = part->disk_specific; + switch (flag) { + case PED_PARTITION_BOOT: + return mac_data->is_boot; + + case PED_PARTITION_ROOT: + return mac_data->is_root; + + case PED_PARTITION_SWAP: + return mac_data->is_swap; + + case PED_PARTITION_LVM: + return mac_data->is_lvm; + + case PED_PARTITION_RAID: + return mac_data->is_raid; + + default: + return 0; + } +} + +static int +mac_partition_is_flag_available ( + const PedPartition* part, PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + case PED_PARTITION_ROOT: + case PED_PARTITION_SWAP: + case PED_PARTITION_LVM: + case PED_PARTITION_RAID: + return 1; + + default: + return 0; + } +} + +static void +mac_partition_set_name (PedPartition* part, const char* name) +{ + MacPartitionData* mac_data; + int i; + + PED_ASSERT (part != NULL, return); + PED_ASSERT (part->disk_specific != NULL, return); + mac_data = part->disk_specific; + +#ifndef DISCOVER_ONLY + if (mac_data->is_root || mac_data->is_swap) { + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Changing the name of a root or swap partition " + "will prevent Linux from recognising it as such.")) + != PED_EXCEPTION_IGNORE) + return; + mac_data->is_root = mac_data->is_swap = 0; + } +#endif + + strncpy (mac_data->volume_name, name, 32); + mac_data->volume_name [32] = 0; + for (i = strlen (mac_data->volume_name) - 1; + mac_data->volume_name[i] == ' '; i--) + mac_data->volume_name [i] = 0; +} + +static const char* +mac_partition_get_name (const PedPartition* part) +{ + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL, return NULL); + PED_ASSERT (part->disk_specific != NULL, return NULL); + mac_data = part->disk_specific; + + return mac_data->volume_name; +} + +static PedConstraint* +_primary_constraint (PedDisk* disk) +{ + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + PedSector sector_size; + + sector_size = disk->dev->sector_size / 512; + + if (!ped_alignment_init (&start_align, 0, sector_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, sector_size)) + return NULL; + if (!ped_geometry_init (&max_geom, disk->dev, 1, disk->dev->length - 1)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, disk->dev->length); +} + +static int +mac_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL, return 0); + + if (_ped_partition_attempt_align (part, constraint, + _primary_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +mac_partition_enumerate (PedPartition* part) +{ + PedDisk* disk; + MacDiskData* mac_disk_data; + int i; + int max_part_count; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + + disk = part->disk; + mac_disk_data = (MacDiskData*) disk->disk_specific; + + max_part_count = ped_disk_get_max_primary_partition_count (disk); + + if (part->num > 0 && part->num <= mac_disk_data->part_map_entry_count) + return 1; + + for (i = 1; i <= max_part_count; i++) { + if (!ped_disk_get_partition (disk, i)) { + part->num = i; + return 1; + } + } + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't add another partition -- the partition map is too " + "small!")); +#endif + + return 0; +} + +static int +_disk_count_partitions (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* part = NULL; + PedPartition* last = NULL; + + PED_ASSERT (disk->update_mode, return 0); + + mac_disk_data->active_part_entry_count = 0; + mac_disk_data->free_part_entry_count = 0; + mac_disk_data->last_part_entry_num = 0; + + /* subtle: we only care about free space after the partition map. + * the partition map is an "active" partition, BTW... */ + for (part = ped_disk_next_partition (disk, part); part; + part = ped_disk_next_partition (disk, part)) { + if (!ped_partition_is_active (part)) + continue; + + mac_disk_data->active_part_entry_count++; + if (last && last->geom.end + 1 < part->geom.start) + mac_disk_data->free_part_entry_count++; + mac_disk_data->last_part_entry_num + = PED_MAX (mac_disk_data->last_part_entry_num, + part->num); + + last = part; + } + + if (last && last->geom.end < disk->dev->length - 1) + mac_disk_data->free_part_entry_count++; + + mac_disk_data->last_part_entry_num + = PED_MAX (mac_disk_data->last_part_entry_num, + mac_disk_data->active_part_entry_count + + mac_disk_data->free_part_entry_count); + return 1; +} + +static int +add_metadata_part (PedDisk* disk, PedSector start, PedSector end) +{ + PedPartition* new_part; + PedConstraint* constraint_any = ped_constraint_any (disk->dev); + + PED_ASSERT (disk != NULL, return 0); + + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + start, end); + if (!new_part) + goto error; + if (!ped_disk_add_partition (disk, new_part, constraint_any)) + goto error_destroy_new_part; + + ped_constraint_destroy (constraint_any); + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +mac_alloc_metadata (PedDisk* disk) +{ + MacDiskData* mac_disk_data; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->disk_specific != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + mac_disk_data = disk->disk_specific; + + if (!add_metadata_part (disk, 0, disk->dev->sector_size / 512 - 1)) + return 0; + + /* hack: this seems to be a good place, to update the partition map + * entry count, since mac_alloc_metadata() gets called during + * _disk_pop_update_mode() + */ + return _disk_count_partitions (disk); +} + +static int +mac_get_max_primary_partition_count (const PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* part_map_partition; + + part_map_partition = ped_disk_get_partition (disk, + mac_disk_data->part_map_entry_num); + + /* HACK: if we haven't found the partition map partition (yet), + * we return this. + */ + if (!part_map_partition) { + mac_disk_data->part_map_entry_num = 0; + return 65536; + } + + /* HACK: since Mac labels need an entry for free-space regions, we + * must allow half plus 1 entries for free-space partitions. I hate + * this, but things get REALLY complicated, otherwise. + * (I'm prepared to complicate things later, but I want to get + * everything working, first) + */ + return mac_disk_data->part_map_entry_count / mac_disk_data->ghost_size + - mac_disk_data->free_part_entry_count + 1; +} + +static PedDiskOps mac_disk_ops = { + probe: mac_probe, +#ifndef DISCOVER_ONLY + clobber: mac_clobber, +#else + clobber: NULL, +#endif + alloc: mac_alloc, + duplicate: mac_duplicate, + free: mac_free, + read: mac_read, +#ifndef DISCOVER_ONLY + write: mac_write, +#else + write: NULL, +#endif + + partition_new: mac_partition_new, + partition_duplicate: mac_partition_duplicate, + partition_destroy: mac_partition_destroy, + partition_set_system: mac_partition_set_system, + partition_set_flag: mac_partition_set_flag, + partition_get_flag: mac_partition_get_flag, + partition_is_flag_available: mac_partition_is_flag_available, + partition_set_name: mac_partition_set_name, + partition_get_name: mac_partition_get_name, + partition_align: mac_partition_align, + partition_enumerate: mac_partition_enumerate, + + alloc_metadata: mac_alloc_metadata, + get_max_primary_partition_count: + mac_get_max_primary_partition_count +}; + +static PedDiskType mac_disk_type = { + next: NULL, + name: "mac", + ops: &mac_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME +}; + +void +ped_disk_mac_init () +{ + PED_ASSERT (sizeof (MacRawPartition) == 512, return); + PED_ASSERT (sizeof (MacRawDisk) == 512, return); + + ped_register_disk_type (&mac_disk_type); +} + +void +ped_disk_mac_done () +{ + ped_unregister_disk_type (&mac_disk_type); +} + diff --git a/libparted/labels/pc98.c b/libparted/labels/pc98.c new file mode 100644 index 0000000..d9a54db --- /dev/null +++ b/libparted/labels/pc98.c @@ -0,0 +1,892 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <string.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* hacked from Linux/98 source: fs/partitions/nec98.h + * + * See also: + * http://people.FreeBSD.org/~kato/pc98.html + * http://www.kmc.kyoto-u.ac.jp/proj/linux98/index-english.html + * + * Partition types: + * + * id0(mid): + * bit 7: 1=bootable, 0=not bootable + * # Linux uses this flag to make a distinction between ext2 and swap. + * bit 6--0: + * 00H : N88-BASIC(data)?, PC-UX(data)? + * 04H : PC-UX(data) + * 06H : N88-BASIC + * 10H : N88-BASIC + * 14H : *BSD, PC-UX + * 20H : DOS(data), Windows95/98/NT, Linux + * 21H..2FH : DOS(system#1 .. system#15) + * 40H : Minix + * + * id1(sid): + * bit 7: 1=active, 0=sleep(hidden) + * # PC-UX uses this flag to make a distinction between its file system + * # and its swap. + * bit 6--0: + * 01H: FAT12 + * 11H: FAT16, <32MB [accessible to DOS 3.3] + * 21H: FAT16, >=32MB [Large Partition] + * 31H: NTFS + * 28H: Windows NT (Volume/Stripe Set?) + * 41H: Windows NT (Volume/Stripe Set?) + * 48H: Windows NT (Volume/Stripe Set?) + * 61H: FAT32 + * 04H: PC-UX + * 06H: N88-BASIC + * 44H: *BSD + * 62H: ext2, linux-swap + */ + +#define MAX_PART_COUNT 16 +#define PC9800_EXTFMT_MAGIC 0xAA55 + +#define BIT(x) (1 << (x)) +#define GET_BIT(n,bit) (((n) & BIT(bit)) != 0) +#define SET_BIT(n,bit,val) n = (val)? (n | BIT(bit)) : (n & ~BIT(bit)) + +typedef struct _PC98RawPartition PC98RawPartition; +typedef struct _PC98RawTable PC98RawTable; + +/* ripped from Linux/98 source */ +struct _PC98RawPartition { + uint8_t mid; /* 0x80 - boot */ + uint8_t sid; /* 0x80 - active */ + uint8_t dum1; /* dummy for padding */ + uint8_t dum2; /* dummy for padding */ + uint8_t ipl_sect; /* IPL sector */ + uint8_t ipl_head; /* IPL head */ + uint16_t ipl_cyl; /* IPL cylinder */ + uint8_t sector; /* starting sector */ + uint8_t head; /* starting head */ + uint16_t cyl; /* starting cylinder */ + uint8_t end_sector; /* end sector */ + uint8_t end_head; /* end head */ + uint16_t end_cyl; /* end cylinder */ + char name[16]; +} __attribute__((packed)); + +struct _PC98RawTable { + uint8_t boot_code [510]; + uint16_t magic; + PC98RawPartition partitions [MAX_PART_COUNT]; +} __attribute__((packed)); + +typedef struct { + PedSector ipl_sector; + int system; + int boot; + int hidden; + char name [17]; +} PC98PartitionData; + +/* this MBR boot code is dummy */ +static char MBR_BOOT_CODE[] = { + 0xcb, /* retf */ + 0x00, 0x00, 0x00, /* */ + 0x49, 0x50, 0x4c, 0x31 /* "IPL1" */ +}; + +static PedDiskType pc98_disk_type; + +static PedSector chs_to_sector (const PedDevice* dev, int c, int h, int s); +static void sector_to_chs (const PedDevice* dev, PedSector sector, + int* c, int* h, int* s); + +/* magic(?) check */ +static int +pc98_check_magic (const PC98RawTable *part_table) +{ + /* check "extended-format" (have partition table?) */ + if (PED_LE16_TO_CPU(part_table->magic) != PC9800_EXTFMT_MAGIC) + return 0; + + return 1; +} + +static int +pc98_check_ipl_signature (const PC98RawTable *part_table) +{ + return !memcmp (part_table->boot_code + 4, "IPL1", 4); +} + +static int +check_partition_consistency (const PedDevice* dev, + const PC98RawPartition* raw_part) +{ + if (raw_part->ipl_sect >= dev->hw_geom.sectors + || raw_part->sector >= dev->hw_geom.sectors + || raw_part->end_sector >= dev->hw_geom.sectors + || raw_part->ipl_head >= dev->hw_geom.heads + || raw_part->head >= dev->hw_geom.heads + || raw_part->end_head >= dev->hw_geom.heads + || PED_LE16_TO_CPU(raw_part->ipl_cyl) >= dev->hw_geom.cylinders + || PED_LE16_TO_CPU(raw_part->cyl) >= dev->hw_geom.cylinders + || PED_LE16_TO_CPU(raw_part->end_cyl) >= dev->hw_geom.cylinders + || PED_LE16_TO_CPU(raw_part->cyl) + > PED_LE16_TO_CPU(raw_part->end_cyl) +#if 0 + || !chs_to_sector(dev, PED_LE16_TO_CPU(raw_part->ipl_cyl), + raw_part->ipl_head, raw_part->ipl_sect) + || !chs_to_sector(dev, PED_LE16_TO_CPU(raw_part->cyl), + raw_part->head, raw_part->sector) + || !chs_to_sector(dev, PED_LE16_TO_CPU(raw_part->end_cyl), + raw_part->end_head, raw_part->end_sector) +#endif + || PED_LE16_TO_CPU(raw_part->end_cyl) + < PED_LE16_TO_CPU(raw_part->cyl)) + return 0; + + return 1; +} + +static int +pc98_probe (const PedDevice *dev) +{ + PC98RawTable part_table; + int empty; + const PC98RawPartition* p; + + PED_ASSERT (dev != NULL, return 0); + + if (dev->sector_size != 512) + return 0; + + if (!ped_device_read (dev, &part_table, 0, 2)) + return 0; + + /* check magic */ + if (!pc98_check_magic (&part_table)) + return 0; + + /* check consistency */ + empty = 1; + for (p = part_table.partitions; + p < part_table.partitions + MAX_PART_COUNT; + p++) + { + if (p->mid == 0 && p->sid == 0) + continue; + empty = 0; + if (!check_partition_consistency (dev, p)) + return 0; + } + + /* check boot loader */ + if (pc98_check_ipl_signature (&part_table)) + return 1; + else if (part_table.boot_code[0]) /* invalid boot loader */ + return 0; + + /* Not to mistake msdos disk map for PC-9800's empty disk map */ + if (empty) + return 0; + + return 1; +} + +#ifndef DISCOVER_ONLY +static int +pc98_clobber (PedDevice* dev) +{ + PC98RawTable table; + + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (pc98_probe (dev), return 0); + + if (!ped_device_read (dev, &table, 0, 1)) + return 0; + + memset (table.partitions, 0, sizeof (table.partitions)); + table.magic = PED_CPU_TO_LE16(0); + + if (pc98_check_ipl_signature (&table)) + memset (table.boot_code, 0, sizeof (table.boot_code)); + + if (!ped_device_write (dev, (void*) &table, 0, 1)) + return 0; + return ped_device_sync (dev); +} +#endif /* !DISCOVER_ONLY */ + +static PedDisk* +pc98_alloc (const PedDevice* dev) +{ + PedDisk* disk; + + PED_ASSERT (dev != NULL, return 0); + + return _ped_disk_alloc (dev, &pc98_disk_type); +} + +static PedDisk* +pc98_duplicate (const PedDisk* disk) +{ + return ped_disk_new_fresh (disk->dev, &pc98_disk_type); +} + +static void +pc98_free (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, return); + + _ped_disk_free (disk); +} + +static PedSector +chs_to_sector (const PedDevice* dev, int c, int h, int s) +{ + PED_ASSERT (dev != NULL, return 0); + return (c * dev->hw_geom.heads + h) * dev->hw_geom.sectors + s; +} + +static void +sector_to_chs (const PedDevice* dev, PedSector sector, int* c, int* h, int* s) +{ + PedSector cyl_size; + + PED_ASSERT (dev != NULL, return); + PED_ASSERT (c != NULL, return); + PED_ASSERT (h != NULL, return); + PED_ASSERT (s != NULL, return); + + cyl_size = dev->hw_geom.heads * dev->hw_geom.sectors; + + *c = sector / cyl_size; + *h = (sector) % cyl_size / dev->hw_geom.sectors; + *s = (sector) % cyl_size % dev->hw_geom.sectors; +} + +static PedSector +legacy_start (const PedDisk* disk, const PC98RawPartition* raw_part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (raw_part != NULL, return 0); + + return chs_to_sector (disk->dev, PED_LE16_TO_CPU(raw_part->cyl), + raw_part->head, raw_part->sector); +} + +static PedSector +legacy_end (const PedDisk* disk, const PC98RawPartition* raw_part) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (raw_part != NULL, return 0); + + if (raw_part->end_head == 0 && raw_part->end_sector == 0) { + return chs_to_sector (disk->dev, + PED_LE16_TO_CPU(raw_part->end_cyl), + disk->dev->hw_geom.heads - 1, + disk->dev->hw_geom.sectors - 1); + } else { + return chs_to_sector (disk->dev, + PED_LE16_TO_CPU(raw_part->end_cyl), + raw_part->end_head, + raw_part->end_sector); + } +} + +static int +is_unused_partition(const PC98RawPartition* raw_part) +{ + if (raw_part->mid || raw_part->sid + || raw_part->ipl_sect + || raw_part->ipl_head + || PED_LE16_TO_CPU(raw_part->ipl_cyl) + || raw_part->sector + || raw_part->head + || PED_LE16_TO_CPU(raw_part->cyl) + || raw_part->end_sector + || raw_part->end_head + || PED_LE16_TO_CPU(raw_part->end_cyl)) + return 0; + return 1; +} + +static int +read_table (PedDisk* disk) +{ + int i; + PC98RawTable table; + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + constraint_any = ped_constraint_any (disk->dev); + + if (!ped_device_read (disk->dev, (void*) &table, 0, 2)) + goto error; + + if (!pc98_check_magic(&table)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid partition table on %s."), + disk->dev->path)) + goto error; + } + + for (i = 0; i < MAX_PART_COUNT; i++) { + PC98RawPartition* raw_part; + PedPartition* part; + PC98PartitionData* pc98_data; + PedSector part_start; + PedSector part_end; + + raw_part = &table.partitions [i]; + + if (is_unused_partition(raw_part)) + continue; + + part_start = legacy_start (disk, raw_part); + part_end = legacy_end (disk, raw_part); + + part = ped_partition_new (disk, 0, NULL, part_start, part_end); + if (!part) + goto error; + pc98_data = part->disk_specific; + PED_ASSERT (pc98_data != NULL, goto error); + + pc98_data->system = (raw_part->mid << 8) | raw_part->sid; + pc98_data->boot = GET_BIT(raw_part->mid, 7); + pc98_data->hidden = !GET_BIT(raw_part->sid, 7); + + ped_partition_set_name (part, raw_part->name); + + pc98_data->ipl_sector = chs_to_sector ( + disk->dev, + PED_LE16_TO_CPU(raw_part->ipl_cyl), + raw_part->ipl_head, + raw_part->ipl_sect); + + /* hack */ + if (pc98_data->ipl_sector == part->geom.start) + pc98_data->ipl_sector = 0; + + part->num = i + 1; + + if (!ped_disk_add_partition (disk, part, constraint_any)) + goto error; + + if (part->geom.start != part_start + || part->geom.end != part_end) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Partition %d isn't aligned to cylinder " + "boundaries. This is still unsupported."), + part->num); + goto error; + } + + part->fs_type = ped_file_system_probe (&part->geom); + } + + ped_constraint_destroy (constraint_any); + return 1; + +error: + ped_disk_delete_all (disk); + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +pc98_read (PedDisk* disk) +{ + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + ped_disk_delete_all (disk); + return read_table (disk); +} + +#ifndef DISCOVER_ONLY +static int +fill_raw_part (PC98RawPartition* raw_part, const PedPartition* part) +{ + PC98PartitionData* pc98_data; + int c, h, s; + const char* name; + + PED_ASSERT (raw_part != NULL, return 0); + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + pc98_data = part->disk_specific; + raw_part->mid = (pc98_data->system >> 8) & 0xFF; + raw_part->sid = pc98_data->system & 0xFF; + + SET_BIT(raw_part->mid, 7, pc98_data->boot); + SET_BIT(raw_part->sid, 7, !pc98_data->hidden); + + memset (raw_part->name, ' ', sizeof(raw_part->name)); + name = ped_partition_get_name (part); + PED_ASSERT (name != NULL, return 0); + PED_ASSERT (strlen (name) <= 16, return 0); + if (!strlen (name) && part->fs_type) + name = part->fs_type->name; + memcpy (raw_part->name, name, strlen (name)); + + sector_to_chs (part->disk->dev, part->geom.start, &c, &h, &s); + raw_part->cyl = PED_CPU_TO_LE16(c); + raw_part->head = h; + raw_part->sector = s; + + if (pc98_data->ipl_sector) { + sector_to_chs (part->disk->dev, pc98_data->ipl_sector, + &c, &h, &s); + raw_part->ipl_cyl = PED_CPU_TO_LE16(c); + raw_part->ipl_head = h; + raw_part->ipl_sect = s; + } else { + raw_part->ipl_cyl = raw_part->cyl; + raw_part->ipl_head = raw_part->head; + raw_part->ipl_sect = raw_part->sector; + } + + sector_to_chs (part->disk->dev, part->geom.end, &c, &h, &s); + if (h != part->disk->dev->hw_geom.heads - 1 + || s != part->disk->dev->hw_geom.sectors - 1) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Partition %d isn't aligned to cylinder " + "boundaries. This is still unsupported."), + part->num); + return 0; + } + raw_part->end_cyl = PED_CPU_TO_LE16(c); +#if 0 + raw_part->end_head = h; + raw_part->end_sector = s; +#else + raw_part->end_head = 0; + raw_part->end_sector = 0; +#endif + + return 1; +} + +static int +pc98_write (PedDisk* disk) +{ + PC98RawTable table; + PedPartition* part; + int i; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + if (!ped_device_read (disk->dev, &table, 0, 2)) + return 0; + + if (!pc98_check_ipl_signature (&table)) { + memset (table.boot_code, 0, sizeof(table.boot_code)); + memcpy (table.boot_code, MBR_BOOT_CODE, sizeof(MBR_BOOT_CODE)); + } + + memset (table.partitions, 0, sizeof (table.partitions)); + table.magic = PED_CPU_TO_LE16(PC9800_EXTFMT_MAGIC); + + for (i = 1; i <= MAX_PART_COUNT; i++) { + part = ped_disk_get_partition (disk, i); + if (!part) + continue; + + if (!fill_raw_part (&table.partitions [i - 1], part)) + return 0; + } + + if (!ped_device_write (disk->dev, (void*) &table, 0, 2)) + return 0; + return ped_device_sync (disk->dev); +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +pc98_partition_new ( + const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, PedSector start, PedSector end) +{ + PedPartition* part; + PC98PartitionData* pc98_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = pc98_data = ped_malloc (sizeof (PC98PartitionData)); + if (!pc98_data) + goto error_free_part; + pc98_data->ipl_sector = 0; + pc98_data->hidden = 0; + pc98_data->boot = 0; + strcpy (pc98_data->name, ""); + } else { + part->disk_specific = NULL; + } + return part; + +error_free_pc98_data: + ped_free (pc98_data); +error_free_part: + ped_free (part); +error: + return 0; +} + +static PedPartition* +pc98_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + PC98PartitionData* new_pc98_data; + PC98PartitionData* old_pc98_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_pc98_data = (PC98PartitionData*) part->disk_specific; + new_pc98_data = (PC98PartitionData*) new_part->disk_specific; + + /* ugly, but C is ugly :p */ + memcpy (new_pc98_data, old_pc98_data, sizeof (PC98PartitionData)); + return new_part; +} + +static void +pc98_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + + if (ped_partition_is_active (part)) + ped_free (part->disk_specific); + ped_free (part); +} + +static int +pc98_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + PC98PartitionData* pc98_data = part->disk_specific; + + part->fs_type = fs_type; + + pc98_data->system = 0x2062; + if (fs_type) { + if (!strcmp (fs_type->name, "fat16")) { + if (part->geom.length * 512 >= 32 * 1024 * 1024) + pc98_data->system = 0x2021; + else + pc98_data->system = 0x2011; + } else if (!strcmp (fs_type->name, "fat32")) { + pc98_data->system = 0x2061; + } else if (!strcmp (fs_type->name, "ntfs")) { + pc98_data->system = 0x2031; + } else if (!strncmp (fs_type->name, "ufs", 3)) { + pc98_data->system = 0x2044; + } else { /* ext2, reiser, xfs, etc. */ + /* ext2 partitions must be marked boot */ + pc98_data->boot = 1; + pc98_data->system = 0xa062; + } + } + + if (pc98_data->boot) + pc98_data->system |= 0x8000; + if (!pc98_data->hidden) + pc98_data->system |= 0x0080; + return 1; +} + +static int +pc98_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + PedDisk* disk; + PC98PartitionData* pc98_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + pc98_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_HIDDEN: + pc98_data->hidden = state; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_BOOT: + pc98_data->boot = state; + return ped_partition_set_system (part, part->fs_type); + + default: + return 0; + } +} + +static int +pc98_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + PC98PartitionData* pc98_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + pc98_data = part->disk_specific; + switch (flag) { + case PED_PARTITION_HIDDEN: + return pc98_data->hidden; + + case PED_PARTITION_BOOT: + return pc98_data->boot; + + default: + return 0; + } +} + +static int +pc98_partition_is_flag_available ( + const PedPartition* part, PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_HIDDEN: + case PED_PARTITION_BOOT: + return 1; + + default: + return 0; + } +} + +static void +pc98_partition_set_name (PedPartition* part, const char* name) +{ + PC98PartitionData* pc98_data; + int i; + + PED_ASSERT (part != NULL, return); + PED_ASSERT (part->disk_specific != NULL, return); + pc98_data = part->disk_specific; + + strncpy (pc98_data->name, name, 16); + pc98_data->name [16] = 0; + for (i = strlen (pc98_data->name) - 1; pc98_data->name[i] == ' '; i--) + pc98_data->name [i] = 0; +} + +static const char* +pc98_partition_get_name (const PedPartition* part) +{ + PC98PartitionData* pc98_data; + + PED_ASSERT (part != NULL, return NULL); + PED_ASSERT (part->disk_specific != NULL, return NULL); + pc98_data = part->disk_specific; + + return pc98_data->name; +} + +static PedConstraint* +_primary_constraint (PedDisk* disk) +{ + PedDevice* dev = disk->dev; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + PedSector cylinder_size; + + cylinder_size = dev->hw_geom.sectors * dev->hw_geom.heads; + + if (!ped_alignment_init (&start_align, 0, cylinder_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, cylinder_size)) + return NULL; + if (!ped_geometry_init (&max_geom, dev, cylinder_size, + dev->length - cylinder_size)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, dev->length); +} + +static int +pc98_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL, return 0); + + if (_ped_partition_attempt_align (part, constraint, + _primary_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +next_primary (PedDisk* disk) +{ + int i; + for (i=1; i<=MAX_PART_COUNT; i++) { + if (!ped_disk_get_partition (disk, i)) + return i; + } + return 0; +} + +static int +pc98_partition_enumerate (PedPartition* part) +{ + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + + /* don't re-number a partition */ + if (part->num != -1) + return 1; + + PED_ASSERT (ped_partition_is_active (part), return 0); + + part->num = next_primary (part->disk); + if (!part->num) { + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't add another partition.")); + return 0; + } + + return 1; +} + +static int +pc98_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + PedConstraint* constraint_any = NULL; + PedSector cyl_size; + + PED_ASSERT (disk != NULL, goto error); + PED_ASSERT (disk->dev != NULL, goto error); + + constraint_any = ped_constraint_any (disk->dev); + + cyl_size = disk->dev->hw_geom.sectors * disk->dev->hw_geom.heads; + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + 0, cyl_size - 1); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; + +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +pc98_get_max_primary_partition_count (const PedDisk* disk) +{ + return MAX_PART_COUNT; +} + +static PedDiskOps pc98_disk_ops = { + probe: pc98_probe, +#ifndef DISCOVER_ONLY + clobber: pc98_clobber, +#else + clobber: NULL, +#endif + alloc: pc98_alloc, + duplicate: pc98_duplicate, + free: pc98_free, + read: pc98_read, +#ifndef DISCOVER_ONLY + write: pc98_write, +#else + write: NULL, +#endif + + partition_new: pc98_partition_new, + partition_duplicate: pc98_partition_duplicate, + partition_destroy: pc98_partition_destroy, + partition_set_system: pc98_partition_set_system, + partition_set_flag: pc98_partition_set_flag, + partition_get_flag: pc98_partition_get_flag, + partition_is_flag_available: pc98_partition_is_flag_available, + partition_set_name: pc98_partition_set_name, + partition_get_name: pc98_partition_get_name, + partition_align: pc98_partition_align, + partition_enumerate: pc98_partition_enumerate, + + alloc_metadata: pc98_alloc_metadata, + get_max_primary_partition_count: + pc98_get_max_primary_partition_count +}; + +static PedDiskType pc98_disk_type = { + next: NULL, + name: "pc98", + ops: &pc98_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME +}; + +void +ped_disk_pc98_init () +{ + PED_ASSERT (sizeof (PC98RawTable) == 512 * 2, return); + ped_register_disk_type (&pc98_disk_type); +} + +void +ped_disk_pc98_done () +{ + ped_unregister_disk_type (&pc98_disk_type); +} diff --git a/libparted/labels/rdb.c b/libparted/labels/rdb.c new file mode 100644 index 0000000..b8fb920 --- /dev/null +++ b/libparted/labels/rdb.c @@ -0,0 +1,1191 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + disk_amiga.c - libparted module to manipulate amiga RDB partition tables. + Copyright (C) 2000, 2001, 2004 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + Contributor: Sven Luther <luther@debian.org> +*/ + +#include "config.h" + +#include <string.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* String manipulation */ +static void _amiga_set_bstr (const char *cstr, char *bstr, int maxsize) { + int size = strlen (cstr); + int i; + + if (size >= maxsize) return; + bstr[0] = size; + for (i = 0; i<size; i++) bstr[i+1] = cstr[i]; +} +static const char * _amiga_get_bstr (char * bstr) { + char * cstr = bstr + 1; + int size = bstr[0]; + + cstr[size] = '\0'; + return cstr; +} + +#define IDNAME_RIGIDDISK (uint32_t)0x5244534B /* 'RDSK' */ +#define IDNAME_BADBLOCK (uint32_t)0x42414442 /* 'BADB' */ +#define IDNAME_PARTITION (uint32_t)0x50415254 /* 'PART' */ +#define IDNAME_FILESYSHEADER (uint32_t)0x46534844 /* 'FSHD' */ +#define IDNAME_LOADSEG (uint32_t)0x4C534547 /* 'LSEG' */ +#define IDNAME_BOOT (uint32_t)0x424f4f54 /* 'BOOT' */ +#define IDNAME_FREE (uint32_t)0xffffffff + +static const char * +_amiga_block_id (uint32_t id) { + switch (id) { + case IDNAME_RIGIDDISK : + return "RDSK"; + case IDNAME_BADBLOCK : + return "BADB"; + case IDNAME_PARTITION : + return "PART"; + case IDNAME_FILESYSHEADER : + return "FSHD"; + case IDNAME_LOADSEG : + return "LSEG"; + case IDNAME_BOOT : + return "BOOT"; + case IDNAME_FREE : + return "<free>"; + default : + return "<unknown>"; + } +} +static int +_amiga_valid_block_id (uint32_t id) { + switch (id) { + case IDNAME_RIGIDDISK : + case IDNAME_BADBLOCK : + case IDNAME_PARTITION : + case IDNAME_FILESYSHEADER : + case IDNAME_LOADSEG : + case IDNAME_BOOT : + return 1; + case IDNAME_FREE : + default : + return 0; + } +} + +struct AmigaIds { + uint32_t ID; + struct AmigaIds *next; +}; + +static struct AmigaIds * +_amiga_add_id (uint32_t id, struct AmigaIds *ids) { + struct AmigaIds *newid; + + if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL) + return 0; + newid->ID = id; + newid->next = ids; + return newid; +} + +static void +_amiga_free_ids (struct AmigaIds *ids) { + struct AmigaIds *current, *next; + + for (current = ids; current != NULL; current = next) { + next = current->next; + ped_free (current); + } +} +static int +_amiga_id_in_list (uint32_t id, struct AmigaIds *ids) { + struct AmigaIds *current; + + for (current = ids; current != NULL; current = current->next) { + if (id == current->ID) + return 1; + } + return 0; +} + +struct AmigaBlock { + uint32_t amiga_ID; /* Identifier 32 bit word */ + uint32_t amiga_SummedLongss; /* Size of the structure for checksums */ + int32_t amiga_ChkSum; /* Checksum of the structure */ +}; +#define AMIGA(pos) ((struct AmigaBlock *)(pos)) + +static int +_amiga_checksum (struct AmigaBlock *blk) { + uint32_t *rdb = (uint32_t *) blk; + uint32_t sum; + int i, end; + + sum = PED_BE32_TO_CPU (rdb[0]); + end = PED_BE32_TO_CPU (rdb[1]); + + if (end > PED_SECTOR_SIZE_DEFAULT) end = PED_SECTOR_SIZE_DEFAULT; + + for (i = 1; i < end; i++) sum += PED_BE32_TO_CPU (rdb[i]); + + return sum; +} + +static void +_amiga_calculate_checksum (struct AmigaBlock *blk) { + blk->amiga_ChkSum = PED_CPU_TO_BE32( + PED_BE32_TO_CPU(blk->amiga_ChkSum) - + _amiga_checksum((struct AmigaBlock *) blk)); + return; +} + +static struct AmigaBlock * +_amiga_read_block (const PedDevice *dev, struct AmigaBlock *blk, + PedSector block, struct AmigaIds *ids) +{ + if (!ped_device_read (dev, blk, block, 1)) + return NULL; + if (ids && !_amiga_id_in_list(PED_BE32_TO_CPU(blk->amiga_ID), ids)) + return NULL; + if (_amiga_checksum (blk) != 0) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL, + _("%s : Bad checksum on block %llu of type %s."), + __func__, block, _amiga_block_id(PED_BE32_TO_CPU(blk->amiga_ID)))) + { + case PED_EXCEPTION_CANCEL : + return NULL; + case PED_EXCEPTION_FIX : + _amiga_calculate_checksum(AMIGA(blk)); + if (!ped_device_write ((PedDevice*)dev, blk, block, 1)) + return NULL; + case PED_EXCEPTION_IGNORE : + case PED_EXCEPTION_UNHANDLED : + default : + return blk; + } + } + return blk; +} + +struct RigidDiskBlock { + uint32_t rdb_ID; /* Identifier 32 bit word : 'RDSK' */ + uint32_t rdb_SummedLongs; /* Size of the structure for checksums */ + int32_t rdb_ChkSum; /* Checksum of the structure */ + uint32_t rdb_HostID; /* SCSI Target ID of host, not really used */ + uint32_t rdb_BlockBytes; /* Size of disk blocks */ + uint32_t rdb_Flags; /* RDB Flags */ + /* block list heads */ + uint32_t rdb_BadBlockList; /* Bad block list */ + uint32_t rdb_PartitionList; /* Partition list */ + uint32_t rdb_FileSysHeaderList; /* File system header list */ + uint32_t rdb_DriveInit; /* Drive specific init code */ + uint32_t rdb_BootBlockList; /* Amiga OS 4 Boot Blocks */ + uint32_t rdb_Reserved1[5]; /* Unused word, need to be set to $ffffffff */ + /* physical drive characteristics */ + uint32_t rdb_Cylinders; /* Number of the cylinders of the drive */ + uint32_t rdb_Sectors; /* Number of sectors of the drive */ + uint32_t rdb_Heads; /* Number of heads of the drive */ + uint32_t rdb_Interleave; /* Interleave */ + uint32_t rdb_Park; /* Head parking cylinder */ + uint32_t rdb_Reserved2[3]; /* Unused word, need to be set to $ffffffff */ + uint32_t rdb_WritePreComp; /* Starting cylinder of write precompensation */ + uint32_t rdb_ReducedWrite; /* Starting cylinder of reduced write current */ + uint32_t rdb_StepRate; /* Step rate of the drive */ + uint32_t rdb_Reserved3[5]; /* Unused word, need to be set to $ffffffff */ + /* logical drive characteristics */ + uint32_t rdb_RDBBlocksLo; /* low block of range reserved for hardblocks */ + uint32_t rdb_RDBBlocksHi; /* high block of range for these hardblocks */ + uint32_t rdb_LoCylinder; /* low cylinder of partitionable disk area */ + uint32_t rdb_HiCylinder; /* high cylinder of partitionable data area */ + uint32_t rdb_CylBlocks; /* number of blocks available per cylinder */ + uint32_t rdb_AutoParkSeconds; /* zero for no auto park */ + uint32_t rdb_HighRDSKBlock; /* highest block used by RDSK */ + /* (not including replacement bad blocks) */ + uint32_t rdb_Reserved4; + /* drive identification */ + char rdb_DiskVendor[8]; + char rdb_DiskProduct[16]; + char rdb_DiskRevision[4]; + char rdb_ControllerVendor[8]; + char rdb_ControllerProduct[16]; + char rdb_ControllerRevision[4]; + uint32_t rdb_Reserved5[10]; +}; + +#define RDSK(pos) ((struct RigidDiskBlock *)(pos)) + +#define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff) +#define RDB_LOCATION_LIMIT 16 +#define AMIGA_MAX_PARTITIONS 128 +#define MAX_RDB_BLOCK (RDB_LOCATION_LIMIT + 2 * AMIGA_MAX_PARTITIONS + 2) + +static uint32_t +_amiga_find_rdb (const PedDevice *dev, struct RigidDiskBlock *rdb) { + int i; + struct AmigaIds *ids; + + ids = _amiga_add_id (IDNAME_RIGIDDISK, NULL); + + for (i = 0; i<RDB_LOCATION_LIMIT; i++) { + if (!_amiga_read_block (dev, AMIGA(rdb), i, ids)) { + continue; + } + if (PED_BE32_TO_CPU (rdb->rdb_ID) == IDNAME_RIGIDDISK) { + _amiga_free_ids (ids); + return i; + } + } + _amiga_free_ids (ids); + return AMIGA_RDB_NOT_FOUND; +} + +struct PartitionBlock { + uint32_t pb_ID; /* Identifier 32 bit word : 'PART' */ + uint32_t pb_SummedLongs; /* Size of the structure for checksums */ + int32_t pb_ChkSum; /* Checksum of the structure */ + uint32_t pb_HostID; /* SCSI Target ID of host, not really used */ + uint32_t pb_Next; /* Block number of the next PartitionBlock */ + uint32_t pb_Flags; /* Part Flags (NOMOUNT and BOOTABLE) */ + uint32_t pb_Reserved1[2]; + uint32_t pb_DevFlags; /* Preferred flags for OpenDevice */ + char pb_DriveName[32]; /* Preferred DOS device name: BSTR form */ + uint32_t pb_Reserved2[15]; + uint32_t de_TableSize; /* Size of Environment vector */ + /* Size of the blocks in 32 bit words, usually 128 */ + uint32_t de_SizeBlock; + uint32_t de_SecOrg; /* Not used; must be 0 */ + uint32_t de_Surfaces; /* Number of heads (surfaces) */ + /* Disk sectors per block, used with SizeBlock, usually 1 */ + uint32_t de_SectorPerBlock; + uint32_t de_BlocksPerTrack; /* Blocks per track. drive specific */ + uint32_t de_Reserved; /* DOS reserved blocks at start of partition. */ + uint32_t de_PreAlloc; /* DOS reserved blocks at end of partition */ + uint32_t de_Interleave; /* Not used, usually 0 */ + uint32_t de_LowCyl; /* First cylinder of the partition */ + uint32_t de_HighCyl; /* Last cylinder of the partition */ + uint32_t de_NumBuffers; /* Initial # DOS of buffers. */ + uint32_t de_BufMemType; /* Type of mem to allocate for buffers */ + uint32_t de_MaxTransfer; /* Max number of bytes to transfer at a time */ + uint32_t de_Mask; /* Address Mask to block out certain memory */ + int32_t de_BootPri; /* Boot priority for autoboot */ + uint32_t de_DosType; /* Dostype of the file system */ + uint32_t de_Baud; /* Baud rate for serial handler */ + uint32_t de_Control; /* Control word for handler/filesystem */ + uint32_t de_BootBlocks; /* Number of blocks containing boot code */ + uint32_t pb_EReserved[12]; +}; + +#define PART(pos) ((struct PartitionBlock *)(pos)) + +#define PBFB_BOOTABLE 0 /* this partition is intended to be bootable */ +#define PBFF_BOOTABLE 1L /* (expected directories and files exist) */ +#define PBFB_NOMOUNT 1 /* do not mount this partition (e.g. manually */ +#define PBFF_NOMOUNT 2L /* mounted, but space reserved here) */ +#define PBFB_RAID 2 /* this partition is intended to be part of */ +#define PBFF_RAID 4L /* a RAID array */ +#define PBFB_LVM 3 /* this partition is intended to be part of */ +#define PBFF_LVM 8L /* a LVM volume group */ + + +struct LinkedBlock { + uint32_t lk_ID; /* Identifier 32 bit word */ + uint32_t lk_SummedLongs; /* Size of the structure for checksums */ + int32_t lk_ChkSum; /* Checksum of the structure */ + uint32_t pb_HostID; /* SCSI Target ID of host, not really used */ + uint32_t lk_Next; /* Block number of the next PartitionBlock */ +}; +struct Linked2Block { + uint32_t lk2_ID; /* Identifier 32 bit word */ + uint32_t lk2_SummedLongs; /* Size of the structure for checksums */ + int32_t lk2_ChkSum; /* Checksum of the structure */ + uint32_t lk2_HostID; /* SCSI Target ID of host, not really used */ + uint32_t lk2_Next; /* Block number of the next PartitionBlock */ + uint32_t lk2_Reverved[13]; + uint32_t lk2_Linked; /* Secondary linked list */ +}; +#define LINK_END (uint32_t)0xffffffff +#define LNK(pos) ((struct LinkedBlock *)(pos)) +#define LNK2(pos) ((struct Linked2Block *)(pos)) + + +static PedDiskType amiga_disk_type; + +static int +amiga_probe (const PedDevice *dev) +{ + struct RigidDiskBlock *rdb; + uint32_t found; + PED_ASSERT(dev != NULL, return 0); + + if ((rdb=RDSK(ped_malloc(dev->sector_size)))==NULL) + return 0; + found = _amiga_find_rdb (dev, rdb); + ped_free (rdb); + + return (found == AMIGA_RDB_NOT_FOUND ? 0 : 1); +} + +static PedDisk* +amiga_alloc (const PedDevice* dev) +{ + PedDisk *disk; + struct AmigaDisk *adsk; + struct RigidDiskBlock *rdb; + PedSector cyl_size; + int highest_cylinder, highest_block; + + PED_ASSERT(dev != NULL, return NULL); + cyl_size = dev->hw_geom.sectors * dev->hw_geom.heads; + + if (!(disk = _ped_disk_alloc (dev, &amiga_disk_type))) + return NULL; + + if (!(disk->disk_specific = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) { + ped_free (disk); + return NULL; + } + rdb = disk->disk_specific; + + memset(rdb, 0, sizeof(struct RigidDiskBlock)); + + rdb->rdb_ID = PED_CPU_TO_BE32 (IDNAME_RIGIDDISK); + rdb->rdb_SummedLongs = PED_CPU_TO_BE32 (64); + rdb->rdb_HostID = PED_CPU_TO_BE32 (0); + rdb->rdb_BlockBytes = PED_CPU_TO_BE32 (PED_SECTOR_SIZE_DEFAULT); + rdb->rdb_Flags = PED_CPU_TO_BE32 (0); + + /* Block lists */ + rdb->rdb_BadBlockList = PED_CPU_TO_BE32 (LINK_END); + rdb->rdb_PartitionList = PED_CPU_TO_BE32 (LINK_END); + rdb->rdb_FileSysHeaderList = PED_CPU_TO_BE32 (LINK_END); + rdb->rdb_DriveInit = PED_CPU_TO_BE32 (LINK_END); + rdb->rdb_BootBlockList = PED_CPU_TO_BE32 (LINK_END); + + /* Physical drive characteristics */ + rdb->rdb_Cylinders = PED_CPU_TO_BE32 (dev->hw_geom.cylinders); + rdb->rdb_Sectors = PED_CPU_TO_BE32 (dev->hw_geom.sectors); + rdb->rdb_Heads = PED_CPU_TO_BE32 (dev->hw_geom.heads); + rdb->rdb_Interleave = PED_CPU_TO_BE32 (0); + rdb->rdb_Park = PED_CPU_TO_BE32 (dev->hw_geom.cylinders); + rdb->rdb_WritePreComp = PED_CPU_TO_BE32 (dev->hw_geom.cylinders); + rdb->rdb_ReducedWrite = PED_CPU_TO_BE32 (dev->hw_geom.cylinders); + rdb->rdb_StepRate = PED_CPU_TO_BE32 (0); + + highest_cylinder = 1 + MAX_RDB_BLOCK / cyl_size; + highest_block = highest_cylinder * cyl_size - 1; + + /* Logical driver characteristics */ + rdb->rdb_RDBBlocksLo = PED_CPU_TO_BE32 (0); + rdb->rdb_RDBBlocksHi = PED_CPU_TO_BE32 (highest_block); + rdb->rdb_LoCylinder = PED_CPU_TO_BE32 (highest_cylinder); + rdb->rdb_HiCylinder = PED_CPU_TO_BE32 (dev->hw_geom.cylinders -1); + rdb->rdb_CylBlocks = PED_CPU_TO_BE32 (cyl_size); + rdb->rdb_AutoParkSeconds = PED_CPU_TO_BE32 (0); + /* rdb_HighRDSKBlock will only be set when writing */ + rdb->rdb_HighRDSKBlock = PED_CPU_TO_BE32 (0); + + /* Driver identification */ + _amiga_set_bstr("", rdb->rdb_DiskVendor, 8); + _amiga_set_bstr(dev->model, rdb->rdb_DiskProduct, 16); + _amiga_set_bstr("", rdb->rdb_DiskRevision, 4); + _amiga_set_bstr("", rdb->rdb_ControllerVendor, 8); + _amiga_set_bstr("", rdb->rdb_ControllerProduct, 16); + _amiga_set_bstr("", rdb->rdb_ControllerRevision, 4); + + /* And calculate the checksum */ + _amiga_calculate_checksum ((struct AmigaBlock *) rdb); + + return disk; +} + +static PedDisk* +amiga_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + struct RigidDiskBlock * new_rdb; + struct RigidDiskBlock * old_rdb; + PED_ASSERT(disk != NULL, return NULL); + PED_ASSERT(disk->dev != NULL, return NULL); + PED_ASSERT(disk->disk_specific != NULL, return NULL); + + old_rdb = (struct RigidDiskBlock *) disk->disk_specific; + + if (!(new_disk = ped_disk_new_fresh (disk->dev, &amiga_disk_type))) + return NULL; + + new_rdb = (struct RigidDiskBlock *) new_disk->disk_specific; + memcpy (new_rdb, old_rdb, 256); + return new_disk; +} + +static void +amiga_free (PedDisk* disk) +{ + PED_ASSERT(disk != NULL, return); + PED_ASSERT(disk->disk_specific != NULL, return); + + ped_free (disk->disk_specific); + _ped_disk_free (disk); +} + +#ifndef DISCOVER_ONLY +static int +amiga_clobber (PedDevice* dev) +{ + struct RigidDiskBlock *rdb; + uint32_t i; + int result = 0; + PED_ASSERT(dev != NULL, return 0); + + if ((rdb=RDSK(ped_malloc(PED_SECTOR_SIZE_DEFAULT)))==NULL) + return 0; + + while ((i = _amiga_find_rdb (dev, rdb)) != AMIGA_RDB_NOT_FOUND) { + rdb->rdb_ID = PED_CPU_TO_BE32 (0); + result = ped_device_write (dev, (void*) rdb, i, 1); + } + + ped_free (rdb); + + return result; +} +#endif /* !DISCOVER_ONLY */ + +static int +_amiga_loop_check (uint32_t block, uint32_t * blocklist, uint32_t max) +{ + uint32_t i; + + for (i = 0; i < max; i++) + if (block == blocklist[i]) { + /* We are looping, let's stop. */ + return 1; + } + blocklist[max] = block; + return 0; +} + +/* We have already allocated a rdb, we are now reading it from the disk */ +static int +amiga_read (PedDisk* disk) +{ + struct RigidDiskBlock *rdb; + struct PartitionBlock *partition; + uint32_t partblock; + uint32_t partlist[AMIGA_MAX_PARTITIONS]; + PedSector cylblocks; + int i; + + PED_ASSERT(disk != NULL, return 0); + PED_ASSERT(disk->dev != NULL, return 0); + PED_ASSERT(disk->dev->sector_size % 512 == 0, return 0); + PED_ASSERT(disk->disk_specific != NULL, return 0); + rdb = RDSK(disk->disk_specific); + + if (_amiga_find_rdb (disk->dev, rdb) == AMIGA_RDB_NOT_FOUND) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Didn't find rdb block, should never happen."), __func__); + return 0; + } + + /* Let's copy the rdb read geometry to the dev */ + /* FIXME: should this go into disk->dev->bios_geom instead? */ + disk->dev->hw_geom.cylinders = PED_BE32_TO_CPU (rdb->rdb_Cylinders); + disk->dev->hw_geom.heads = PED_BE32_TO_CPU (rdb->rdb_Heads); + disk->dev->hw_geom.sectors = PED_BE32_TO_CPU (rdb->rdb_Sectors); + cylblocks = (PedSector) PED_BE32_TO_CPU (rdb->rdb_Heads) * + (PedSector) PED_BE32_TO_CPU (rdb->rdb_Sectors); + + /* Remove all partitions in the former in memory table */ + ped_disk_delete_all (disk); + + /* Let's allocate a partition block */ + if (!(partition = ped_malloc (disk->dev->sector_size))) + return 0; + + /* We initialize the hardblock free list to detect loops */ + for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = LINK_END; + + for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList); + i < AMIGA_MAX_PARTITIONS && partblock != LINK_END; + i++, partblock = PED_BE32_TO_CPU(partition->pb_Next)) + { + PedPartition *part; + PedSector start, end; + struct DosEnvec *de; + PedConstraint *constraint_exact; + int j; + + /* Let's look for loops in the partition table */ + if (_amiga_loop_check(partblock, partlist, i)) { + break; + } + + /* Let's allocate and read a partition block to get its geometry*/ + if (!_amiga_read_block (disk->dev, AMIGA(partition), + (PedSector)partblock, NULL)) { + ped_free(partition); + return 0; + } + + start = ((PedSector) PED_BE32_TO_CPU (partition->de_LowCyl)) + * cylblocks; + end = (((PedSector) PED_BE32_TO_CPU (partition->de_HighCyl)) + + 1) * cylblocks - 1; + + /* We can now construct a new partition */ + if (!(part = ped_partition_new (disk, 0, NULL, start, end))) { + ped_free(partition); + return 0; + } + /* And copy over the partition block */ + memcpy(part->disk_specific, partition, 256); + + part->num = i; + part->type = 0; + /* Let's probe what file system is present on the disk */ + part->fs_type = ped_file_system_probe (&part->geom); + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) { + ped_partition_destroy(part); + ped_free(partition); + return 0; + } + ped_constraint_destroy (constraint_exact); + } + return 1; +} + +static int +_amiga_find_free_blocks(PedDisk *disk, uint32_t *table, + struct LinkedBlock *block, uint32_t first, uint32_t type) +{ + PedSector next; + + PED_ASSERT(disk != NULL, return 0); + PED_ASSERT(disk->dev != NULL, return 0); + + for (next = first; next != LINK_END; next = PED_BE32_TO_CPU(block->lk_Next)) { + if (table[next] != IDNAME_FREE) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL, + _("%s : Loop detected at block %d."), __func__, next)) + { + case PED_EXCEPTION_CANCEL : + return 0; + case PED_EXCEPTION_FIX : + /* TODO : Need to add fixing code */ + case PED_EXCEPTION_IGNORE : + case PED_EXCEPTION_UNHANDLED : + default : + return 1; + } + } + + if (!_amiga_read_block (disk->dev, AMIGA(block), next, NULL)) { + return 0; + } + if (PED_BE32_TO_CPU(block->lk_ID) != type) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : The %s list seems bad at block %s."), + __func__, _amiga_block_id(PED_BE32_TO_CPU(block->lk_ID)), next)) + { + /* TODO : to more subtile things here */ + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + return 0; + } + } + table[next] = type; + if (PED_BE32_TO_CPU(block->lk_ID) == IDNAME_FILESYSHEADER) { + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU(LNK2(block)->lk2_Linked), + IDNAME_LOADSEG) == 0) return 0; + } + } + return 1; +} +static uint32_t +_amiga_next_free_block(uint32_t *table, uint32_t start, uint32_t type) { + int i; + + for (i = start; table[i] != type && table[i] != IDNAME_FREE; i++); + return i; +} +static PedPartition * +_amiga_next_real_partition(PedDisk *disk, PedPartition *part) { + PedPartition *next; + + for (next = ped_disk_next_partition (disk, part); + next != NULL && !ped_partition_is_active (next); + next = ped_disk_next_partition (disk, next)); + return next; +} +#ifndef DISCOVER_ONLY +static int +amiga_write (PedDisk* disk) +{ + struct RigidDiskBlock *rdb; + struct LinkedBlock *block; + struct PartitionBlock *partition; + PedPartition *part, *next_part; + PedSector cylblocks, first_hb, last_hb, last_used_hb; + uint32_t * table; + uint32_t i, rdb_block, max_part; + uint32_t rdb_num, part_num, block_num, next_num; + + PED_ASSERT (disk != NULL, return 0;); + PED_ASSERT (disk->dev != NULL, return 0;); + PED_ASSERT (disk->disk_specific != NULL, return 0;); + + if (!(rdb = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) + return 0; + + /* Let's read the rdb */ + if ((rdb_num = _amiga_find_rdb (disk->dev, rdb)) == AMIGA_RDB_NOT_FOUND) { + rdb_num = 2; + } else { + memcpy (RDSK(disk->disk_specific), rdb, PED_SECTOR_SIZE_DEFAULT); + } + ped_free (rdb); + rdb = RDSK(disk->disk_specific); + + cylblocks = (PedSector) PED_BE32_TO_CPU (rdb->rdb_Heads) * + (PedSector) PED_BE32_TO_CPU (rdb->rdb_Sectors); + first_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_RDBBlocksLo); + last_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_RDBBlocksHi); + last_used_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_HighRDSKBlock); + + /* let's allocate a free block table and initialize it */ + if (!(table = ped_malloc ((last_hb - first_hb + 1) * sizeof(uint32_t)))) + return 0; + + memset(table, 0xff, (last_hb - first_hb + 1) * sizeof(uint32_t)); + for (i = 0; i<=rdb_num; i++) table[i] = IDNAME_RIGIDDISK; + + /* Let's allocate a partition block */ + if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) { + ped_free (table); + return 0; + } + + /* And fill the free block table */ + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU (rdb->rdb_BadBlockList), IDNAME_BADBLOCK) == 0) + { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to list bad blocks."), __func__); + goto error_free_table; + } + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU (rdb->rdb_PartitionList), IDNAME_PARTITION) == 0) + { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to list partition blocks."), __func__); + goto error_free_table; + } + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU (rdb->rdb_FileSysHeaderList), IDNAME_FILESYSHEADER) == 0) + { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to list file system blocks."), __func__); + goto error_free_table; + } + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU (rdb->rdb_BootBlockList), IDNAME_BOOT) == 0) + { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to list boot blocks."), __func__); + goto error_free_table; + } + + block_num = next_num = part_num = _amiga_next_free_block(table, rdb_num+1, + IDNAME_PARTITION); + part = _amiga_next_real_partition(disk, NULL); + rdb->rdb_PartitionList = PED_CPU_TO_BE32(part ? part_num : LINK_END); + for (; part != NULL; part = next_part, block_num = next_num) { + PED_ASSERT(part->disk_specific != NULL, return 0); + PED_ASSERT(part->geom.start % cylblocks == 0, return 0); + PED_ASSERT((part->geom.end + 1) % cylblocks == 0, return 0); + + next_part = _amiga_next_real_partition(disk, part); + next_num = _amiga_next_free_block(table, block_num+1, IDNAME_PARTITION); + + partition = PART(part->disk_specific); + if (next_part == NULL) + partition->pb_Next = PED_CPU_TO_BE32(LINK_END); + else + partition->pb_Next = PED_CPU_TO_BE32(next_num); + partition->de_LowCyl = PED_CPU_TO_BE32(part->geom.start/cylblocks); + partition->de_HighCyl = PED_CPU_TO_BE32((part->geom.end+1)/cylblocks-1); + _amiga_calculate_checksum(AMIGA(partition)); + if (!ped_device_write (disk->dev, (void*) partition, block_num, 1)) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Failed to write partition block at %d."), + block_num); + goto error_free_table; + /* WARNING : If we fail here, we stop everything, + * and the partition table is lost. A better + * solution should be found, using the second + * half of the hardblocks to not overwrite the + * old partition table. It becomes problematic + * if we use more than half of the hardblocks. */ + } + } + + if (block_num > PED_BE32_TO_CPU (rdb->rdb_HighRDSKBlock)) + rdb->rdb_HighRDSKBlock = PED_CPU_TO_BE32(block_num); + + _amiga_calculate_checksum(AMIGA(rdb)); + if (!ped_device_write (disk->dev, (void*) disk->disk_specific, rdb_num, 1)) + goto error_free_table; + + ped_free (table); + ped_free (block); + return ped_device_sync (disk->dev); + +error_free_table: + ped_free (table); +error_free_block: + ped_free (block); +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +amiga_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition *part; + PedDevice *dev; + PedSector cyl; + struct PartitionBlock *partition; + struct RigidDiskBlock *rdb; + + PED_ASSERT(disk != NULL, return NULL); + PED_ASSERT(disk->dev != NULL, return NULL); + PED_ASSERT(disk->disk_specific != NULL, return NULL); + dev = disk->dev; + cyl = (PedSector) (dev->hw_geom.sectors * dev->hw_geom.heads); + rdb = RDSK(disk->disk_specific); + + if (!(part = _ped_partition_alloc (disk, part_type, fs_type, start, end))) + return NULL; + + if (ped_partition_is_active (part)) { + if (!(part->disk_specific = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) { + ped_free (part); + return NULL; + } + partition = PART(part->disk_specific); + memset(partition, 0, sizeof(struct PartitionBlock)); + + partition->pb_ID = PED_CPU_TO_BE32(IDNAME_PARTITION); + partition->pb_SummedLongs = PED_CPU_TO_BE32(64); + partition->pb_HostID = rdb->rdb_HostID; + partition->pb_Flags = PED_CPU_TO_BE32(0); + /* TODO : use a scheme including the device name and the + * partition number, if it is possible */ + _amiga_set_bstr("dhx", partition->pb_DriveName, 32); + + partition->de_TableSize = PED_CPU_TO_BE32(19); + partition->de_SizeBlock = PED_CPU_TO_BE32(128); + partition->de_SecOrg = PED_CPU_TO_BE32(0); + partition->de_Surfaces = PED_CPU_TO_BE32(dev->hw_geom.heads); + partition->de_SectorPerBlock = PED_CPU_TO_BE32(1); + partition->de_BlocksPerTrack + = PED_CPU_TO_BE32(dev->hw_geom.sectors); + partition->de_Reserved = PED_CPU_TO_BE32(2); + partition->de_PreAlloc = PED_CPU_TO_BE32(0); + partition->de_Interleave = PED_CPU_TO_BE32(0); + partition->de_LowCyl = PED_CPU_TO_BE32(start/cyl); + partition->de_HighCyl = PED_CPU_TO_BE32((end+1)/cyl-1); + partition->de_NumBuffers = PED_CPU_TO_BE32(30); + partition->de_BufMemType = PED_CPU_TO_BE32(0); + partition->de_MaxTransfer = PED_CPU_TO_BE32(0x7fffffff); + partition->de_Mask = PED_CPU_TO_BE32(0xffffffff); + partition->de_BootPri = PED_CPU_TO_BE32(0); + partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); + partition->de_Baud = PED_CPU_TO_BE32(0); + partition->de_Control = PED_CPU_TO_BE32(0); + partition->de_BootBlocks = PED_CPU_TO_BE32(0); + + } else { + part->disk_specific = NULL; + } + return part; +} + +static PedPartition* +amiga_partition_duplicate (const PedPartition* part) +{ + PedPartition *new_part; + struct PartitionBlock *new_amiga_part; + struct PartitionBlock *old_amiga_part; + + PED_ASSERT(part != NULL, return NULL); + PED_ASSERT(part->disk != NULL, return NULL); + PED_ASSERT(part->disk_specific != NULL, return NULL); + old_amiga_part = (struct PartitionBlock *) part->disk_specific; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + + new_amiga_part = (struct PartitionBlock *) new_part->disk_specific; + memcpy (new_amiga_part, old_amiga_part, 256); + + return new_part; +} + +static void +amiga_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + + if (ped_partition_is_active (part)) { + PED_ASSERT (part->disk_specific != NULL, return); + ped_free (part->disk_specific); + } + _ped_partition_free (part); +} + +static int +amiga_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + partition = PART(part->disk_specific); + + part->fs_type = fs_type; + + if (!fs_type) + partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); /* 'LNX\0' */ + else if (!strcmp (fs_type->name, "ext2")) + partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); /* 'LNX\0' */ + else if (!strcmp (fs_type->name, "ext3")) + partition->de_DosType = PED_CPU_TO_BE32(0x45585403); /* 'EXT\3' */ + else if (!strcmp (fs_type->name, "linux-swap")) + partition->de_DosType = PED_CPU_TO_BE32(0x53575000); /* 'SWP\0' */ + else if (!strcmp (fs_type->name, "fat16")) + partition->de_DosType = PED_CPU_TO_BE32(0x46415400); /* 'FAT\0' */ + else if (!strcmp (fs_type->name, "fat32")) + partition->de_DosType = PED_CPU_TO_BE32(0x46415401); /* 'FAT\1'*/ + else if (!strcmp (fs_type->name, "hfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x48465300); /* 'HFS\0' */ + else if (!strcmp (fs_type->name, "jfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x4a465300); /* 'JFS\0' */ + else if (!strcmp (fs_type->name, "ntfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x4e544653); /* 'NTFS' */ + else if (!strcmp (fs_type->name, "reiserfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x52465300); /* 'RFS\0' */ + else if (!strcmp (fs_type->name, "sun-ufs")) + partition->de_DosType = PED_CPU_TO_BE32(0x53554653); /* 'SUFS' */ + else if (!strcmp (fs_type->name, "hp-ufs")) + partition->de_DosType = PED_CPU_TO_BE32(0x48554653); /* 'HUFS' */ + else if (!strcmp (fs_type->name, "xfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x58465300); /* 'XFS\0' */ + else + partition->de_DosType = PED_CPU_TO_BE32(0x00000000); /* unknown */ + return 1; +} + +static int +amiga_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + partition = PART(part->disk_specific); + + switch (flag) { + case PED_PARTITION_BOOT: + if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_BOOTABLE); + else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_BOOTABLE)); + return 1; + case PED_PARTITION_HIDDEN: + if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_NOMOUNT); + else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_NOMOUNT)); + return 1; + case PED_PARTITION_RAID: + if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_RAID); + else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_RAID)); + return 1; + case PED_PARTITION_LVM: + if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_LVM); + else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_LVM)); + return 1; + default: + return 0; + } +} + +static int +amiga_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + partition = PART(part->disk_specific); + + switch (flag) { + case PED_PARTITION_BOOT: + return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_BOOTABLE)); + case PED_PARTITION_HIDDEN: + return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_NOMOUNT)); + case PED_PARTITION_RAID: + return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_RAID)); + case PED_PARTITION_LVM: + return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_LVM)); + default: + return 0; + } +} + +static int +amiga_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + case PED_PARTITION_HIDDEN: + case PED_PARTITION_RAID: + case PED_PARTITION_LVM: + return 1; + default: + return 0; + } +} + +static void +amiga_partition_set_name (PedPartition* part, const char* name) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL, return); + PED_ASSERT (part->disk_specific != NULL, return); + + partition = PART(part->disk_specific); + _amiga_set_bstr(name, partition->pb_DriveName, 32); +} +static const char* +amiga_partition_get_name (const PedPartition* part) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + partition = PART(part->disk_specific); + + return _amiga_get_bstr(partition->pb_DriveName); +} + +static PedConstraint* +_amiga_get_constraint (const PedDisk *disk) +{ + PedDevice *dev = disk->dev; + PedAlignment start_align, end_align; + PedGeometry max_geom; + struct RigidDiskBlock *rdb = RDSK(disk->disk_specific); + PedSector cyl_size = dev->hw_geom.sectors * dev->hw_geom.heads; + + if (!ped_alignment_init(&start_align, 0, cyl_size)) + return NULL; + if (!ped_alignment_init(&end_align, -1, cyl_size)) + return NULL; + if (!ped_geometry_init(&max_geom, dev, MAX_RDB_BLOCK + 1, + dev->length - MAX_RDB_BLOCK - 1)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, + &max_geom, &max_geom, 1, dev->length); +} + +static int +amiga_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + + if (_ped_partition_attempt_align (part, constraint, + _amiga_get_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +amiga_partition_enumerate (PedPartition* part) +{ + int i; + PedPartition* p; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk != NULL, return 0); + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + for (i = 1; i <= AMIGA_MAX_PARTITIONS; i++) { + p = ped_disk_get_partition (part->disk, i); + if (!p) { + part->num = i; + return 1; + } + } + + /* failed to allocate a number */ +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to allocate a partition number.")); +#endif + return 0; +} + +static int +amiga_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + PedConstraint* constraint_any = NULL; + PedSector highest_block; + + PED_ASSERT (disk != NULL, goto error); + PED_ASSERT (disk->dev != NULL, goto error); + + constraint_any = ped_constraint_any (disk->dev); + + /* Allocate space for the RDB */ + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + 0, MAX_RDB_BLOCK); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +amiga_get_max_primary_partition_count (const PedDisk* disk) +{ + return AMIGA_MAX_PARTITIONS; +} + +static PedDiskOps amiga_disk_ops = { + probe: amiga_probe, +#ifndef DISCOVER_ONLY + clobber: amiga_clobber, +#else + clobber: NULL, +#endif + alloc: amiga_alloc, + duplicate: amiga_duplicate, + free: amiga_free, + read: amiga_read, +#ifndef DISCOVER_ONLY + write: amiga_write, +#else + write: NULL, +#endif + + partition_new: amiga_partition_new, + partition_duplicate: amiga_partition_duplicate, + partition_destroy: amiga_partition_destroy, + partition_set_system: amiga_partition_set_system, + partition_set_flag: amiga_partition_set_flag, + partition_get_flag: amiga_partition_get_flag, + partition_is_flag_available: + amiga_partition_is_flag_available, + partition_set_name: amiga_partition_set_name, + partition_get_name: amiga_partition_get_name, + partition_align: amiga_partition_align, + partition_enumerate: amiga_partition_enumerate, + + + alloc_metadata: amiga_alloc_metadata, + get_max_primary_partition_count: + amiga_get_max_primary_partition_count +}; + +static PedDiskType amiga_disk_type = { + next: NULL, + name: "amiga", + ops: &amiga_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME +}; + +void +ped_disk_amiga_init () +{ + PED_ASSERT(sizeof(struct AmigaBlock) != 3, return); + PED_ASSERT(sizeof(struct RigidDiskBlock) != 64, return); + PED_ASSERT(sizeof(struct PartitionBlock) != 64, return); + PED_ASSERT(sizeof(struct LinkedBlock) != 5, return); + PED_ASSERT(sizeof(struct Linked2Block) != 18, return); + + ped_register_disk_type (&amiga_disk_type); +} + +void +ped_disk_amiga_done () +{ + ped_unregister_disk_type (&amiga_disk_type); +} diff --git a/libparted/labels/sun.c b/libparted/labels/sun.c new file mode 100644 index 0000000..6d9606c --- /dev/null +++ b/libparted/labels/sun.c @@ -0,0 +1,853 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2001, 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + + Contributor: Ben Collins <bcollins@debian.org> +*/ + +#include "config.h" + +#include <string.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* Most of this came from util-linux's sun support, which was mostly done + by Jakub Jelinek. */ + +#define SUN_DISK_MAGIC 0xDABE /* Disk magic number */ +#define SUN_DISK_MAXPARTITIONS 8 + +#define WHOLE_DISK_ID 0x05 +#define WHOLE_DISK_PART 2 /* as in 0, 1, 2 (3rd partition) */ +#define LINUX_SWAP_ID 0x82 + +typedef struct { + u_int32_t start_cylinder; /* where the part starts... */ + u_int32_t num_sectors; /* ...and it's length */ +} __attribute__ ((packed)) SunRawPartition; + +typedef struct { + u_int8_t spare1; + u_int8_t id; /* Partition type */ + u_int8_t spare2; + u_int8_t flags; /* Partition flags */ +} __attribute__ ((packed)) SunPartitionInfo; + +typedef struct { + char info[128]; /* Informative text string */ + u_int8_t spare0[14]; + SunPartitionInfo infos[SUN_DISK_MAXPARTITIONS]; + u_int8_t spare1[246]; /* Boot information etc. */ + u_int16_t rspeed; /* Disk rotational speed */ + u_int16_t pcylcount; /* Physical cylinder count */ + u_int16_t sparecyl; /* extra sects per cylinder */ + u_int8_t spare2[4]; /* More magic... */ + u_int16_t ilfact; /* Interleave factor */ + u_int16_t ncyl; /* Data cylinder count */ + u_int16_t nacyl; /* Alt. cylinder count */ + u_int16_t ntrks; /* Tracks per cylinder */ + u_int16_t nsect; /* Sectors per track */ + u_int8_t spare3[4]; /* Even more magic... */ + SunRawPartition partitions[SUN_DISK_MAXPARTITIONS]; + u_int16_t magic; /* Magic number */ + u_int16_t csum; /* Label xor'd checksum */ +} __attribute__ ((packed)) SunRawLabel; + +typedef struct { + u_int8_t type; + int is_boot; + int is_root; + int is_lvm; +} SunPartitionData; + +typedef struct { + PedSector length; /* This is based on cyl - alt-cyl */ + SunRawLabel raw_label; +} SunDiskData; + +static PedDiskType sun_disk_type; + +/* Checksum computation */ +static void +sun_compute_checksum (SunRawLabel *label) +{ + u_int16_t *ush = (u_int16_t *)label; + u_int16_t csum = 0; + + while(ush < (u_int16_t *)(&label->csum)) + csum ^= *ush++; + label->csum = csum; +} + +/* Checksum Verification */ +static int +sun_verify_checksum (SunRawLabel *label) +{ + u_int16_t *ush = ((u_int16_t *)(label + 1)) - 1; + u_int16_t csum = 0; + + while (ush >= (u_int16_t *)label) + csum ^= *ush--; + + return !csum; +} + +static int +sun_probe (const PedDevice *dev) +{ + PedDiskType* disk_type; + SunRawLabel label; + int i; + + PED_ASSERT (dev != NULL, return 0); + + if (dev->sector_size != 512) + return 0; + + if (!ped_device_read (dev, &label, 0, 1)) + return 0; + + /* check magic */ + if (PED_BE16_TO_CPU (label.magic) != SUN_DISK_MAGIC) + return 0; + +#ifndef DISCOVER_ONLY + if (!sun_verify_checksum(&label)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Corrupted Sun disk label detected.")); + return 0; + } +#endif + + return 1; +} + +#ifndef DISCOVER_ONLY +static int +sun_clobber (PedDevice* dev) +{ + SunRawLabel label; + + PED_ASSERT (dev != NULL, return 0); + PED_ASSERT (sun_probe (dev), return 0); + + if (!ped_device_read (dev, &label, 0, 1)) + return 0; + + label.magic = 0; + return ped_device_write (dev, &label, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + +static PedDisk* +sun_alloc (const PedDevice* dev) +{ + PedDisk* disk; + SunRawLabel* label; + SunDiskData* sun_specific; + PedCHSGeometry* bios_geom = &((PedDevice*)dev)->bios_geom; + PedSector cyl_size = bios_geom->sectors * bios_geom->heads; + + disk = _ped_disk_alloc (dev, &sun_disk_type); + if (!disk) + goto error; + + disk->disk_specific = (SunDiskData*) ped_malloc (sizeof (SunDiskData)); + if (!disk->disk_specific) + goto error_free_disk; + sun_specific = (SunDiskData*) disk->disk_specific; + + bios_geom->cylinders = dev->length / cyl_size; + sun_specific->length = bios_geom->cylinders * cyl_size; + + label = &sun_specific->raw_label; + memset(label, 0, sizeof(SunRawLabel)); + + /* #gentoo-sparc people agree that nacyl = 0 is the best option */ + label->magic = PED_CPU_TO_BE16 (SUN_DISK_MAGIC); + label->nacyl = 0; + label->pcylcount = PED_CPU_TO_BE16 (bios_geom->cylinders); + label->rspeed = PED_CPU_TO_BE16 (5400); + label->ilfact = PED_CPU_TO_BE16 (1); + label->sparecyl = 0; + label->ntrks = PED_CPU_TO_BE16 (bios_geom->heads); + label->nsect = PED_CPU_TO_BE16 (bios_geom->sectors); + label->ncyl = PED_CPU_TO_BE16 (bios_geom->cylinders - 0); + + /* Add a whole disk partition at a minimum */ + label->infos[WHOLE_DISK_PART].id = WHOLE_DISK_ID; + label->partitions[WHOLE_DISK_PART].start_cylinder = 0; + label->partitions[WHOLE_DISK_PART].num_sectors = + PED_CPU_TO_BE32(bios_geom->cylinders * cyl_size); + + /* Now a neato string to describe this label */ + snprintf(label->info, sizeof(label->info) - 1, + "GNU Parted Custom cyl %d alt %d hd %d sec %d", + PED_BE16_TO_CPU(label->ncyl), + PED_BE16_TO_CPU(label->nacyl), + PED_BE16_TO_CPU(label->ntrks), + PED_BE16_TO_CPU(label->nsect)); + + sun_compute_checksum(label); + return disk; + +error_free_disk: + _ped_disk_free (disk); +error: + return NULL; +} + +static PedDisk* +sun_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + SunDiskData* new_sun_data; + SunDiskData* old_sun_data = (SunDiskData*) disk->disk_specific; + + new_disk = ped_disk_new_fresh (disk->dev, &sun_disk_type); + if (!new_disk) + return NULL; + + new_sun_data = (SunDiskData*) new_disk->disk_specific; + memcpy (new_sun_data, old_sun_data, sizeof (SunDiskData)); + return new_disk; +} + +static void +sun_free (PedDisk *disk) +{ + ped_free (disk->disk_specific); + _ped_disk_free (disk); +} + +static int +_check_geometry_sanity (PedDisk* disk, SunRawLabel* label) +{ + PedDevice* dev = disk->dev; + + if (PED_BE16_TO_CPU(label->nsect) == dev->hw_geom.sectors && + PED_BE16_TO_CPU(label->ntrks) == dev->hw_geom.heads) + dev->bios_geom = dev->hw_geom; + + if (PED_BE16_TO_CPU(label->nsect) != dev->bios_geom.sectors || + PED_BE16_TO_CPU(label->ntrks) != dev->bios_geom.heads) { +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The disk CHS geometry (%d,%d,%d) reported " + "by the operating system does not match " + "the geometry stored on the disk label " + "(%d,%d,%d)."), + dev->bios_geom.cylinders, + dev->bios_geom.heads, + dev->bios_geom.sectors, + PED_BE16_TO_CPU(label->pcylcount), + PED_BE16_TO_CPU(label->ntrks), + PED_BE16_TO_CPU(label->nsect)) + == PED_EXCEPTION_CANCEL) + return 0; +#endif + dev->bios_geom.sectors = PED_BE16_TO_CPU(label->nsect); + dev->bios_geom.heads = PED_BE16_TO_CPU(label->ntrks); + dev->bios_geom.cylinders = PED_BE16_TO_CPU(label->pcylcount); + + if (dev->bios_geom.sectors * dev->bios_geom.heads + * dev->bios_geom.cylinders > dev->length) { + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The disk label describes a disk bigger than " + "%s."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + } + } + return 1; +} + +static int +sun_read (PedDisk* disk) +{ + SunRawLabel* label; + SunPartitionData* sun_data; + SunDiskData* disk_data; + int i; + PedPartition* part; + PedSector end, start, block; + PedConstraint* constraint_exact; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + PED_ASSERT (disk->disk_specific != NULL, return 0); + + disk_data = (SunDiskData*) disk->disk_specific; + label = &disk_data->raw_label; + + ped_disk_delete_all (disk); + + if (!ped_device_read (disk->dev, label, 0, 1)) + goto error; + if (!_check_geometry_sanity (disk, label)) + goto error; + + block = disk->dev->bios_geom.sectors * disk->dev->bios_geom.heads; + disk_data->length = block * disk->dev->bios_geom.cylinders; + + for (i = 0; i < SUN_DISK_MAXPARTITIONS; i++) { + if (!PED_BE32_TO_CPU(label->partitions[i].num_sectors)) + continue; + if (!label->infos[i].id) + continue; + if (label->infos[i].id == WHOLE_DISK_ID) + continue; + + start = PED_BE32_TO_CPU(label->partitions[i].start_cylinder) + * block; + end = start + + PED_BE32_TO_CPU(label->partitions[i].num_sectors) - 1; + + part = ped_partition_new (disk, 0, NULL, start, end); + if (!part) + goto error; + + sun_data = part->disk_specific; + sun_data->type = label->infos[i].id; + sun_data->is_boot = sun_data->type == 0x1; + sun_data->is_root = sun_data->type == 0x2; + sun_data->is_lvm = sun_data->type == 0x8e; + + part->num = i + 1; + part->fs_type = ped_file_system_probe (&part->geom); + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) + goto error; + ped_constraint_destroy (constraint_exact); + } + + return 1; + + error: + return 0; +} + +#ifndef DISCOVER_ONLY +static void +_probe_and_use_old_info (PedDisk* disk) +{ + SunDiskData* sun_specific; + SunRawLabel old_label; + + sun_specific = (SunDiskData*) disk->disk_specific; + + if (!ped_device_read (disk->dev, &old_label, 0, 1)) + return; + if (old_label.info [0] + && PED_BE16_TO_CPU (old_label.magic) == SUN_DISK_MAGIC) + memcpy (&sun_specific->raw_label, &old_label, 512); +} + +static int +sun_write (PedDisk* disk) +{ + SunRawLabel* label; + SunPartitionData* sun_data; + SunDiskData* disk_data; + PedPartition* part; + int i; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + _probe_and_use_old_info (disk); + + disk_data = (SunDiskData*) disk->disk_specific; + label = &disk_data->raw_label; + + memset (label->partitions, 0, + sizeof (SunRawPartition) * SUN_DISK_MAXPARTITIONS); + memset (label->infos, 0, + sizeof (SunPartitionInfo) * SUN_DISK_MAXPARTITIONS); + + for (i = 0; i < SUN_DISK_MAXPARTITIONS; i++) { + part = ped_disk_get_partition (disk, i + 1); + + if (!part && i == WHOLE_DISK_PART) { + /* Ok, nothing explicitly in the whole disk + partition, so let's put it there for safety + sake. */ + + label->infos[i].id = WHOLE_DISK_ID; + label->partitions[i].start_cylinder = 0; + label->partitions[i].num_sectors = + PED_CPU_TO_BE32(disk_data->length); + continue; + } + if (!part) + continue; + + sun_data = part->disk_specific; + label->infos[i].id = sun_data->type; + label->partitions[i].start_cylinder + = PED_CPU_TO_BE32 (part->geom.start + / (disk->dev->bios_geom.sectors + * disk->dev->bios_geom.heads)); + label->partitions[i].num_sectors + = PED_CPU_TO_BE32 (part->geom.end + - part->geom.start + 1); + } + + /* We assume the harddrive is always right, and that the label may + be wrong. I don't think this will cause any problems, since the + cylinder count is always enforced by our alignment, and we + sanity checked the sectors/heads when we detected the device. The + worst that could happen here is that the drive seems bigger or + smaller than it really is, but we'll have that problem even if we + don't do this. */ + + if (disk->dev->bios_geom.cylinders > 65536) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("The disk has %d cylinders, which is greater than " + "the maximum of 65536."), + disk->dev->bios_geom.cylinders); + } + + label->pcylcount = PED_CPU_TO_BE16 (disk->dev->bios_geom.cylinders); + label->ncyl = PED_CPU_TO_BE16 (disk->dev->bios_geom.cylinders + - PED_BE16_TO_CPU (label->nacyl)); + + sun_compute_checksum (label); + + if (!ped_device_write (disk->dev, label, 0, 1)) + goto error; + return ped_device_sync (disk->dev); + +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +sun_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + SunPartitionData* sun_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = sun_data = ped_malloc (sizeof (SunPartitionData)); + if (!sun_data) + goto error_free_part; + sun_data->type = 0; + sun_data->is_boot = 0; + sun_data->is_root = 0; + sun_data->is_lvm = 0; + } else { + part->disk_specific = NULL; + } + + return part; + +error_free_sun_data: + ped_free (sun_data); +error_free_part: + ped_free (part); +error: + return NULL; +} + +static PedPartition* +sun_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + SunPartitionData* new_sun_data; + SunPartitionData* old_sun_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_sun_data = (SunPartitionData*) part->disk_specific; + new_sun_data = (SunPartitionData*) new_part->disk_specific; + new_sun_data->type = old_sun_data->type; + new_sun_data->is_boot = old_sun_data->is_boot; + new_sun_data->is_root = old_sun_data->is_root; + new_sun_data->is_lvm = old_sun_data->is_lvm; + return new_part; +} + +static void +sun_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL, return); + + if (ped_partition_is_active (part)) + ped_free (part->disk_specific); + ped_free (part); +} + +static int +sun_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + SunPartitionData* sun_data = part->disk_specific; + + part->fs_type = fs_type; + + if (sun_data->is_boot) { + sun_data->type = 0x1; + return 1; + } + if (sun_data->is_root) { + sun_data->type = 0x2; + return 1; + } + if (sun_data->is_lvm) { + sun_data->type = 0x8e; + return 1; + } + + sun_data->type = 0x83; + if (fs_type) { + if (!strcmp (fs_type->name, "linux-swap")) + sun_data->type = 0x82; + else if (!strcmp (fs_type->name, "ufs")) + sun_data->type = 0x6; + } + + return 1; +} + +static int +sun_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + SunPartitionData* sun_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + PED_ASSERT (ped_partition_is_flag_available (part, flag), return 0); + + sun_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + sun_data->is_boot = state; + if (state) + sun_data->is_root = sun_data->is_lvm = 0; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_ROOT: + sun_data->is_root = state; + if (state) + sun_data->is_boot = sun_data->is_lvm = 0; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_LVM: + sun_data->is_lvm = state; + if (state) + sun_data->is_root = sun_data->is_boot = 0; + return ped_partition_set_system (part, part->fs_type); + + default: + return 0; + } +} + + +static int +sun_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + SunPartitionData* sun_data; + + PED_ASSERT (part != NULL, return 0); + PED_ASSERT (part->disk_specific != NULL, return 0); + + sun_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + return sun_data->is_boot; + case PED_PARTITION_ROOT: + return sun_data->is_root; + case PED_PARTITION_LVM: + return sun_data->is_lvm; + + default: + return 0; + } +} + + +static int +sun_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + case PED_PARTITION_ROOT: + case PED_PARTITION_LVM: + return 1; + + default: + return 0; + } +} + + +static int +sun_get_max_primary_partition_count (const PedDisk* disk) +{ + return SUN_DISK_MAXPARTITIONS; +} + +static PedConstraint* +_get_strict_constraint (PedDisk* disk) +{ + PedDevice* dev = disk->dev; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + SunDiskData* disk_data = disk->disk_specific; + PedSector block = dev->bios_geom.sectors * dev->bios_geom.heads; + + if (!ped_alignment_init (&start_align, 0, block)) + return NULL; + if (!ped_alignment_init (&end_align, -1, block)) + return NULL; + if (!ped_geometry_init (&max_geom, dev, 0, disk_data->length)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, dev->length); +} + +static PedConstraint* +_get_lax_constraint (PedDisk* disk) +{ + PedDevice* dev = disk->dev; + PedAlignment start_align; + PedGeometry max_geom; + SunDiskData* disk_data = disk->disk_specific; + PedSector block = dev->bios_geom.sectors * dev->bios_geom.heads; + + if (!ped_alignment_init (&start_align, 0, block)) + return NULL; + if (!ped_geometry_init (&max_geom, dev, 0, disk_data->length)) + return NULL; + + return ped_constraint_new (&start_align, ped_alignment_any, &max_geom, + &max_geom, 1, dev->length); +} + +/* _get_strict_constraint() will align the partition to the end of the cylinder. + * This isn't required, but since partitions must start at the start of the + * cylinder, space between the end of a partition and the end of a cylinder + * is unusable, so there's no point wasting space! + * However, if they really insist (via constraint)... which they will + * if they're reading a weird table of the disk... then we allow the end to + * be anywhere, with _get_lax_constraint() + */ +static int +sun_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL, return 0); + + if (_ped_partition_attempt_align (part, constraint, + _get_strict_constraint (part->disk))) + return 1; + if (_ped_partition_attempt_align (part, constraint, + _get_lax_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +sun_partition_enumerate (PedPartition* part) +{ + int i; + PedPartition* p; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + for (i = 1; i <= SUN_DISK_MAXPARTITIONS; i++) { + /* skip the Whole Disk partition for now */ + if (i == WHOLE_DISK_PART + 1) + continue; + p = ped_disk_get_partition (part->disk, i); + if (!p) { + part->num = i; + return 1; + } + } + +#ifndef DISCOVER_ONLY + /* Ok, now allocate the Whole disk if it isn't already */ + p = ped_disk_get_partition (part->disk, WHOLE_DISK_PART + 1); + if (!p) { + int i = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The Whole Disk partition is the only " + "available one left. Generally, it is not a " + "good idea to overwrite this partition with " + "a real one. Solaris may not be able to " + "boot without it, and SILO (the sparc boot " + "loader) appreciates it as well.")); + if (i == PED_EXCEPTION_IGNORE) { + /* bad bad bad...you will suffer your own fate */ + part->num = WHOLE_DISK_PART + 1; + return 1; + } + } + + /* failed to allocate a number, this means we are full */ + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Sun disk label is full.")); +#endif + return 0; +} + +static int +sun_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + SunDiskData* disk_data; + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL, return 0); + PED_ASSERT (disk->disk_specific != NULL, return 0); + PED_ASSERT (disk->dev != NULL, return 0); + + constraint_any = ped_constraint_any (disk->dev); + + /* Sun disk label does not need to allocate a sector. The disk + label is contained within the first 512 bytes, which should not + be overwritten by any boot loader or superblock. It is safe for + most partitions to start at sector 0. We do however, allocate + the space used by alt-cyl's, since we cannot use those. Put them + at the end of the disk. */ + + disk_data = disk->disk_specific; + + if (disk->dev->length <= 0 || + disk_data->length <= 0 || + disk->dev->length == disk_data->length) + goto error; + + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + disk_data->length, disk->dev->length - 1); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static PedDiskOps sun_disk_ops = { + probe: sun_probe, +#ifndef DISCOVER_ONLY + clobber: sun_clobber, +#else + clobber: NULL, +#endif + alloc: sun_alloc, + duplicate: sun_duplicate, + free: sun_free, + read: sun_read, +#ifndef DISCOVER_ONLY + write: sun_write, +#else + write: NULL, +#endif + + partition_new: sun_partition_new, + partition_duplicate: sun_partition_duplicate, + partition_destroy: sun_partition_destroy, + partition_set_system: sun_partition_set_system, + partition_set_flag: sun_partition_set_flag, + partition_get_flag: sun_partition_get_flag, + partition_is_flag_available: sun_partition_is_flag_available, + partition_align: sun_partition_align, + partition_enumerate: sun_partition_enumerate, + alloc_metadata: sun_alloc_metadata, + get_max_primary_partition_count: + sun_get_max_primary_partition_count, + + partition_set_name: NULL, + partition_get_name: NULL, +}; + +static PedDiskType sun_disk_type = { + next: NULL, + name: "sun", + ops: &sun_disk_ops, + features: 0 +}; + +void +ped_disk_sun_init () +{ + PED_ASSERT (sizeof (SunRawLabel) == 512, return); + ped_register_disk_type (&sun_disk_type); +} + +void +ped_disk_sun_done () +{ + ped_unregister_disk_type (&sun_disk_type); +} + diff --git a/libparted/labels/vtoc.c b/libparted/labels/vtoc.c new file mode 100644 index 0000000..ad25bbe --- /dev/null +++ b/libparted/labels/vtoc.c @@ -0,0 +1,1162 @@ +#include <parted/vtoc.h> + +#ifdef DEBUG_DASD +#define PDEBUG fprintf(stderr, "%s:%d:%s\n", \ + __FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__); +#else +#define PDEBUG +#endif + +#include <parted/parted.h> + +#include <libintl.h> +#if ENABLE_NLS +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static unsigned char EBCtoASC[256] = +{ +/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, +/* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, +/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, +/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, +/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, +/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, +/* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, +/* 0x48 . < ( + | */ + 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, +/* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, +/* 0x58 ß ! $ * ) ; */ + 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, +/* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, +/* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, +/* 0x70 --- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +/* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, +/* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, +/* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, +/* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, +/* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, +/* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, +/* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, +/* 0xB0 ^ ---- § ---- */ + 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, +/* 0xB8 ---- [ ] ---- ---- ---- ---- */ + 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, +/* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, +/* 0xC8 H I ---- ö ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, +/* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, +/* 0xD8 Q R ---- ü */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, +/* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, +/* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, +/* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; + +static unsigned char ASCtoEBC[256] = +{ + /*00 NL SH SX EX ET NQ AK BL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DL D1 D2 D3 D4 NK SN EB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x15, 0x32, 0x26, + /*18 CN EM SB EC FS GS RS US */ + 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF +}; + +enum failure { + unable_to_open, + unable_to_seek, + unable_to_write, + unable_to_read +}; + +static char buffer[85]; + +static void +vtoc_error (enum failure why, char *s1, char *s2) +{ + PDEBUG + char error[8192]; + + switch (why) { + case unable_to_open: + sprintf(error, _("%s opening device '%s' failed.\n%s\n"), + VTOC_ERROR, s1, s2); + break; + case unable_to_seek: + sprintf(error, _("%s seeking device '%s' failed.\n%s\n"), + VTOC_ERROR, s1, s2); + break; + case unable_to_write: + sprintf(error, _("%s writing to device '%s' failed,\n%s\n"), + VTOC_ERROR, s1, s2); + break; + case unable_to_read: + sprintf(error, _("%s reading from device '%s' failed.\n%s\n"), + VTOC_ERROR, s1, s2); + break; + default: + sprintf(error, _("Fatal error\n")); + } + + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, error); +} + +char * +vtoc_ebcdic_enc (char source[LINE_LENGTH], + char target[LINE_LENGTH], + int l) +{ + PDEBUG + int i; + + for (i = 0; i < l; i++) + target[i]=ASCtoEBC[(unsigned char)(source[i])]; + + return target; +} + +char * +vtoc_ebcdic_dec (char source[LINE_LENGTH], + char target[LINE_LENGTH], + int l) +{ + PDEBUG + int i; + + for (i = 0; i < l; i++) + target[i]=EBCtoASC[(unsigned char)(source[i])]; + + return target; +} + +void +vtoc_set_extent (extent_t *ext, u_int8_t typeind, u_int8_t seqno, + cchh_t *lower, cchh_t *upper) +{ + PDEBUG + ext->typeind = typeind; + ext->seqno = seqno; + memcpy(&ext->llimit,lower,sizeof(cchh_t)); + memcpy(&ext->ulimit,upper,sizeof(cchh_t)); +} + +void +vtoc_set_cchh (cchh_t *addr, u_int16_t cc, u_int16_t hh) +{ + PDEBUG + addr->cc = cc; + addr->hh = hh; +} + +static void +vtoc_set_ttr (ttr_t *addr, u_int16_t tt, u_int8_t r) +{ + PDEBUG + addr->tt = tt; + addr->r = r; +} + +void +vtoc_set_cchhb (cchhb_t *addr, u_int16_t cc, u_int16_t hh, u_int8_t b) +{ + PDEBUG + addr->cc = cc; + addr->hh = hh; + addr->b = b; +} + +void +vtoc_set_date (labeldate_t * d, u_int8_t year, u_int16_t day) +{ + PDEBUG + d->year = year; + d->day = day; +} + +/* + * initializes the volume label with EBCDIC spaces + */ +void +vtoc_volume_label_init (volume_label_t *vlabel) +{ + PDEBUG + sprintf(buffer, "%84s", " "); + vtoc_ebcdic_enc(buffer, buffer, 84); + strncpy(vlabel->volkey, buffer, 84); +} + +/* + * reads the volume label from dasd + */ +int +vtoc_read_volume_label (int f, unsigned long vlabel_start, + volume_label_t *vlabel) +{ + PDEBUG + int rc; + + if (lseek(f, vlabel_start, SEEK_SET) == -1) { + vtoc_error(unable_to_seek, "vtoc_read_volume_label", + "Could not read volume label."); + return 1; + } + + rc = read(f, vlabel, sizeof(volume_label_t)); + if (rc != sizeof(volume_label_t)) { + vtoc_error(unable_to_read, "vtoc_read_volume_label", + "Could not read volume label."); + return 1; + } + + return 0; +} + +/* + * writes the volume label to dasd + */ +int +vtoc_write_volume_label (int f, unsigned long vlabel_start, + volume_label_t *vlabel) +{ + PDEBUG + int rc; + + if (lseek(f, vlabel_start, SEEK_SET) == -1) + vtoc_error(unable_to_seek, "vtoc_write_volume_label", + "Could not write volume label."); + + rc = write(f, vlabel, sizeof(volume_label_t)); + if (rc != sizeof(volume_label_t)) + vtoc_error(unable_to_write, "vtoc_write_volume_label", + "Could not write volume label."); + + return 0; +} + +/* + * takes a string as input, converts it to uppercase, translates + * it to EBCDIC and fills it up with spaces before it copies it + * as volume serial to the volume label + */ +void +vtoc_volume_label_set_volser (volume_label_t *vlabel, char *volser) +{ + PDEBUG + int j, i = strlen(volser); + char s[VOLSER_LENGTH + 1]; + + strcpy(s, " "); + vtoc_ebcdic_enc(s, s, VOLSER_LENGTH); + strncpy(vlabel->volid, s, VOLSER_LENGTH); + + if (i > VOLSER_LENGTH) + i = VOLSER_LENGTH; + + strncpy(s, volser, i); + for (j=0; j<i; j++) + s[j] = toupper(s[j]); + + s[VOLSER_LENGTH] = 0x00; + vtoc_ebcdic_enc(s, s, i); + strncpy(vlabel->volid, s, i); + + return; +} + +/* + * returns the volume serial number right after it is translated + * to ASCII + */ +char * +vtoc_volume_label_get_volser (volume_label_t *vlabel, char *volser) +{ + PDEBUG + vtoc_ebcdic_dec(vlabel->volid, volser, VOLSER_LENGTH); + + return volser; +} + +/* + * sets the volume label key right after + * it has been translated to EBCDIC + */ +void +vtoc_volume_label_set_key (volume_label_t *vlabel, char *key) +{ + PDEBUG + char s[4]; + + vtoc_ebcdic_enc(key, s, 4); + strncpy(vlabel->volkey, s, 4); + + return; +} + +/* + * sets the volume label identifier right + * after it has been translated to EBCDIC + */ +void +vtoc_volume_label_set_label (volume_label_t *vlabel, char *lbl) +{ + PDEBUG + char s[4]; + + vtoc_ebcdic_enc(lbl, s, 4); + strncpy(vlabel->vollbl, s, 4); + + return; +} + +/* + * returns the volume label key = the label identifier + * right after it has been translated to ASCII + */ +char * +vtoc_volume_label_get_label (volume_label_t *vlabel, char *lbl) +{ + PDEBUG + vtoc_ebcdic_dec(vlabel->vollbl, lbl, 4); + + return lbl; +} + +/* + * reads either a format4 label or a format1 label + * from the specified position + */ +void +vtoc_read_label (int f, unsigned long position, format1_label_t *f1, + format4_label_t *f4, format5_label_t *f5, format7_label_t *f7) +{ + PDEBUG + int t; + + if (lseek(f, position, SEEK_SET) == -1) + vtoc_error(unable_to_seek, "vtoc_read_label", + _("Could not read VTOC labels.")); + + if (f1 != NULL) { + t = sizeof(format1_label_t); + if (read(f, f1, t) != t) + vtoc_error(unable_to_read, "vtoc_read_label", + _("Could not read VTOC FMT1 DSCB.")); + } + + if (f4 != NULL) { + t = sizeof(format4_label_t); + if (read(f, f4, t) != t) + vtoc_error(unable_to_read, "vtoc_read_label", + _("Could not read VTOC FMT4 DSCB.")); + } + + if (f5 != NULL) { + t = sizeof(format5_label_t); + if (read(f, f5, t) != t) + vtoc_error(unable_to_read, "vtoc_read_label", + _("Could not read VTOC FMT5 DSCB.")); + } + + if (f7 != NULL) { + t = sizeof(format7_label_t); + if (read(f, f7, t) != t) + vtoc_error(unable_to_read, "vtoc_read_label", + _("Could not read VTOC FMT7 DSCB.")); + } +} + +/* + * writes either a FMT1, FMT4 or FMT5 label + * to the specified position + */ +void +vtoc_write_label (int f, unsigned long position, format1_label_t *f1, + format4_label_t *f4, format5_label_t *f5, format7_label_t *f7) +{ + PDEBUG + int t; + + if (lseek(f, position, SEEK_SET) == -1) + vtoc_error(unable_to_seek, "vtoc_write_label", + _("Could not write VTOC labels.")); + + if (f1 != NULL) { + t = sizeof(format1_label_t); + if (write(f, f1, t) != t) + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT1 DSCB.")); + } + + if (f4 != NULL) { + t = sizeof(format4_label_t); + if (write(f, f4, t) != t) + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT4 DSCB.")); + } + + if (f5 != NULL) { + t = sizeof(format5_label_t); + if (write(f, f5, t) != t) + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT5 DSCB.")); + } + + if (f7 != NULL) { + t = sizeof(format7_label_t); + if (write(f, f7, t) != t) + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT7 DSCB.")); + } +} + +/* + * initializes a format4 label + */ +void +vtoc_init_format4_label (format4_label_t *f4, unsigned int usable_partitions, + unsigned int cylinders, unsigned int tracks, + unsigned int blocks, unsigned int blksize, + u_int16_t dev_type) +{ + PDEBUG + int i; + + cchh_t lower = {VTOC_START_CC, VTOC_START_HH}; + cchh_t upper = {VTOC_START_CC, VTOC_START_HH}; + + for (i=0; i<44; i++) f4->DS4KEYCD[i] = 0x04; + f4->DS4IDFMT = 0xf4; + + vtoc_set_cchhb(&f4->DS4HPCHR, 0x0000, 0x0000, 0x00); + f4->DS4DSREC = blocks - 2; + /* free space starts right behind VTOC + vtoc_set_cchh(&f4->DS4HCCHH, VTOC_START_CC, VTOC_START_HH + 1);*/ + vtoc_set_cchh(&f4->DS4HCCHH, 0x0000, 0x0000); + f4->DS4NOATK = 0x0000; + f4->DS4VTOCI = 0x00; + f4->DS4NOEXT = 0x01; + f4->DS4SMSFG = 0x00; + f4->DS4DEVAC = 0x00; + + /* -- begin f4->DS4DEVCT -- */ + f4->DS4DEVCT.DS4DSCYL = cylinders; + f4->DS4DEVCT.DS4DSTRK = tracks; + + switch (dev_type) { + case DASD_3380_TYPE: + f4->DS4DEVCT.DS4DEVTK = DASD_3380_VALUE; + break; + case DASD_3390_TYPE: + f4->DS4DEVCT.DS4DEVTK = DASD_3390_VALUE; + break; + case DASD_9345_TYPE: + f4->DS4DEVCT.DS4DEVTK = DASD_9345_VALUE; + break; + default: + f4->DS4DEVCT.DS4DEVTK = blocks * blksize;; + } + + f4->DS4DEVCT.DS4DEVI = 0x00; + f4->DS4DEVCT.DS4DEVL = 0x00; + f4->DS4DEVCT.DS4DEVK = 0x00; + f4->DS4DEVCT.DS4DEVFG = 0x30; + f4->DS4DEVCT.DS4DEVTL = 0x0000; + f4->DS4DEVCT.DS4DEVDT = blocks; + f4->DS4DEVCT.DS4DEVDB = 0x00; + /* -- end f4->DS4DEVCT -- */ + + bzero(f4->DS4AMTIM, sizeof(f4->DS4AMTIM)); + bzero(f4->DS4AMCAT, sizeof(f4->DS4AMCAT)); + bzero(f4->DS4R2TIM, sizeof(f4->DS4R2TIM)); + bzero(f4->res1, sizeof(f4->res1)); + bzero(f4->DS4F6PTR, sizeof(f4->DS4F6PTR)); + + /* -- begin f4lbl->DS4VTOCE -- */ + vtoc_set_extent(&f4->DS4VTOCE, 0x01, 0x00, &lower, &upper); + /* -- end f4lbl->DS4VTOCE -- */ + + bzero(f4->res2, sizeof(f4->res2)); + f4->DS4EFLVL = 0x00; + bzero(&f4->DS4EFPTR, sizeof(f4->DS4EFPTR)); + bzero(f4->res3, sizeof(f4->res3)); +} + +/* + * initializes a format5 label + */ +void +vtoc_init_format5_label (format5_label_t *f5) +{ + PDEBUG + int i; + + bzero(f5, sizeof(format5_label_t)); + for (i=0; i<4; i++) + f5->DS5KEYID[i] = 0x05; + f5->DS5FMTID = 0xf5; +} + +/* + * initializes a format7 label + */ +void +vtoc_init_format7_label (format7_label_t *f7) +{ + PDEBUG + int i; + + bzero(f7, sizeof(format7_label_t)); + for (i=0; i<4; i++) + f7->DS7KEYID[i] = 0x07; + f7->DS7FMTID = 0xf7; +} + +/* + * initializes a format1 label + */ +void +vtoc_init_format1_label (char *volid, unsigned int blksize, + extent_t *part_extent, format1_label_t *f1) +{ + PDEBUG + struct tm * creatime; + time_t t; + char str[80]; + + /* get actual date */ + t = time(NULL); + creatime = gmtime(&t); + + bzero(f1->DS1DSNAM, sizeof(f1->DS1DSNAM)); + sprintf(str, "PART .NEW "); + vtoc_ebcdic_enc(str, str, 44); + strncpy(f1->DS1DSNAM, str, 44); + f1->DS1FMTID = 0xf1; + strncpy(f1->DS1DSSN, " ", 6); + f1->DS1VOLSQ = 0x0001; + + vtoc_set_date(&f1->DS1CREDT, (u_int8_t) creatime->tm_year, + (u_int16_t) creatime->tm_yday); + /* expires never - 99 365 */ + vtoc_set_date(&f1->DS1EXPDT, 0x63, 0x016D); + f1->DS1NOEPV = 0x01; + f1->DS1NOBDB = 0x00; + f1->DS1FLAG1 = 0x00; + vtoc_ebcdic_enc("IBM LINUX ", str, 13); + strncpy(f1->DS1SYSCD, str, 13); + vtoc_set_date(&f1->DS1REFD, (u_int8_t) creatime->tm_year, + (u_int16_t) creatime->tm_yday); + f1->DS1SMSFG = 0x00; + f1->DS1SCXTF = 0x00; + f1->DS1SCXTV = 0x0000; + f1->DS1DSRG1 = 0x00; + f1->DS1DSRG2 = 0x00; + f1->DS1RECFM = 0x88; + f1->DS1OPTCD = 0x00; + f1->DS1BLKL = blksize; + f1->DS1LRECL = blksize; + f1->DS1KEYL = 0x00; + f1->DS1RKP = 0x0000; + f1->DS1DSIND = 0x80; /* last volume for this dataset */ + f1->DS1SCAL1 = 0x80; + bzero(&f1->DS1SCAL3, sizeof(f1->DS1SCAL3)); + vtoc_set_ttr(&f1->DS1LSTAR, 0x0000, 0x00); + f1->DS1TRBAL = 0x00; + bzero(&f1->res1, sizeof(f1->res1)); + memcpy(&f1->DS1EXT1, part_extent, sizeof(extent_t)); + bzero(&f1->DS1EXT2, sizeof(extent_t)); + bzero(&f1->DS1EXT3, sizeof(extent_t)); + vtoc_set_cchhb(&f1->DS1PTRDS, 0x0000, 0x0000, 0x00); +} + +/* + * do some updates to the VTOC format4 label + */ +void +vtoc_update_format4_label (format4_label_t *f4, cchhb_t *highest_f1, + u_int16_t unused_update) +{ + PDEBUG + /* update highest address of a format 1 label */ + memcpy(&f4->DS4HPCHR, highest_f1, sizeof(cchhb_t)); + + /* update unused DSCB count */ + f4->DS4DSREC = unused_update; +} + +/* + * reorganizes all extents within a FMT5 label + */ +static void +vtoc_reorganize_FMT5_extents (format5_label_t *f5) +{ + PDEBUG + ds5ext_t *ext, *last, tmp; + int i, j; + + for (i=0; i<26; i++) { + if (i==0) + last = &f5->DS5AVEXT; + else if ((i > 0) && (i < 8)) + last = &f5->DS5EXTAV[i-1]; + else + last = &f5->DS5MAVET[i-8]; + + for (j=i; j<26; j++) { + if (j==0) + ext = &f5->DS5AVEXT; + else if ((j > 0) && (j < 8)) + ext = &f5->DS5EXTAV[j-1]; + else + ext = &f5->DS5MAVET[j-8]; + + if (((ext->t > 0) && (last->t == 0)) || + ((ext->t > 0) && (ext->t < last->t))) + { + tmp.t = last->t; + tmp.fc = last->fc; + tmp.ft = last->ft; + last->t = ext->t; + last->fc = ext->fc; + last->ft = ext->ft; + ext->t = tmp.t; + ext->fc = tmp.fc; + ext->ft = tmp.ft; + } + } + } +} + +/* + * add a free space extent description to the VTOC FMT5 DSCB + */ +void +vtoc_update_format5_label_add (format5_label_t *f5, int verbose, int cyl, + int trk, u_int16_t a, u_int16_t b, u_int8_t c) +{ + PDEBUG + ds5ext_t *ext = NULL, *tmp = NULL; + int i; + + for (i=0; i<26; i++) { + if (i==0) + ext = &f5->DS5AVEXT; + else if ((i > 0) && (i < 8)) + ext = &f5->DS5EXTAV[i-1]; + else + ext = &f5->DS5MAVET[i-8]; + + if (((a < ext->t) && (a + b*trk + c > ext->t)) || + ((a > ext->t) && (ext->t + ext->fc*trk + ext->ft > a))) + { + printf("BUG: overlapping free space extents " \ + "in FMT5 DSCB!\nexiting...\n"); + exit(1); + } + + if ((ext->t + ext->fc + ext->ft) == 0x0000) { + ext->t = a; + ext->fc = b; + ext->ft = c; + tmp = ext; + if (verbose) + printf("FMT5 add extent: " \ + "add new extent\n"); + break; + } + } + + if (tmp == NULL) { + /* BUG: no free extent found */ + printf("BUG: no free FMT5 DSCB extent found!\nexiting...\n"); + exit(1); + } + + for (i=0; i<26; i++) { + if (i==0) + ext = &f5->DS5AVEXT; + else if ((i > 0) && (i < 8)) + ext = &f5->DS5EXTAV[i-1]; + else + ext = &f5->DS5MAVET[i-8]; + + if ((ext->t + ext->fc + ext->ft) == 0x0000) + continue; + + if ((ext->t + ext->fc*trk + ext->ft) == tmp->t) { + /* this extent precedes the new one */ + ext->fc += (tmp->fc + (tmp->ft + ext->ft)/trk); + ext->ft = (tmp->ft + ext->ft) % trk; + bzero(tmp, sizeof(ds5ext_t)); + tmp = ext; + + if (verbose) + printf("FMT5 add extent: " \ + "merge with predecessor\n"); + + i = -1; + continue; + } + + if ((tmp->t + tmp->fc*trk + tmp->ft) == ext->t) { + /* this extent succeeds the new one */ + ext->t = tmp->t; + ext->fc += (tmp->fc + (tmp->ft + ext->ft)/trk); + ext->ft = (tmp->ft + ext->ft) % trk; + bzero(tmp, sizeof(ds5ext_t)); + tmp = ext; + + if (verbose) + printf("FMT5 add extent: " \ + "merge with successor\n"); + + i = -1; + continue; + } + } +} + +/* + * remove a free space extent description from the VTOC FMT5 DSCB + */ +void +vtoc_update_format5_label_del (format5_label_t *f5, int verbose, int cyl, + int trk, u_int16_t a, u_int16_t b, u_int8_t c) +{ + PDEBUG + ds5ext_t *ext; + int i, counter=0; + + for (i=0; i<26; i++) { + if (i==0) + ext = &f5->DS5AVEXT; + else if ((i > 0) && (i < 8)) + ext = &f5->DS5EXTAV[i-1]; + else + ext = &f5->DS5MAVET[i-8]; + + if ((a == ext->t) && (b == ext->fc) && (c == ext->ft)) { + /* fills up whole free space gap */ + bzero(ext, sizeof(ds5ext_t)); + + if (verbose) + printf("FMT5 del extent: fills whole gap\n"); + + counter++; + break; + } + + if ((a == ext->t) && ((b < ext->fc) || (c < ext->ft))) { + /* left-bounded in free space gap */ + ext->t = ext->t + b*trk + c; + + if (c > ext->ft) { + ext->fc -= (b + 1); + ext->ft -= (c - trk); + } else { + ext->fc -= b; + ext->ft -= c; + } + + if (verbose) + printf("FMT5 del extent: left bounded\n"); + + counter++; + break; + } + + if ((ext->t < a) + && ((ext->t + ext->fc*trk + ext->ft) == (a + b*trk + c))) + { + /* right-bounded in free space gap */ + if (c > ext->ft) { + ext->fc -= (b + 1); + ext->ft -= (c - trk); + } else { + ext->fc -= b; + ext->ft -= c; + } + + if (verbose) + printf("FMT5 del extent: right bounded\n"); + + counter++; + break; + } + + if ((a > ext->t) + && ((ext->t + ext->fc*trk + ext->ft) > (a + b*trk + c))) + { + /* partition devides free space into 2 pieces */ + u_int16_t x = a + b*trk + c; + u_int16_t w,y; + u_int8_t z; + + w = (ext->t + ext->fc*trk + ext->ft) - (a + b*trk + c); + y = w / trk; + z = w % trk; + + ext->fc = (a - ext->t) / trk; + ext->ft = (a - ext->t) % trk; + + vtoc_update_format5_label_add(f5, verbose, + cyl, trk, x, y, z); + + if (verbose) + printf("FMT5 del extent: 2 pieces\n"); + + counter++; + break; + } + + if ((a < ext->t) && (a + b*trk + c > ext->t) + && (a + b*trk + c < ext->t + ext->fc*trk + ext->ft)) + { + printf("BUG: corresponding free space extent " \ + "doesn't match free space currently shown " \ + "in FMT5 DSCB!\nexiting...\n"); + exit(1); + } + + if ((a > ext->t) && (a < ext->t + ext->fc*trk + ext->ft) + && (a + b*trk + c > ext->t + ext->fc*trk + ext->ft)) + { + printf("BUG: specified free space extent for " \ + "deleting doesn't match free space " \ + "currently shown in FMT5 DSCB!\n" \ + "exiting...\n"); + exit(1); + } + } + + if (counter > 0) + return; + + printf("BUG: specified free space extent for " \ + "deleting not found in FMT5 DSCB!\n" \ + "exiting...\n"); + exit(1); +} + +/* + * reorganizes all extents within a FMT7 label + */ +static void +vtoc_reorganize_FMT7_extents (format7_label_t *f7) +{ + PDEBUG + ds7ext_t *ext, *last, tmp; + int i, j; + + for (i=0; i<16; i++) { + if (i<5) + last = &f7->DS7EXTNT[i]; + else + last = &f7->DS7ADEXT[i-5]; + + for (j=i; j<16; j++) { + if (j<5) + ext = &f7->DS7EXTNT[j]; + else + ext = &f7->DS7ADEXT[j-5]; + + if (((ext->a > 0) && (last->a == 0)) + || ((ext->a > 0) && (ext->a < last->a))) + { + tmp.a = last->a; + tmp.b = last->b; + last->a = ext->a; + last->b = ext->b; + ext->a = tmp.a; + ext->b = tmp.b; + } + } + } +} + +/* + * add a free space extent description to the VTOC FMT7 DSCB + */ +void +vtoc_update_format7_label_add (format7_label_t *f7, int verbose, + u_int32_t a, u_int32_t b) +{ + PDEBUG + ds7ext_t *ext = NULL, *tmp = NULL; + int i; + + for (i=0; i<16; i++) { + if (i<5) + ext = &f7->DS7EXTNT[i]; + else + ext = &f7->DS7ADEXT[i-5]; + + if (((a < ext->a) && (b > ext->a) && (b < ext->b)) + || ((a > ext->a) && (a < ext->b) && (b > ext->b))) + { + printf("BUG: overlapping free space extents " \ + "in FMT7 DSCB!\nexiting...\n"); + exit(1); + } + + if ((ext->a + ext->b) == 0x00000000) { + ext->a = a; + ext->b = b; + tmp = ext; + + if (verbose) + printf("FMT7 add extent: " \ + "add new extent\n"); + + break; + } + } + + if (tmp == NULL) { + /* BUG: no free extent found */ + printf("BUG: no free FMT7 DSCB extent found!\nexiting...\n"); + exit(1); + } + + for (i=0; i<16; i++) { + if (i<5) + ext = &f7->DS7EXTNT[i]; + else + ext = &f7->DS7ADEXT[i-5]; + + if ((ext->a + ext->b) == 0x00000000) + continue; + + if ((ext->b + 1) == tmp->a) { + /* this extent precedes the new one */ + ext->b = tmp->b; + bzero(tmp, sizeof(ds7ext_t)); + tmp = ext; + + if (verbose) + printf("FMT7 add extent: " \ + "merge with predecessor\n"); + + i = -1; + continue; + } + + if (ext->a == (tmp->b + 1)) { + /* this extent succeeds the new one */ + ext->a = tmp->a; + bzero(tmp, sizeof(ds7ext_t)); + tmp = ext; + + if (verbose) + printf("FMT7 add extent: " \ + "merge with successor\n"); + + i = -1; + continue; + } + } +} + +/* + * remove a free space extent description from the VTOC FMT7 DSCB + */ +void +vtoc_update_format7_label_del (format7_label_t *f7, int verbose, + u_int32_t a, u_int32_t b) +{ + PDEBUG + ds7ext_t *ext; + int i, counter=0; + + for (i=0; i<16; i++) { + if (i<5) + ext = &f7->DS7EXTNT[i]; + else + ext = &f7->DS7ADEXT[i-5]; + + if ((a == ext->a) && (b == ext->b)) { + /* fills up whole free space gap */ + bzero(ext, sizeof(ds7ext_t)); + + if (verbose) + printf("FMT7 del extent: " \ + "fills whole gap\n"); + + counter++; + break; + } + + if ((a == ext->a) && (b < ext->b)) { + /* left-bounded in free space gap */ + ext->a = b + 1; + + if (verbose) + printf("FMT7 add extent: " \ + "left-bounded\n"); + + counter++; + break; + } + + if ((a > ext->a) && (b == ext->b)) { + /* right-bounded in free space gap */ + ext->b = a - 1; + + if (verbose) + printf("FMT7 add extent: " \ + "right-bounded\n"); + + counter++; + break; + } + + if ((a > ext->a) && (b < ext->b)) { + /* partition devides free space into 2 pieces */ + vtoc_update_format7_label_add(f7, verbose, b+1, ext->b); + ext->b = a - 1; + + if (verbose) + printf("FMT7 add extent: " \ + "2 pieces\n"); + + counter++; + break; + } + + if (((a < ext->a) && (b > ext->a)) || ((a < ext->b) && (b > ext->b))) { + printf ("BUG: specified free space extent for deleting " + "doesn't match free space currently shown in " + "FMT7 DSCB!\nexiting...\n"); + printf ("%d %d %d %d\n", a, b, ext->a, ext->b); + exit(1); + } + } + + if (counter > 0) + return; + + printf("BUG: specified free space extent for " \ + "deleting not found in FMT7 DSCB!\n" \ + "exiting...\n"); + exit(1); +} + +void +vtoc_set_freespace(format4_label_t *f4, format5_label_t *f5, + format7_label_t *f7, char ch, int verbose, + u_int32_t start, u_int32_t stop, int cyl, int trk) +{ + PDEBUG + if ((cyl * trk) > BIG_DISK_SIZE) { + if (ch == '+') + vtoc_update_format7_label_add(f7, verbose, start, stop); + else if (ch == '-') + vtoc_update_format7_label_del(f7, verbose, start, stop); + else + printf("BUG: syntax error in " \ + "vtoc_set_freespace call\n"); + + vtoc_reorganize_FMT7_extents (f7); + + f4->DS4VTOCI = 0xa0; + f4->DS4EFLVL = 0x07; + vtoc_set_cchhb(&f4->DS4EFPTR, 0x0000, 0x0001, 0x03); + } else { + u_int16_t x,y; + u_int8_t z; + + x = (u_int16_t) start; + y = (u_int16_t) ((stop - start + 1) / trk); + z = (u_int8_t) ((stop - start + 1) % trk); + + if (ch == '+') + vtoc_update_format5_label_add(f5, verbose, cyl, trk, x, y, z); + else if (ch == '-') + vtoc_update_format5_label_del(f5, verbose, cyl, trk, x, y, z); + else + printf("BUG: syntax error in " \ + "vtoc_set_freespace call\n"); + + vtoc_reorganize_FMT5_extents (f5); + } +} diff --git a/libparted/libparted.c b/libparted/libparted.c new file mode 100644 index 0000000..c3c8ab4 --- /dev/null +++ b/libparted/libparted.c @@ -0,0 +1,345 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "config.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +#ifdef linux +# include <parted/linux.h> +#elif defined(__BEOS__) +# include <parted/beos.h> +#else +# include <parted/gnu.h> +#endif + +#include <string.h> +#include <stdlib.h> + +#if ENABLE_NLS +# include <locale.h> +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +const PedArchitecture* ped_architecture; + +/* ped_malloc() debugging. Stick the address and size of memory blocks that + * weren't ped_free()d in here, and an exception will be thrown when it is + * allocated. That way, you can find out what, exactly, the allocated thing + * is, and where it is created. + */ +typedef struct +{ + void* pointer; + size_t size; +} pointer_size_type; + +#ifdef DEBUG +static pointer_size_type dodgy_malloc_list[] = { + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} +}; + +static int dodgy_memory_active[100]; +#endif /* DEBUG */ + +int +ped_set_architecture (const PedArchitecture* arch) +{ + PED_ASSERT (ped_device_get_next (NULL) == NULL, return 0); + + ped_architecture = arch; + return 1; +} + +extern void ped_disk_aix_init (); +extern void ped_disk_bsd_init (); +extern void ped_disk_dvh_init (); +extern void ped_disk_gpt_init (); +extern void ped_disk_loop_init (); +extern void ped_disk_mac_init (); +extern void ped_disk_msdos_init (); +extern void ped_disk_pc98_init (); +extern void ped_disk_sun_init (); +extern void ped_disk_amiga_init (); +extern void ped_disk_dasd_init (); + +static void +init_disk_types () +{ + ped_disk_loop_init (); /* must be last in the probe list */ + +#if defined(__s390__) || defined(__s390x__) + ped_disk_dasd_init(); +#endif + + ped_disk_sun_init (); +#ifdef ENABLE_PC98 + ped_disk_pc98_init (); +#endif + ped_disk_msdos_init (); + ped_disk_mac_init (); + ped_disk_gpt_init (); + ped_disk_dvh_init (); + ped_disk_bsd_init (); + ped_disk_amiga_init (); + ped_disk_aix_init (); +} + +#ifdef ENABLE_FS +extern void ped_file_system_amiga_init (void); +extern void ped_file_system_xfs_init (void); +extern void ped_file_system_ufs_init (void); +extern void ped_file_system_reiserfs_init (void); +extern void ped_file_system_ntfs_init (void); +extern void ped_file_system_linux_swap_init (void); +extern void ped_file_system_jfs_init (void); +extern void ped_file_system_hfs_init (void); +extern void ped_file_system_fat_init (void); +extern void ped_file_system_ext2_init (void); + +static void +init_file_system_types () +{ + ped_file_system_amiga_init (); + ped_file_system_xfs_init (); + ped_file_system_ufs_init (); + ped_file_system_reiserfs_init (); + ped_file_system_ntfs_init (); + ped_file_system_linux_swap_init (); + ped_file_system_jfs_init (); + ped_file_system_hfs_init (); + ped_file_system_fat_init (); + ped_file_system_ext2_init (); +} +#endif /* ENABLE_FS */ + +extern void ped_disk_aix_done (); +extern void ped_disk_bsd_done (); +extern void ped_disk_dvh_done (); +extern void ped_disk_gpt_done (); +extern void ped_disk_loop_done (); +extern void ped_disk_mac_done (); +extern void ped_disk_msdos_done (); +extern void ped_disk_pc98_done (); +extern void ped_disk_sun_done (); +extern void ped_disk_amiga_done (); +extern void ped_disk_dasd_done (); + +static void +done_disk_types () +{ +#if defined(__s390__) || (__s390x__) + ped_disk_dasd_done (); +#endif + ped_disk_sun_done (); +#ifdef ENABLE_PC98 + ped_disk_pc98_done (); +#endif + ped_disk_msdos_done (); + ped_disk_mac_done (); + ped_disk_loop_done (); + ped_disk_gpt_done (); + ped_disk_dvh_done (); + ped_disk_bsd_done (); + ped_disk_amiga_done (); + ped_disk_aix_done (); +} + +static void _init() __attribute__ ((constructor)); + +static void +_init() +{ +#ifdef ENABLE_NLS + bindtextdomain (PACKAGE, LOCALEDIR); +#endif + + init_disk_types (); + +#ifdef ENABLE_FS + init_file_system_types (); +#endif + + /* FIXME: a better way of doing this? */ +#ifdef linux + ped_set_architecture (&ped_linux_arch); +#elif defined(__BEOS__) + ped_set_architecture (&ped_beos_arch); +#else + ped_set_architecture (&ped_gnu_arch); +#endif + +#ifdef DEBUG + memset (dodgy_memory_active, 0, sizeof (dodgy_memory_active)); +#endif +} + +#ifdef ENABLE_FS +extern void ped_file_system_ext2_done (void); +extern void ped_file_system_fat_done (void); +extern void ped_file_system_hfs_done (void); +extern void ped_file_system_jfs_done (void); +extern void ped_file_system_linux_swap_done (void); +extern void ped_file_system_ntfs_done (void); +extern void ped_file_system_reiserfs_done (void); +extern void ped_file_system_ufs_done (void); +extern void ped_file_system_xfs_done (void); +extern void ped_file_system_amiga_done (void); + +static void +done_file_system_types () +{ + ped_file_system_ext2_done (); + ped_file_system_fat_done (); + ped_file_system_hfs_done (); + ped_file_system_jfs_done (); + ped_file_system_linux_swap_done (); + ped_file_system_ntfs_done (); + ped_file_system_reiserfs_done (); + ped_file_system_ufs_done (); + ped_file_system_xfs_done (); + ped_file_system_amiga_done (); +} +#endif /* ENABLE_FS */ + +static void _done() __attribute__ ((destructor)); + +static void +_done() +{ + ped_device_free_all (); + + done_disk_types (); + +#ifdef ENABLE_FS + done_file_system_types (); +#endif +} + +const char* +ped_get_version () +{ + return VERSION; +} + +#ifdef DEBUG +static void +_check_dodgy_pointer (const void* ptr, size_t size, int is_malloc) +{ + int i; + + for (i=0; dodgy_malloc_list[i].pointer; i++) { + if (dodgy_malloc_list[i].pointer != ptr) + continue; + if (is_malloc && dodgy_malloc_list[i].size != size) + continue; + if (!is_malloc && !dodgy_memory_active[i]) + continue; + + + if (is_malloc) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + "Dodgy malloc(%x) == %p occurred (active==%d)", + size, ptr, dodgy_memory_active[i]); + dodgy_memory_active[i]++; + } else { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + "Dodgy free(%p) occurred (active==%d)", + ptr, dodgy_memory_active[i]); + dodgy_memory_active[i]--; + } + + return; + } +} +#endif /* DEBUG */ + +void* +ped_malloc (size_t size) +{ + void* mem; + + mem = (void*) malloc (size); + if (!mem) { + ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL, + _("Out of memory.")); + return NULL; + } + +#ifdef DEBUG + memset (mem, 0xff, size); + _check_dodgy_pointer (mem, size, 1); +#endif + + return mem; +} + +int +ped_realloc (void** old, size_t size) +{ + void* mem; + + mem = (void*) realloc (*old, size); + if (!mem) { + ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL, + _("Out of memory.")); + return 0; + } + *old = mem; + return 1; +} + + +void* ped_calloc (size_t size) +{ + void* buf = ped_malloc (size); + + memset (buf, 0, size); + + return buf; +} + + +void +ped_free (void* ptr) +{ +#ifdef DEBUG + _check_dodgy_pointer (ptr, 0, 0); +#endif + + free (ptr); +} + diff --git a/libparted/mbr.s b/libparted/mbr.s new file mode 100644 index 0000000..0d35aa9 --- /dev/null +++ b/libparted/mbr.s @@ -0,0 +1,86 @@ +; libparted - a library for manipulating disk partitions +; Copyright (C) 1999, 2000 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 2 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, write to the Free Software +; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +; NOTE: I build this with: +; $ as86 -b /dev/stdout mbr.s | hexdump -e '8/1 "0x%02x, " "\n"' +; +; The build isn't done automagically by make, because as86 may not be on many +; machines (particularly non-x86). Also, it seems rather difficult to get +; as86 to build object files that can be linked, especially as it's 16 bit +; code... + +USE16 + +; This code, plus the partition table is loaded into 0000:7C00 by the BIOS + +.text + +; set top of stack to 1000:B000 + + cli + + mov ax, #0x1000 + mov ss, ax + mov sp, #0xB000 + + mov ax, #0x0000 + mov ds, ax + mov es, ax + + sti + +; Copy what the BIOS loaded (i.e. the MBR + head of partition table) from +; 0000:7c00 to 0000:0600 + + mov si, #0x7c00 + mov di, #0x0600 + mov cx, #0x200 + rep + movsb + +; Jump to the copy of the MBR + + jmp 0x0000:find_boot_partition + 0x0600 + +find_boot_partition: + mov si, #0x07BE + +check_next_bootable: + cmp [si], al + jnz found_bootable + add si, #0x0010 + cmp si, #0x07FE + jnz check_next_bootable + jmp error + +found_bootable: + +; Load in the boot sector at 0000:7c00 + + mov ah, #2 ; BIOS command (read) + mov al, #1 ; count + mov bx, #0x7c00 ; destination pointer + mov dl, #0x80 ; drive + mov dh, byte ptr [si + 1] ; head + mov cx, word ptr [si + 2] ; sector / cylinder + int #0x13 ; BIOS read interrupt + + jmp 0x0000:0x7c00 ; hand control to boot sector + +error: + jmp error + diff --git a/libparted/timer.c b/libparted/timer.c new file mode 100644 index 0000000..ac403ba --- /dev/null +++ b/libparted/timer.c @@ -0,0 +1,245 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** \file timer.c */ + +/** + * \addtogroup PedTimer + * + * \brief A PedTimer keeps track of the progress of a single (possibly + * compound) operation. + * + * The user of libparted constructs a PedTimer, and passes it to libparted + * functions that are likely to be expensive operations + * (like ped_file_system_resize). Use of timers is optional... you may + * pass NULL instead. + * + * When you create a PedTimer, you must specify a timer handler function. + * This will be called when there's an update on how work is progressing. + * + * Timers may be nested. When a timer is constructed, you can choose + * to assign it a parent, along with an estimate of what proportion of + * the total (parent's) time will be used in the nested operation. In + * this case, the nested timer's handler is internal to libparted, + * and simply updates the parent's progress, and calls its handler. + * + * @{ + */ + + +#include <parted/parted.h> +#include <parted/debug.h> + +#define PED_TIMER_START_DELAY 2 + +typedef struct { + PedTimer* parent; + float nest_frac; + float start_frac; +} NestedContext; + + +/** + * \brief Creates a timer. + * + * Context will be passed in the \p context + * argument to the \p handler, when it is invoked. + * + * \return a new PedTimer + */ +PedTimer* +ped_timer_new (PedTimerHandler* handler, void* context) +{ + PedTimer* timer; + + PED_ASSERT (handler != NULL, return NULL); + + timer = (PedTimer*) ped_malloc (sizeof (PedTimer)); + if (!timer) + return NULL; + + timer->handler = handler; + timer->context = context; + ped_timer_reset (timer); + return timer; +} + + +/** + * \brief Destroys a \p timer. + */ +void +ped_timer_destroy (PedTimer* timer) +{ + if (!timer) + return; + + ped_free (timer); +} + +/* This function is used by ped_timer_new_nested() as the timer->handler + * function. + */ +static void +_nest_handler (PedTimer* timer, void* context) +{ + NestedContext* ncontext = (NestedContext*) context; + + ped_timer_update ( + ncontext->parent, + ncontext->start_frac + ncontext->nest_frac * timer->frac); +} + + +/** + * \brief Creates a new nested timer. + * + * This function creates a "nested" timer that describes the progress + * of a subtask. \p parent is the parent timer, and \p nested_frac is + * the estimated proportion (between 0 and 1) of the time that will be + * spent doing the nested timer's operation. The timer should only be + * constructed immediately prior to starting the nested operation. + * (It will be inaccurate, otherwise). + * Updates to the progress of the subtask are propagated + * back through to the parent task's timer. + * + * \return nested timer + */ +PedTimer* +ped_timer_new_nested (PedTimer* parent, float nest_frac) +{ + NestedContext* context; + + if (!parent) + return NULL; + + PED_ASSERT (nest_frac >= 0.0, return NULL); + PED_ASSERT (nest_frac <= 1.0, return NULL); + + context = (NestedContext*) ped_malloc (sizeof (NestedContext)); + if (!context) + return NULL; + context->parent = parent; + context->nest_frac = nest_frac; + context->start_frac = parent->frac; + + return ped_timer_new (_nest_handler, context); +} + +/** + * \brief Destroys a nested \p timer. + */ +void +ped_timer_destroy_nested (PedTimer* timer) +{ + if (!timer) + return; + + ped_free (timer->context); + ped_timer_destroy (timer); +} + +/** + * \internal + * + * \brief This function calls the update handler, making sure that it has + * the latest time. + * + * First it updates \p timer->now and recomputes \p timer->predicted_end, + * and then calls the handler. + */ +void +ped_timer_touch (PedTimer* timer) +{ + if (!timer) + return; + + timer->now = time (NULL); + if (timer->now > timer->predicted_end) + timer->predicted_end = timer->now; + + timer->handler (timer, timer->context); +} + +/** + * \internal + * + * \brief This function sets the \p timer into a "start of task" position. + * + * It resets the \p timer, by setting \p timer->start and \p timer->now + * to the current time. + */ +void +ped_timer_reset (PedTimer* timer) +{ + if (!timer) + return; + + timer->start = timer->now = timer->predicted_end = time (NULL); + timer->state_name = NULL; + timer->frac = 0; + + ped_timer_touch (timer); +} + +/** + * \internal + * + * \brief This function tells a \p timer what fraction \p frac of the task + * has been completed. + * + * Sets the new \p timer->frac, and calls ped_timer_touch(). + */ +void +ped_timer_update (PedTimer* timer, float frac) +{ + if (!timer) + return; + + timer->now = time (NULL); + timer->frac = frac; + + if (frac) + timer->predicted_end + = timer->start + + (long) ((timer->now - timer->start) / frac); + + ped_timer_touch (timer); +} + +/** + * \internal + * + * \brief This function changes the description of the current task that the + * \p timer describes. + * + * Sets a new name - \p state_name - for the current "phase" of the operation, + * and calls ped_timer_touch(). + */ +void +ped_timer_set_state_name (PedTimer* timer, const char* state_name) +{ + if (!timer) + return; + + timer->state_name = state_name; + ped_timer_touch (timer); +} + +/** @} */ diff --git a/libparted/unit.c b/libparted/unit.c new file mode 100644 index 0000000..ac4d74e --- /dev/null +++ b/libparted/unit.c @@ -0,0 +1,564 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2005 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** \file unit.c */ + +/** + * \addtogroup PedUnit + * + * \brief The PedUnit module provides a standard mechanism for describing + * and parsing locations within devices in human-friendly plain text. + * + * Internally, libparted uses PedSector (which is typedef'ed to be long long + * in <parted/device.h>) to describe device locations such as the start and + * end of partitions. However, sector numbers are often long and unintuitive. + * For example, my extended partition starts at sector 208845. PedUnit allows + * this location to be represented in more intutitive ways, including "106Mb", + * "0Gb" and "0%", as well as "208845s". PedUnit aims to provide facilities + * to provide a consistent system for describing device locations all + * throughout libparted. + * + * PedUnit provides two basic services: converting a PedSector into a text + * representation, and parsing a text representation into a PedSector. + * PedUnit currently supports these units: + * + * sectors, bytes, kilobytes, megabytes, gigabytes, terabytes, compact, + * cylinder and percent. + * + * PedUnit has a global variable that contains the default unit for all + * conversions. + * + * @{ + */ + + + + +#include <parted/parted.h> +#include <parted/debug.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <float.h> + +#define N_(String) String +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + + +static PedUnit default_unit = PED_UNIT_COMPACT; +static const char* unit_names[] = { + "s", + "B", + "kB", + "MB", + "GB", + "TB", + "compact", + "cyl", + "chs", + "%", + "kiB", + "MiB", + "GiB", + "TiB" +}; + + +/** + * \brief Set the default \p unit used by subsequent calls to the PedUnit API. + * + * In particular, this affects how locations inside error messages + * (exceptions) are displayed. + */ +void +ped_unit_set_default (PedUnit unit) +{ + default_unit = unit; +} + + +/** + * \brief Get the current default unit. + */ +PedUnit +ped_unit_get_default () +{ + return default_unit; +} + +/** + * Get the byte size of a given \p unit. + */ +long long +ped_unit_get_size (PedDevice* dev, PedUnit unit) +{ + PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors; + + switch (unit) { + case PED_UNIT_SECTOR: return dev->sector_size; + case PED_UNIT_BYTE: return 1; + case PED_UNIT_KILOBYTE: return PED_KILOBYTE_SIZE; + case PED_UNIT_MEGABYTE: return PED_MEGABYTE_SIZE; + case PED_UNIT_GIGABYTE: return PED_GIGABYTE_SIZE; + case PED_UNIT_TERABYTE: return PED_TERABYTE_SIZE; + case PED_UNIT_KIBIBYTE: return PED_KIBIBYTE_SIZE; + case PED_UNIT_MEBIBYTE: return PED_MEBIBYTE_SIZE; + case PED_UNIT_GIBIBYTE: return PED_GIBIBYTE_SIZE; + case PED_UNIT_TEBIBYTE: return PED_TEBIBYTE_SIZE; + case PED_UNIT_CYLINDER: return cyl_size * dev->sector_size; + case PED_UNIT_CHS: return dev->sector_size; + + case PED_UNIT_PERCENT: + return dev->length * dev->sector_size / 100; + + case PED_UNIT_COMPACT: + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Cannot get unit size for special unit " + "'COMPACT'.")); + return 0; + } + + /* never reached */ + PED_ASSERT(0, return 0); + return 0; +} + +/** + * Get a textual (non-internationalized) representation of a \p unit. + * + * For example, the textual representation of PED_UNIT_SECTOR is "s". + */ +const char* +ped_unit_get_name (PedUnit unit) +{ + return unit_names[unit]; +} + +/** + * Get a unit based on its textual representation: \p unit_name. + * + * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE. + */ +PedUnit +ped_unit_get_by_name (const char* unit_name) +{ + PedUnit unit; + for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) { + if (!strcasecmp (unit_names[unit], unit_name)) + return unit; + } + return -1; +} + +static char* +ped_strdup (const char *str) +{ + char *result; + result = ped_malloc (strlen (str) + 1); + if (!result) + return NULL; + strcpy (result, str); + return result; +} + +/** + * \brief Get a string that describes the location of the \p byte on + * device \p dev. + * + * The string is described with the desired \p unit. + * The returned string must be freed with ped_free(). + */ +char* +ped_unit_format_custom_byte (PedDevice* dev, PedSector byte, PedUnit unit) +{ + char buf[100]; + PedSector sector = byte / dev->sector_size; + double d, w; + int p; + + PED_ASSERT (dev != NULL, return NULL); + + /* CHS has a special comma-separated format. */ + if (unit == PED_UNIT_CHS) { + PedCHSGeometry *chs = &dev->bios_geom; + snprintf (buf, 100, "%lld,%lld,%lld", + sector / chs->sectors / chs->heads, + (sector / chs->sectors) % chs->heads, + sector % chs->sectors); + return ped_strdup (buf); + } + + /* Cylinders, sectors and bytes should be rounded down... */ + if (unit == PED_UNIT_CYLINDER + || unit == PED_UNIT_SECTOR + || unit == PED_UNIT_BYTE) { + snprintf (buf, 100, "%lld%s", + byte / ped_unit_get_size (dev, unit), + ped_unit_get_name (unit)); + return ped_strdup (buf); + } + + if (unit == PED_UNIT_COMPACT) { + if (byte >= 10LL * PED_TERABYTE_SIZE) + unit = PED_UNIT_TERABYTE; + else if (byte >= 10LL * PED_GIGABYTE_SIZE) + unit = PED_UNIT_GIGABYTE; + else if (byte >= 10LL * PED_MEGABYTE_SIZE) + unit = PED_UNIT_MEGABYTE; + else + unit = PED_UNIT_KILOBYTE; + } + + /* IEEE754 says that 100.5 has to be rounded to 100 (by printf) */ + /* but 101.5 has to be rounded to 102... so we multiply by 1+E. */ + /* This just divide by 2 the natural IEEE754 extended precision */ + /* and won't cause any trouble before 1000 TB */ + d = ((double)byte / (double)ped_unit_get_size (dev, unit)) + * (1. + DBL_EPSILON); + w = d + ( (d < 10. ) ? 0.005 : + (d < 100.) ? 0.05 : + 0.5 ); + p = (w < 10. ) ? 2 : + (w < 100.) ? 1 : + 0 ; + +#ifdef __BEOS__ + snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit)); +#else + snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit)); +#endif + + return ped_strdup (buf); +} + +/** + * \brief Get a string that describes the location of the \p byte on + * device \p dev. + * + * The string is described with the default unit, which is set + * by ped_unit_set_default(). + * The returned string must be freed with ped_free(). + */ +char* +ped_unit_format_byte (PedDevice* dev, PedSector byte) +{ + PED_ASSERT (dev != NULL, return NULL); + return ped_unit_format_custom_byte (dev, byte, default_unit); +} + +/** + * \brief Get a string that describes the location \p sector on device \p dev. + * + * The string is described with the desired \p unit. + * The returned string must be freed with ped_free(). + */ +char* +ped_unit_format_custom (PedDevice* dev, PedSector sector, PedUnit unit) +{ + PED_ASSERT (dev != NULL, return NULL); + return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit); +} + +/** + * \brief Get a string that describes the location \p sector on device \p dev. + * + * The string is described with the default unit, which is set + * by ped_unit_set_default(). + * The returned string must be freed with ped_free(). + */ +char* +ped_unit_format (PedDevice* dev, PedSector sector) +{ + PED_ASSERT (dev != NULL, return NULL); + return ped_unit_format_custom_byte (dev, sector * dev->sector_size, + default_unit); +} + +/** + * If \p str contains a valid description of a location on \p dev, + * then \p *sector is modified to describe the location and a geometry + * is created in \p *range describing a 2 units large area centered on + * \p *sector. If the \p range as described here would be partially outside + * the device \p dev, the geometry returned is the intersection between the + * former and the whole device geometry. If no units are specified, then the + * default unit is assumed. + * + * \return \c 1 if \p str is a valid location description, \c 0 otherwise + */ +int +ped_unit_parse (const char* str, PedDevice* dev, PedSector *sector, + PedGeometry** range) +{ + return ped_unit_parse_custom (str, dev, default_unit, sector, range); +} + +/* Inefficiently removes all spaces from a string, in-place. */ +static void +strip_string (char* str) +{ + int i; + + for (i = 0; str[i] != 0; i++) { + if (isspace (str[i])) { + int j; + for (j = i + 1; str[j] != 0; j++) + str[j - 1] = str[j]; + } + } +} + + +/* Find non-number suffix. Eg: find_suffix("32Mb") returns a pointer to + * "Mb". */ +static char* +find_suffix (char* str) +{ + while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0]))) + str++; + return str; +} + +static void +remove_punct (char* str) +{ + int i = 0; + + for (i = 0; str[i]; i++) { + if (ispunct (str[i])) + str[i] = ' '; + } +} + +static int +is_chs (const char* str) +{ + int punct_count = 0; + int i = 0; + + for (i = 0; str[i]; i++) + punct_count += ispunct (str[i]) != 0; + return punct_count == 2; +} + +static int +parse_chs (const char* str, PedDevice* dev, PedSector* sector, + PedGeometry** range) +{ + PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors; + char* copy = ped_strdup (str); + PedCHSGeometry chs; + + copy = ped_strdup (str); + if (!copy) + return 0; + strip_string (copy); + remove_punct (copy); + + if (sscanf (copy, "%d %d %d", + &chs.cylinders, &chs.heads, &chs.sectors) != 3) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("\"%s\" has invalid syntax for locations."), + copy); + goto error_free_copy; + } + + if (chs.heads >= dev->bios_geom.heads) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The maximum head value is %d."), + dev->bios_geom.heads - 1); + goto error_free_copy; + } + if (chs.sectors >= dev->bios_geom.sectors) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The maximum sector value is %d."), + dev->bios_geom.sectors - 1); + goto error_free_copy; + } + + *sector = 1LL * chs.cylinders * cyl_size + + chs.heads * dev->bios_geom.sectors + + chs.sectors; + + if (*sector >= dev->length) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The location %s is outside of the " + "device %s."), + str, dev->path); + goto error_free_copy; + } + if (range) + *range = ped_geometry_new (dev, *sector, 1); + ped_free (copy); + return !range || *range != NULL; + +error_free_copy: + ped_free (copy); +error: + *sector = 0; + if (range) + *range = NULL; + return 0; +} + +static PedSector +clip (PedDevice* dev, PedSector sector) +{ + if (sector < 0) + return 0; + if (sector > dev->length - 1) + return dev->length - 1; + return sector; +} + +static PedGeometry* +geometry_from_centre_radius (PedDevice* dev, PedSector sector, PedSector radius) +{ + PedSector start = clip (dev, sector - radius); + PedSector end = clip (dev, sector + radius); + if (sector - end > radius || start - sector > radius) + return NULL; + return ped_geometry_new (dev, start, end - start + 1); +} + +static PedUnit +parse_unit_suffix (const char* suffix, PedUnit suggested_unit) +{ + if (strlen (suffix) > 1 && tolower (suffix[1]) == 'i') { + switch (tolower (suffix[0])) { + case 'k': return PED_UNIT_KIBIBYTE; + case 'm': return PED_UNIT_MEBIBYTE; + case 'g': return PED_UNIT_GIBIBYTE; + case 't': return PED_UNIT_TEBIBYTE; + } + } else if (strlen (suffix) > 0) { + switch (tolower (suffix[0])) { + case 's': return PED_UNIT_SECTOR; + case 'b': return PED_UNIT_BYTE; + case 'k': return PED_UNIT_KILOBYTE; + case 'm': return PED_UNIT_MEGABYTE; + case 'g': return PED_UNIT_GIGABYTE; + case 't': return PED_UNIT_TERABYTE; + case 'c': return PED_UNIT_CYLINDER; + case '%': return PED_UNIT_PERCENT; + } + } + + if (suggested_unit == PED_UNIT_COMPACT) { + if (default_unit == PED_UNIT_COMPACT) + return PED_UNIT_MEGABYTE; + else + return default_unit; + } + + return suggested_unit; +} + +/** + * If \p str contains a valid description of a location on \p dev, then + * \p *sector is modified to describe the location and a geometry is created + * in \p *range describing a 2 units large area centered on \p *sector. If the + * \p range as described here would be partially outside the device \p dev, the + * geometry returned is the intersection between the former and the whole + * device geometry. If no units are specified, then the default unit is + * assumed. + * + * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a + * location + * \throws PED_EXCEPTION_ERROR if location described by \p str + * is outside of the device \p dev->path + * + * \return \c 1 if \p str is a valid location description, \c 0 otherwise. + */ +int +ped_unit_parse_custom (const char* str, PedDevice* dev, PedUnit unit, + PedSector* sector, PedGeometry** range) +{ + char* copy; + char* suffix; + double num; + long long unit_size; + PedSector radius; + + if (is_chs (str)) + return parse_chs (str, dev, sector, range); + + copy = ped_strdup (str); + if (!copy) + goto error; + strip_string (copy); + + suffix = find_suffix (copy); + unit = parse_unit_suffix (suffix, unit); + suffix[0] = 0; + + if (sscanf (copy, "%lf", &num) != 1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Invalid number.")); + goto error_free_copy; + } + + unit_size = ped_unit_get_size (dev, unit); + radius = ped_div_round_up (unit_size, dev->sector_size) - 1; + if (radius < 0) + radius = 0; + + *sector = num * unit_size / dev->sector_size; + /* negative numbers count from the end */ + if (copy[0] == '-') + *sector += dev->length; + if (range) { + *range = geometry_from_centre_radius (dev, *sector, radius); + if (!*range) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The location %s is outside of the " + "device %s."), + str, dev->path); + goto error_free_copy; + } + } + *sector = clip (dev, *sector); + + ped_free (copy); + return 1; + +error_free_copy: + ped_free (copy); +error: + *sector = 0; + if (range) + *range = NULL; + return 0; +} + + +/** @} */ |