/* libparted - a library for manipulating disk partitions Copyright (C) 1999 - 2001, 2005, 2007-2010 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** \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(). * * @{ */ #include #include #include #include #include #include #include #include "architecture.h" static PedDevice* devices; /* legal advice says: initialized to NULL, under section 6.7.8 part 10 of ISO/EIC 9899:1999 */ 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; } /* This function may be called twice for the same device if a libparted user explictly removes the device from the cache using ped_device_cache_remove(), we get called and it then becomes the user's responsibility to free the PedDevice by calling ped_device_destroy(). ped_device_destroy() will then call us a second time, so if the device is not found in the list do nothing. */ if (walk == NULL) return; 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); 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 = NULL; PED_ASSERT (path != NULL); /* Don't canonicalize /dev/mapper or /dev/md/ paths, see tests/symlink.c */ if (strncmp (path, "/dev/mapper/", 12) && strncmp (path, "/dev/md/", 8)) 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)) { free (normal_path); return walk; } } walk = ped_architecture->dev_ops->_new (normal_path); 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); } void ped_device_cache_remove(PedDevice *dev) { _device_unregister (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); PED_ASSERT (!dev->external_mode); 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); PED_ASSERT (!dev->external_mode); PED_ASSERT (dev->open_count > 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); PED_ASSERT (!dev->external_mode); 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); PED_ASSERT (dev->external_mode); 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); PED_ASSERT (buffer != NULL); PED_ASSERT (!dev->external_mode); PED_ASSERT (dev->open_count > 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); PED_ASSERT (buffer != NULL); PED_ASSERT (!dev->external_mode); PED_ASSERT (dev->open_count > 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); PED_ASSERT (!dev->external_mode); PED_ASSERT (dev->open_count > 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); PED_ASSERT (!dev->external_mode); PED_ASSERT (dev->open_count > 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); PED_ASSERT (!dev->external_mode); PED_ASSERT (dev->open_count > 0); return ped_architecture->dev_ops->sync_fast (dev); } /** * Get a constraint that represents hardware requirements on geometry. * This function will return a constraint representing the limits imposed * by the size of the disk, it will *not* provide any alignment constraints. * * Alignment constraints may be desirable when using media that have a physical * sector size that is a multiple of the logical sector size, as in this case * proper partition alignment can benefit disk performance signigicantly. * When you want a constraint with alignment info, use * ped_device_get_minimal_aligned_constraint() or * ped_device_get_optimal_aligned_constraint(). * * \return NULL on error, otherwise a pointer to a dynamically allocated * constraint. */ PedConstraint* ped_device_get_constraint (const PedDevice* dev) { PedGeometry *s, *e; PedConstraint* c = ped_constraint_new ( ped_alignment_any, ped_alignment_any, s = ped_geometry_new (dev, 0, dev->length), e = ped_geometry_new (dev, 0, dev->length), 1, dev->length); free (s); free (e); return c; } static PedConstraint* _ped_device_get_aligned_constraint(const PedDevice *dev, PedAlignment* start_align) { PedAlignment *end_align = NULL; PedGeometry *whole_dev_geom = NULL; PedConstraint *c = NULL; if (start_align) { end_align = ped_alignment_new(start_align->offset - 1, start_align->grain_size); if (!end_align) goto free_start_align; } whole_dev_geom = ped_geometry_new (dev, 0, dev->length); if (start_align) c = ped_constraint_new (start_align, end_align, whole_dev_geom, whole_dev_geom, 1, dev->length); else c = ped_constraint_new (ped_alignment_any, ped_alignment_any, whole_dev_geom, whole_dev_geom, 1, dev->length); free (whole_dev_geom); free (end_align); free_start_align: free (start_align); return c; } /** * Get a constraint that represents hardware requirements on geometry and * alignment. * * This function will return a constraint representing the limits imposed * by the size of the disk and the minimal alignment requirements for proper * performance of the disk. * * \return NULL on error, otherwise a pointer to a dynamically allocated * constraint. */ PedConstraint* ped_device_get_minimal_aligned_constraint(const PedDevice *dev) { return _ped_device_get_aligned_constraint(dev, ped_device_get_minimum_alignment(dev)); } /** * Get a constraint that represents hardware requirements on geometry and * alignment. * * This function will return a constraint representing the limits imposed * by the size of the disk and the alignment requirements for optimal * performance of the disk. * * \return NULL on error, otherwise a pointer to a dynamically allocated * constraint. */ PedConstraint* ped_device_get_optimal_aligned_constraint(const PedDevice *dev) { return _ped_device_get_aligned_constraint(dev, ped_device_get_optimum_alignment(dev)); } /** * Get an alignment that represents minimum hardware requirements on alignment. * When for example using media that has a physical sector size that is a * multiple of the logical sector size, it is desirable to have disk accesses * (and thus partitions) properly aligned. Having partitions not aligned to * the minimum hardware requirements may lead to a performance penalty. * * The returned alignment describes the alignment for the start sector of the * partition, the end sector should be aligned too, to get the end sector * alignment decrease the returned alignment's offset by 1. * * \return the minimum alignment of partition start sectors, or NULL if this * information is not available. */ PedAlignment* ped_device_get_minimum_alignment(const PedDevice *dev) { PedAlignment *align = NULL; if (ped_architecture->dev_ops->get_minimum_alignment) align = ped_architecture->dev_ops->get_minimum_alignment(dev); if (align == NULL) align = ped_alignment_new(0, dev->phys_sector_size / dev->sector_size); return align; } /** * Get an alignment that represents the hardware requirements for optimal * performance. * * The returned alignment describes the alignment for the start sector of the * partition, the end sector should be aligned too, to get the end sector * alignment decrease the returned alignment's offset by 1. * * \return the optimal alignment of partition start sectors, or NULL if this * information is not available. */ PedAlignment* ped_device_get_optimum_alignment(const PedDevice *dev) { PedAlignment *align = NULL; if (ped_architecture->dev_ops->get_optimum_alignment) align = ped_architecture->dev_ops->get_optimum_alignment(dev); /* If the arch specific code could not give as an alignment return a default value based on the type of device. */ if (align == NULL) { /* Align to a grain of 1MiB (like vista / win7) */ align = ped_alignment_new(0, (PED_DEFAULT_ALIGNMENT / dev->sector_size)); } return align; } /** @} */