Allow BIO_s_file to open and sequentially access files larger than 2GB on
[openssl.git] / crypto / bio / bss_file.c
index ab73b23d0ada0e7f82db3475775e16959f1ce113..ccc741556ea54aa59151ad1c3094872f5b8abee4 100644 (file)
 #ifndef HEADER_BSS_FILE_C
 #define HEADER_BSS_FILE_C
 
+#if defined(__linux) || defined(__sun) || defined(__hpux)
+/* Following definition aliases fopen to fopen64 on above mentioned
+ * platforms. This makes it possible to open and sequentially access
+ * files larger than 2GB from 32-bit application. It does not allow to
+ * traverse them beyond 2GB with fseek/ftell, but on the other hand *no*
+ * 32-bit platform permits that, not with fseek/ftell. Not to mention
+ * that breaking 2GB limit for seeking would require surgery to *our*
+ * API. But sequential access suffices for practical cases when you
+ * can run into large files, such as fingerprinting, so we can let API
+ * alone. For reference, the list of 32-bit platforms which allow for
+ * sequential access of large files without extra "magic" comprise *BSD,
+ * Darwin, IRIX...
+ */
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+#endif
+
 #include <stdio.h>
 #include <errno.h>
 #include "cryptlib.h"
-#include <openssl/bio.h>
+#include "bio_lcl.h"
 #include <openssl/err.h>
 
-#if !defined(NO_STDIO)
+#if !defined(OPENSSL_NO_STDIO)
 
-#ifndef NOPROTO
-static int MS_CALLBACK file_write(BIO *h,char *buf,int num);
-static int MS_CALLBACK file_read(BIO *h,char *buf,int size);
-static int MS_CALLBACK file_puts(BIO *h,char *str);
-static int MS_CALLBACK file_gets(BIO *h,char *str,int size);
-static long MS_CALLBACK file_ctrl(BIO *h,int cmd,long arg1,char *arg2);
+static int MS_CALLBACK file_write(BIO *h, const char *buf, int num);
+static int MS_CALLBACK file_read(BIO *h, char *buf, int size);
+static int MS_CALLBACK file_puts(BIO *h, const char *str);
+static int MS_CALLBACK file_gets(BIO *h, char *str, int size);
+static long MS_CALLBACK file_ctrl(BIO *h, int cmd, long arg1, void *arg2);
 static int MS_CALLBACK file_new(BIO *h);
 static int MS_CALLBACK file_free(BIO *data);
-#else
-static int MS_CALLBACK file_write();
-static int MS_CALLBACK file_read();
-static int MS_CALLBACK file_puts();
-static int MS_CALLBACK file_gets();
-static long MS_CALLBACK file_ctrl();
-static int MS_CALLBACK file_new();
-static int MS_CALLBACK file_free();
-#endif
-
 static BIO_METHOD methods_filep=
        {
        BIO_TYPE_FILE,
@@ -102,9 +109,10 @@ static BIO_METHOD methods_filep=
        file_ctrl,
        file_new,
        file_free,
+       NULL,
        };
 
-BIO *BIO_new_file(char *filename, char *mode)
+BIO *BIO_new_file(const char *filename, const char *mode)
        {
        BIO *ret;
        FILE *file;
@@ -113,12 +121,16 @@ BIO *BIO_new_file(char *filename, char *mode)
                {
                SYSerr(SYS_F_FOPEN,get_last_sys_error());
                ERR_add_error_data(5,"fopen('",filename,"','",mode,"')");
-               BIOerr(BIO_F_BIO_NEW_FILE,ERR_R_SYS_LIB);
+               if (errno == ENOENT)
+                       BIOerr(BIO_F_BIO_NEW_FILE,BIO_R_NO_SUCH_FILE);
+               else
+                       BIOerr(BIO_F_BIO_NEW_FILE,ERR_R_SYS_LIB);
                return(NULL);
                }
        if ((ret=BIO_new(BIO_s_file_internal())) == NULL)
                return(NULL);
 
+       BIO_clear_flags(ret,BIO_FLAGS_UPLINK); /* we did fopen -> we disengage UPLINK */
        BIO_set_fp(ret,file,BIO_CLOSE);
        return(ret);
        }
@@ -130,6 +142,7 @@ BIO *BIO_new_fp(FILE *stream, int close_flag)
        if ((ret=BIO_new(BIO_s_file())) == NULL)
                return(NULL);
 
+       BIO_set_flags(ret,BIO_FLAGS_UPLINK); /* redundant, left for documentation puposes */
        BIO_set_fp(ret,stream,close_flag);
        return(ret);
        }
@@ -144,6 +157,7 @@ static int MS_CALLBACK file_new(BIO *bi)
        bi->init=0;
        bi->num=0;
        bi->ptr=NULL;
+       bi->flags=BIO_FLAGS_UPLINK; /* default to UPLINK */
        return(1);
        }
 
@@ -154,8 +168,12 @@ static int MS_CALLBACK file_free(BIO *a)
                {
                if ((a->init) && (a->ptr != NULL))
                        {
-                       fclose((FILE *)a->ptr);
+                       if (a->flags&BIO_FLAGS_UPLINK)
+                               UP_fclose (a->ptr);
+                       else
+                               fclose (a->ptr);
                        a->ptr=NULL;
+                       a->flags=BIO_FLAGS_UPLINK;
                        }
                a->init=0;
                }
@@ -168,28 +186,41 @@ static int MS_CALLBACK file_read(BIO *b, char *out, int outl)
 
        if (b->init && (out != NULL))
                {
-               ret=fread(out,1,(int)outl,(FILE *)b->ptr);
+               if (b->flags&BIO_FLAGS_UPLINK)
+                       ret=UP_fread(out,1,(int)outl,b->ptr);
+               else
+                       ret=fread(out,1,(int)outl,(FILE *)b->ptr);
+               if(ret == 0 && (b->flags&BIO_FLAGS_UPLINK)?UP_ferror((FILE *)b->ptr):ferror((FILE *)b->ptr))
+                       {
+                       SYSerr(SYS_F_FREAD,get_last_sys_error());
+                       BIOerr(BIO_F_FILE_READ,ERR_R_SYS_LIB);
+                       ret=-1;
+                       }
                }
        return(ret);
        }
 
-static int MS_CALLBACK file_write(BIO *b, char *in, int inl)
+static int MS_CALLBACK file_write(BIO *b, const char *in, int inl)
        {
        int ret=0;
 
        if (b->init && (in != NULL))
                {
-               if (fwrite(in,(int)inl,1,(FILE *)b->ptr))
+               if (b->flags&BIO_FLAGS_UPLINK)
+                       ret=UP_fwrite(in,(int)inl,1,b->ptr);
+               else
+                       ret=fwrite(in,(int)inl,1,(FILE *)b->ptr);
+               if (ret)
                        ret=inl;
                /* ret=fwrite(in,1,(int)inl,(FILE *)b->ptr); */
-               /* acording to Tim Hudson <tjh@cryptsoft.com>, the commented
+               /* according to Tim Hudson <tjh@cryptsoft.com>, the commented
                 * out version above can cause 'inl' write calls under
                 * some stupid stdio implementations (VMS) */
                }
        return(ret);
        }
 
-static long MS_CALLBACK file_ctrl(BIO *b, int cmd, long num, char *ptr)
+static long MS_CALLBACK file_ctrl(BIO *b, int cmd, long num, void *ptr)
        {
        long ret=1;
        FILE *fp=(FILE *)b->ptr;
@@ -200,27 +231,84 @@ static long MS_CALLBACK file_ctrl(BIO *b, int cmd, long num, char *ptr)
                {
        case BIO_C_FILE_SEEK:
        case BIO_CTRL_RESET:
-               ret=(long)fseek(fp,num,0);
+               if (b->flags&BIO_FLAGS_UPLINK)
+                       ret=(long)UP_fseek(b->ptr,num,0);
+               else
+                       ret=(long)fseek(fp,num,0);
                break;
        case BIO_CTRL_EOF:
-               ret=(long)feof(fp);
+               if (b->flags&BIO_FLAGS_UPLINK)
+                       ret=(long)UP_feof(fp);
+               else
+                       ret=(long)feof(fp);
                break;
        case BIO_C_FILE_TELL:
        case BIO_CTRL_INFO:
-               ret=ftell(fp);
+               if (b->flags&BIO_FLAGS_UPLINK)
+                       ret=UP_ftell(b->ptr);
+               else
+                       ret=ftell(fp);
                break;
        case BIO_C_SET_FILE_PTR:
                file_free(b);
-               b->shutdown=(int)num;
-               b->ptr=(char *)ptr;
+               b->shutdown=(int)num&BIO_CLOSE;
+               b->ptr=ptr;
                b->init=1;
-#if defined(MSDOS) || defined(WINDOWS)
+#if BIO_FLAGS_UPLINK!=0
+#if defined(__MINGW32__) && defined(__MSVCRT__) && !defined(_IOB_ENTRIES)
+#define _IOB_ENTRIES 20
+#endif
+#if defined(_IOB_ENTRIES)
+               /* Safety net to catch purely internal BIO_set_fp calls */
+               if ((size_t)ptr >= (size_t)stdin &&
+                   (size_t)ptr <  (size_t)(stdin+_IOB_ENTRIES))
+                       BIO_clear_flags(b,BIO_FLAGS_UPLINK);
+#endif
+#endif
+#ifdef UP_fsetmode
+               if (b->flags&BIO_FLAGS_UPLINK)
+                       UP_fsetmode(b->ptr,num&BIO_FP_TEXT?'t':'b');
+               else
+#endif
+               {
+#if defined(OPENSSL_SYS_WINDOWS)
+               int fd = fileno((FILE*)ptr);
+               if (num & BIO_FP_TEXT)
+                       _setmode(fd,_O_TEXT);
+               else
+                       _setmode(fd,_O_BINARY);
+#elif defined(OPENSSL_SYS_NETWARE) && defined(NETWARE_CLIB)
+               int fd = fileno((FILE*)ptr);
+         /* Under CLib there are differences in file modes
+         */
+               if (num & BIO_FP_TEXT)
+                       _setmode(fd,O_TEXT);
+               else
+                       _setmode(fd,O_BINARY);
+#elif defined(OPENSSL_SYS_MSDOS)
+               int fd = fileno((FILE*)ptr);
                /* Set correct text/binary mode */
                if (num & BIO_FP_TEXT)
-                       _setmode(fileno((FILE *)ptr),_O_TEXT);
+                       _setmode(fd,_O_TEXT);
+               /* Dangerous to set stdin/stdout to raw (unless redirected) */
+               else
+                       {
+                       if (fd == STDIN_FILENO || fd == STDOUT_FILENO)
+                               {
+                               if (isatty(fd) <= 0)
+                                       _setmode(fd,_O_BINARY);
+                               }
+                       else
+                               _setmode(fd,_O_BINARY);
+                       }
+#elif defined(OPENSSL_SYS_OS2)
+               int fd = fileno((FILE*)ptr);
+               if (num & BIO_FP_TEXT)
+                       setmode(fd, O_TEXT);
                else
-                       _setmode(fileno((FILE *)ptr),_O_BINARY);
+                       setmode(fd, O_BINARY);
 #endif
+               }
                break;
        case BIO_C_SET_FILENAME:
                file_free(b);
@@ -228,22 +316,28 @@ static long MS_CALLBACK file_ctrl(BIO *b, int cmd, long num, char *ptr)
                if (num & BIO_FP_APPEND)
                        {
                        if (num & BIO_FP_READ)
-                               strcpy(p,"a+");
-                       else    strcpy(p,"a");
+                               BUF_strlcpy(p,"a+",sizeof p);
+                       else    BUF_strlcpy(p,"a",sizeof p);
                        }
                else if ((num & BIO_FP_READ) && (num & BIO_FP_WRITE))
-                       strcpy(p,"r+");
+                       BUF_strlcpy(p,"r+",sizeof p);
                else if (num & BIO_FP_WRITE)
-                       strcpy(p,"w");
+                       BUF_strlcpy(p,"w",sizeof p);
                else if (num & BIO_FP_READ)
-                       strcpy(p,"r");
+                       BUF_strlcpy(p,"r",sizeof p);
                else
                        {
                        BIOerr(BIO_F_FILE_CTRL,BIO_R_BAD_FOPEN_MODE);
                        ret=0;
                        break;
                        }
-#if defined(MSDOS) || defined(WINDOWS)
+#if defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_OS2) || defined(OPENSSL_SYS_WIN32_CYGWIN)
+               if (!(num & BIO_FP_TEXT))
+                       strcat(p,"b");
+               else
+                       strcat(p,"t");
+#endif
+#if defined(OPENSSL_SYS_NETWARE)
                if (!(num & BIO_FP_TEXT))
                        strcat(p,"b");
                else
@@ -258,8 +352,9 @@ static long MS_CALLBACK file_ctrl(BIO *b, int cmd, long num, char *ptr)
                        ret=0;
                        break;
                        }
-               b->ptr=(char *)fp;
+               b->ptr=fp;
                b->init=1;
+               BIO_clear_flags(b,BIO_FLAGS_UPLINK); /* we did fopen -> we disengage UPLINK */
                break;
        case BIO_C_GET_FILE_PTR:
                /* the ptr parameter is actually a FILE ** in this case. */
@@ -276,7 +371,10 @@ static long MS_CALLBACK file_ctrl(BIO *b, int cmd, long num, char *ptr)
                b->shutdown=(int)num;
                break;
        case BIO_CTRL_FLUSH:
-               fflush((FILE *)b->ptr);
+               if (b->flags&BIO_FLAGS_UPLINK)
+                       UP_fflush(b->ptr);
+               else
+                       fflush((FILE *)b->ptr);
                break;
        case BIO_CTRL_DUP:
                ret=1;
@@ -304,7 +402,7 @@ static int MS_CALLBACK file_gets(BIO *bp, char *buf, int size)
        return(ret);
        }
 
-static int MS_CALLBACK file_puts(BIO *bp, char *str)
+static int MS_CALLBACK file_puts(BIO *bp, const char *str)
        {
        int n,ret;
 
@@ -313,7 +411,7 @@ static int MS_CALLBACK file_puts(BIO *bp, char *str)
        return(ret);
        }
 
-#endif /* NO_STDIO */
+#endif /* OPENSSL_NO_STDIO */
 
 #endif /* HEADER_BSS_FILE_C */