Whamcloud - gitweb
build: use MKDIR_P instead of MKINSTALLDIRS
[tools/e2fsprogs.git] / lib / ext2fs / getsize.c
index 39e471f..be06775 100644 (file)
@@ -1,20 +1,25 @@
 /*
  * 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 Public
- * License.
+ * 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>
 #include <errno.h>
 #endif
 #include <fcntl.h>
-#ifdef HAVE_LINUX_FD_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/ioctl.h>
 #include <sys/disklabel.h>
-#endif /* HAVE_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
 
-#ifdef APPLE_DARWIN
-#include <sys/ioctl.h>
-#include <sys/disk.h>
+#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 "windows.h"
 #include "winioctl.h"
 
-errcode_t ext2fs_get_device_size(const char *file, int blocksize,
-                                blk_t *retblocks)
+#if (_WIN32_WINNT >= 0x0500)
+#define HAVE_GET_FILE_SIZE_EX 1
+#endif
+
+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 */
 
-       dev = CreateFile(file, GENERIC_READ, 
+       dev = CreateFile(file, GENERIC_READ,
                         FILE_SHARE_READ | FILE_SHARE_WRITE ,
-                        NULL,  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,  NULL); 
+                        NULL,  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,  NULL);
+
        if (dev == INVALID_HANDLE_VALUE)
                return EBADF;
        if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO,
@@ -70,7 +96,7 @@ errcode_t ext2fs_get_device_size(const char *file, int blocksize,
                            &retbytes, NULL)) {
 
                *retblocks = pi.PartitionLength.QuadPart / blocksize;
-       
+
        } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY,
                                &gi, sizeof(DISK_GEOMETRY),
                                &gi, sizeof(DISK_GEOMETRY),
@@ -80,7 +106,19 @@ errcode_t ext2fs_get_device_size(const char *file, int blocksize,
                             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 */
 
        CloseHandle(dev);
        return 0;
@@ -102,77 +140,126 @@ static int valid_offset (int fd, ext2_loff_t offset)
 /*
  * Returns the number of blocks in a partition
  */
-errcode_t ext2fs_get_device_size(const char *file, int blocksize,
-                                blk_t *retblocks)
+errcode_t ext2fs_get_device_size2(const char *file, int blocksize,
+                                 blk64_t *retblocks)
 {
-       int     fd;
-#ifdef BLKGETSIZE
-       unsigned long   size;
-#endif
+       int     fd, rc = 0;
+       unsigned long long size64;
        ext2_loff_t high, low;
-#ifdef FDGETPRM
-       struct floppy_struct this_floppy;
-#endif
-#ifdef HAVE_SYS_DISKLABEL_H
-       int part;
-       struct disklabel lab;
-       struct partition *pp;
-       char ch;
-#endif /* HAVE_SYS_DISKLABEL_H */
 
-#ifdef HAVE_OPEN64
-       fd = open64(file, O_RDONLY);
-#else
-       fd = open(file, O_RDONLY);
-#endif
+       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
-       if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
-               close(fd);
-               *retblocks = size / (blocksize / 512);
-               return 0;
+       {
+               unsigned long   size;
+
+               if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+                       *retblocks = size / (blocksize / 512);
+                       goto out;
+               }
        }
 #endif
+
 #ifdef FDGETPRM
-       if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
-               close(fd);
-               *retblocks = this_floppy.size / (blocksize / 512);
-               return 0;
+       {
+               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
-       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) {
-                       close(fd);
-                       *retblocks = pp->p_size / (blocksize / 512);
-                       return 0;
+       {
+               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)
+       for (high = 1024; valid_offset(fd, high); high *= 2)
                low = high;
-       while (low < high - 1)
-       {
+       while (low < high - 1) {
                const ext2_loff_t mid = (low + high) / 2;
 
                if (valid_offset (fd, mid))
@@ -180,20 +267,37 @@ errcode_t ext2fs_get_device_size(const char *file, int blocksize,
                else
                        high = mid;
        }
-       valid_offset (fd, 0);
+       valid_offset(fd, 0);
+       size64 = low + 1;
+       *retblocks = size64 / blocksize;
+out:
        close(fd);
-       *retblocks = (low + 1) / blocksize;
-       return 0;
+       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)
 {
        blk_t   blocks;
        int     retval;
-       
+
        if (argc < 2) {
                fprintf(stderr, "Usage: %s device\n", argv[0]);
                exit(1);
@@ -205,7 +309,7 @@ int main(int argc, char **argv)
                        "while calling ext2fs_get_device_size");
                exit(1);
        }
-       printf("Device %s has %d 1k blocks.\n", argv[1], blocks);
+       printf("Device %s has %u 1k blocks.\n", argv[1], blocks);
        exit(0);
 }
 #endif