diff options
Diffstat (limited to 'lib/ext2fs/getsize.c')
-rw-r--r-- | lib/ext2fs/getsize.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/lib/ext2fs/getsize.c b/lib/ext2fs/getsize.c new file mode 100644 index 0000000..bcf3020 --- /dev/null +++ b/lib/ext2fs/getsize.c @@ -0,0 +1,324 @@ +/* + * getsize.c --- get the size of a partition. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * Copyright (C) 2003 VMware, Inc. + * + * Windows version of ext2fs_get_device_size by Chris Li, VMware. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif + +#include "config.h" +#include <stdio.h> +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#if HAVE_ERRNO_H +#include <errno.h> +#endif +#include <fcntl.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_LINUX_FD_H +#include <linux/fd.h> +#endif +#ifdef HAVE_SYS_DISKLABEL_H +#include <sys/disklabel.h> +#endif +#ifdef HAVE_SYS_DISK_H +#include <sys/disk.h> +#endif +#ifdef __linux__ +#include <sys/utsname.h> +#endif +#if HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include <ctype.h> + +#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#endif + +#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ +#endif + +#ifdef APPLE_DARWIN +#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 +#endif /* APPLE_DARWIN */ + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__CYGWIN__) || defined (WIN32) +#include "windows.h" +#include "winioctl.h" + +#if (_WIN32_WINNT >= 0x0500) +#define HAVE_GET_FILE_SIZE_EX 1 +#endif + +HANDLE windows_get_handle(io_channel channel); + +errcode_t ext2fs_get_device_size2(const char *file, int blocksize, + blk64_t *retblocks) +{ + HANDLE dev; + PARTITION_INFORMATION pi; + DISK_GEOMETRY gi; + DWORD retbytes; +#ifdef HAVE_GET_FILE_SIZE_EX + LARGE_INTEGER filesize; +#else + DWORD filesize; +#endif /* HAVE_GET_FILE_SIZE_EX */ + + io_channel data_io = 0; + int retval; + + retval = windows_io_manager->open(file, 0, &data_io); + if (retval) + return retval; + + dev = windows_get_handle(data_io); + if (dev == INVALID_HANDLE_VALUE) + return EBADF; + + if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, + &pi, sizeof(PARTITION_INFORMATION), + &pi, sizeof(PARTITION_INFORMATION), + &retbytes, NULL)) { + + *retblocks = pi.PartitionLength.QuadPart / blocksize; + + } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, + &gi, sizeof(DISK_GEOMETRY), + &gi, sizeof(DISK_GEOMETRY), + &retbytes, NULL)) { + + *retblocks = gi.BytesPerSector * + gi.SectorsPerTrack * + gi.TracksPerCylinder * + gi.Cylinders.QuadPart / blocksize; + +#ifdef HAVE_GET_FILE_SIZE_EX + } else if (GetFileSizeEx(dev, &filesize)) { + *retblocks = filesize.QuadPart / blocksize; + } +#else + } else { + filesize = GetFileSize(dev, NULL); + if (INVALID_FILE_SIZE != filesize) { + *retblocks = filesize / blocksize; + } + } +#endif /* HAVE_GET_FILE_SIZE_EX */ + + windows_io_manager->close(data_io); + + return 0; +} + +#else + +static int valid_offset (int fd, ext2_loff_t offset) +{ + char ch; + + if (ext2fs_llseek (fd, offset, 0) < 0) + return 0; + if (read (fd, &ch, 1) < 1) + return 0; + return 1; +} + +/* + * Returns the number of blocks in a partition + */ +errcode_t ext2fs_get_device_size2(const char *file, int blocksize, + blk64_t *retblocks) +{ + int fd, rc = 0; + unsigned long long size64; + ext2_loff_t high, low; + + fd = ext2fs_open_file(file, O_RDONLY, 0); + if (fd < 0) + return errno; + +#if defined DKIOCGETBLOCKCOUNT && defined DKIOCGETBLOCKSIZE /* For Apple Darwin */ + unsigned int size; + + if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0 && + ioctl(fd, DKIOCGETBLOCKSIZE, &size) >= 0) { + *retblocks = size64 * size / blocksize; + goto out; + } +#endif + +#ifdef BLKGETSIZE64 + { + int valid_blkgetsize64 = 1; +#ifdef __linux__ + struct utsname ut; + + if ((uname(&ut) == 0) && + ((ut.release[0] == '2') && (ut.release[1] == '.') && + (ut.release[2] < '6') && (ut.release[3] == '.'))) + valid_blkgetsize64 = 0; +#endif + if (valid_blkgetsize64 && + ioctl(fd, BLKGETSIZE64, &size64) >= 0) { + *retblocks = size64 / blocksize; + goto out; + } + } +#endif /* BLKGETSIZE64 */ + +#ifdef BLKGETSIZE + { + unsigned long size; + + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + *retblocks = size / (blocksize / 512); + goto out; + } + } +#endif + +#ifdef FDGETPRM + { + struct floppy_struct this_floppy; + + if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { + *retblocks = this_floppy.size / (blocksize / 512); + goto out; + } + } +#endif + +#ifdef HAVE_SYS_DISKLABEL_H + { + int part; + struct disklabel lab; + struct partition *pp; + char ch; + +#if defined(DIOCGMEDIASIZE) + { + off_t ms; + u_int bs; + if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { + *retblocks = ms / blocksize; + goto out; + } + } +#elif defined(DIOCGDINFO) + /* old disklabel interface */ + part = strlen(file) - 1; + if (part >= 0) { + ch = file[part]; + if (isdigit(ch)) + part = 0; + else if (ch >= 'a' && ch <= 'h') + part = ch - 'a'; + else + part = -1; + } + if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { + pp = &lab.d_partitions[part]; + if (pp->p_size) { + *retblocks = pp->p_size / (blocksize / 512); + goto out; + } + } +#endif /* defined(DIOCG*) */ + } +#endif /* HAVE_SYS_DISKLABEL_H */ + + { + ext2fs_struct_stat st; + + if (ext2fs_fstat(fd, &st) == 0) + if (S_ISREG(st.st_mode)) { + *retblocks = st.st_size / blocksize; + goto out; + } + } + + /* + * OK, we couldn't figure it out by using a specialized ioctl, + * which is generally the best way. So do binary search to + * find the size of the partition. + */ + low = 0; + for (high = 1024; valid_offset(fd, high); high *= 2) + low = high; + while (low < high - 1) { + const ext2_loff_t mid = (low + high) / 2; + + if (valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + valid_offset(fd, 0); + size64 = low + 1; + *retblocks = size64 / blocksize; +out: + close(fd); + return rc; +} + +#endif /* WIN32 */ + +errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks) +{ + errcode_t retval; + blk64_t blocks; + + retval = ext2fs_get_device_size2(file, blocksize, &blocks); + if (retval) + return retval; + if (blocks >= (1ULL << 32)) + return EFBIG; + *retblocks = (blk_t) blocks; + return 0; +} + +#ifdef DEBUG +int main(int argc, char **argv) +{ + blk64_t blocks; + int retval; + + if (argc < 2) { + fprintf(stderr, "Usage: %s device\n", argv[0]); + exit(1); + } + + retval = ext2fs_get_device_size2(argv[1], 1024, &blocks); + if (retval) { + com_err(argv[0], retval, + "while calling ext2fs_get_device_size"); + exit(1); + } + printf("Device %s has %llu 1k blocks.\n", argv[1], + (unsigned long long) locks); + exit(0); +} +#endif |