adtools/clib2

View on GitHub
library/fcntl_open.c

Summary

Maintainability
Test Coverage
/*
 * $Id: fcntl_open.c,v 1.18 2006-01-08 12:04:22 obarthel Exp $
 *
 * :ts=4
 *
 * Portable ISO 'C' (1994) runtime library for the Amiga computer
 * Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Neither the name of Olaf Barthel nor the names of contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _STDLIB_NULL_POINTER_CHECK_H
#include "stdlib_null_pointer_check.h"
#endif /* _STDLIB_NULL_POINTER_CHECK_H */

/****************************************************************************/

#ifndef _FCNTL_HEADERS_H
#include "fcntl_headers.h"
#endif /* _FCNTL_HEADERS_H */

/****************************************************************************/

#ifndef _STDLIB_MEMORY_H
#include "stdlib_memory.h"
#endif /* _STDLIB_MEMORY_H */

/****************************************************************************/

/* The following is not part of the ISO 'C' (1994) standard. */

/****************************************************************************/

/* This is used in place of ChangeMode() in order to work around a bug in
   dos.library V40 and below: a "NIL:" file handle will crash the
   caller of the ChangeMode() function. */
STATIC LONG
safe_change_mode(LONG type,BPTR file_handle,LONG mode)
{
    LONG result = DOSFALSE;

    #ifndef __amigaos4__
    {
        struct FileHandle * fh = (struct FileHandle *)BADDR(file_handle);

        assert( type == CHANGE_FH );

        if(fh == NULL || fh->fh_Type == NULL)
        {
            SetIoErr(ERROR_OBJECT_WRONG_TYPE);
            goto out;
        }
    }
    #endif /* __amigaos4__ */

    PROFILE_OFF();
    result = ChangeMode(type,file_handle,mode);
    PROFILE_ON();

 out:

    return(result);
}

/****************************************************************************/

int
open(const char *path_name, int open_flag, ... /* mode_t mode */ )
{
    DECLARE_UTILITYBASE();
    #if defined(UNIX_PATH_SEMANTICS)
    struct name_translation_info path_name_nti;
    #endif /* UNIX_PATH_SEMANTICS */
    D_S(struct FileInfoBlock,fib);
    struct SignalSemaphore * fd_lock;
    LONG is_file_system = FALSE;
    LONG open_mode;
    BPTR lock = ZERO;
    BPTR handle = ZERO;
    BOOL create_new_file = FALSE;
    LONG is_interactive;
    int fd_slot_number;
    struct fd * fd;
    int access_mode;
    int result = ERROR;
    int i;

    ENTER();

    SHOWSTRING(path_name);
    SHOWVALUE(open_flag);

    assert(path_name != NULL);
    assert( UtilityBase != NULL );

    if(__check_abort_enabled)
        __check_abort();

    __stdio_lock();

    #if defined(CHECK_FOR_NULL_POINTERS)
    {
        if(path_name == NULL)
        {
            SHOWMSG("path name is invalid");

            __set_errno(EFAULT);
            goto out;
        }
    }
    #endif /* CHECK_FOR_NULL_POINTERS */

    access_mode = (open_flag & 3);
    if(access_mode < O_RDONLY && access_mode > O_RDWR)
    {
        SHOWMSG("access mode is invalid");

        __set_errno(EINVAL);
        goto out;
    }

    fd_slot_number = __find_vacant_fd_entry();
    if(fd_slot_number < 0)
    {
        if(__grow_fd_table(0) < 0)
        {
            SHOWMSG("couldn't find a vacant file descriptor, and couldn't allocate one either");
            goto out;
        }

        fd_slot_number = __find_vacant_fd_entry();
        assert( fd_slot_number >= 0 );
    }

    #if defined(UNIX_PATH_SEMANTICS)
    {
        if(__unix_path_semantics)
        {
            if(path_name[0] == '\0')
            {
                SHOWMSG("no name given");

                __set_errno(ENOENT);
                goto out;
            }

            if(__translate_unix_to_amiga_path_name(&path_name,&path_name_nti) != 0)
                goto out;

            if(path_name_nti.is_root)
            {
                __set_errno(EACCES);
                goto out;
            }
        }
    }
    #endif /* UNIX_PATH_SEMANTICS */

    if (Strnicmp(path_name, "PIPE:",5) == SAME && FLAG_IS_SET(open_flag, O_CREAT))
    {
        open_mode = MODE_NEWFILE;
    }
    else if (Strnicmp(path_name,"NIL:",4) != SAME && FLAG_IS_SET(open_flag,O_CREAT))
    {
        if(FLAG_IS_SET(open_flag,O_EXCL))
        {
            LONG error;

            SHOWMSG("checking if the file to create already exists");

            PROFILE_OFF();
            lock = Lock((STRPTR)path_name,SHARED_LOCK);
            PROFILE_ON();

            if(lock != ZERO)
            {
                SHOWMSG("the file already exists");

                __set_errno(EEXIST);
                goto out;
            }

            error = IoErr();

            if(error == ERROR_OBJECT_WRONG_TYPE)
            {
                SHOWMSG("there's something not a directory on the path");

                __set_errno(ENOTDIR);
                goto out;
            }
            else if (error != ERROR_OBJECT_NOT_FOUND && error != ERROR_ACTION_NOT_KNOWN)
            {
                SHOWMSG("error accessing the object");

                __set_errno(__translate_io_error_to_errno(IoErr()));
                goto out;
            }

            SHOWMSG("the object does not already exist");
        }

        open_mode = MODE_READWRITE;

        if(FLAG_IS_SET(open_flag,O_TRUNC))
        {
            SHOWMSG("checking if the file to create already exists");

            PROFILE_OFF();
            lock = Lock((STRPTR)path_name,SHARED_LOCK);
            PROFILE_ON();

            if(lock != ZERO)
            {
                LONG status;

                PROFILE_OFF();
                status = Examine(lock,fib);
                PROFILE_ON();

                if(status == DOSFALSE)
                {
                    SHOWMSG("could not examine the object");

                    __set_errno(__translate_io_error_to_errno(IoErr()));
                    goto out;
                }

                /* We can open only files, but never directories. */
                if(fib->fib_DirEntryType >= 0)
                {
                    SHOWMSG("can't open a directory");

                    __set_errno(EISDIR);
                    goto out;
                }

                if(FLAG_IS_SET(fib->fib_Protection,FIBF_WRITE) ||
                   FLAG_IS_SET(fib->fib_Protection,FIBF_DELETE))
                {
                    SHOWMSG("this object is not write enabled");

                    __set_errno(EACCES);
                    goto out;
                }

                open_mode = MODE_NEWFILE;

                PROFILE_OFF();

                UnLock(lock);
                lock = ZERO;

                PROFILE_ON();
            }
            else
            {
                LONG error;

                error = IoErr();

                if(error == ERROR_OBJECT_WRONG_TYPE)
                {
                    SHOWMSG("there's something not a directory on the path");

                    __set_errno(ENOTDIR);
                    goto out;
                }
                else if (error != ERROR_OBJECT_NOT_FOUND && error != ERROR_ACTION_NOT_KNOWN)
                {
                    SHOWMSG("error accessing the object");

                    __set_errno(__translate_io_error_to_errno(IoErr()));
                    goto out;
                }
            }
        }

        create_new_file = TRUE;
    }
    else
    {
        open_mode = MODE_OLDFILE;
    }

    SHOWSTRING(path_name);

    PROFILE_OFF();
    handle = Open((STRPTR)path_name,open_mode);
    PROFILE_ON();

    if(handle == ZERO)
    {
        LONG io_err = IoErr();

        D(("the file '%s' didn't open in mode %ld",path_name,open_mode));
        __set_errno(__translate_access_io_error_to_errno(io_err));

        /* Check if ended up trying to open a directory as if
           it were a plain file. */
        if(io_err == ERROR_OBJECT_WRONG_TYPE)
        {
            PROFILE_OFF();

            lock = Lock((STRPTR)path_name,SHARED_LOCK);
            if(lock != ZERO)
            {
                if(Examine(lock,fib) && fib->fib_DirEntryType >= 0)
                    __set_errno(EISDIR);
            }

            PROFILE_ON();
        }

        goto out;
    }

    #if defined(__THREAD_SAFE)
    {
        fd_lock = __create_semaphore();
        if(fd_lock == NULL)
        {
            __set_errno(ENOMEM);
            goto out;
        }
    }
    #else
    {
        fd_lock = NULL;
    }
    #endif /* __THREAD_SAFE */

    fd = __fd[fd_slot_number];

    __initialize_fd(fd,__fd_hook_entry,handle,0,fd_lock);

    /* Figure out if this stream is attached to a console. */
    PROFILE_OFF();
    is_interactive = IsInteractive(handle);
    PROFILE_ON();

    if(is_interactive)
    {
        SET_FLAG(fd->fd_Flags,FDF_IS_INTERACTIVE);

        if(FLAG_IS_SET(open_flag,O_NONBLOCK))
        {
            SHOWMSG("enabling non-blocking mode");

            if(SetMode(handle,DOSTRUE)) /* single character mode */
                SET_FLAG(fd->fd_Flags,FDF_NON_BLOCKING);
        }
    }
    else
    {
        size_t len;

        len = 0;

        for(i = 0 ; path_name[i] != '\0' ; i++)
        {
            if(path_name[i] == ':')
            {
                len = i+1;
                break;
            }
        }

        if(len > 0)
        {
            char * path_name_copy;

            path_name_copy = malloc(len+1);
            if(path_name_copy != NULL)
            {
                memmove(path_name_copy,path_name,len);
                path_name_copy[len] = '\0';

                PROFILE_OFF();
                is_file_system = IsFileSystem(path_name_copy);
                PROFILE_ON();

                free(path_name_copy);
            }
        }
        else
        {
            PROFILE_OFF();
            is_file_system = IsFileSystem("");
            PROFILE_ON();
        }

        if(is_file_system)
        {
            /* We opened the file in exclusive access mode. Switch it back
               into shared access mode so that its contents can be read
               while it's still open. */
            if(open_mode == MODE_NEWFILE)
                safe_change_mode(CHANGE_FH,handle,SHARED_LOCK);

            /* We should be able to seek in this file. */
            SET_FLAG(fd->fd_Flags,FDF_CACHE_POSITION);
        }
    }

    if(FLAG_IS_SET(open_flag,O_APPEND))
    {
        SHOWMSG("appending; seeking to end of file");

        PROFILE_OFF();
        Seek(handle,0,OFFSET_END);
        PROFILE_ON();

        SET_FLAG(fd->fd_Flags,FDF_APPEND);
    }

    switch(access_mode)
    {
        case O_RDONLY:

            SET_FLAG(fd->fd_Flags,FDF_READ);
            break;

        case O_WRONLY:

            SET_FLAG(fd->fd_Flags,FDF_WRITE);
            break;

        case O_RDWR:

            SET_FLAG(fd->fd_Flags,FDF_READ);
            SET_FLAG(fd->fd_Flags,FDF_WRITE);
            break;
    }

    if(create_new_file && is_file_system)
        SET_FLAG(fd->fd_Flags,FDF_CREATED);

    SET_FLAG(fd->fd_Flags,FDF_IN_USE);

    result = fd_slot_number;

    handle = ZERO;

    assert( result != ERROR );

 out:

    PROFILE_OFF();

    if(handle != ZERO)
        Close(handle);

    UnLock(lock);

    __stdio_unlock();

    PROFILE_ON();

    RETURN(result);
    return(result);
}