/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * Editor Settings: expandtabs and use 4 spaces for indentation */

/*
 * Copyright Likewise Software
 * All rights reserved.
 *
 * 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 2 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
 * <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *        attrib.c
 *
 * Abstract:
 *
 *        Likewise Posix File System Driver (PVFS)
 *
 *        Supporting DOS Attribute routines
 *
 * Authors: Gerald Carter <gcarter@likewise.com>
 */

#include "pvfs.h"


////////////////////////////////////////////////////////////////////////

static
NTSTATUS
PvfspGetAttributes(
    IN OPTIONAL PCSTR Filename,
    IN OPTIONAL PPVFS_CCB pCcb,
    OUT PFILE_ATTRIBUTES pAttributes
    )
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    FILE_ATTRIBUTES attributes = 0;

    *pAttributes = 0;

    if ((!Filename && !pCcb) || (Filename && pCcb))
    {
        // Have to have only one of a Filename or a CCB
        status = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(status);
    }

    // We prefer stat(2) flags to extended attributes if they're available.
#ifndef HAVE_STATFLAG_SUPPORT
#ifdef HAVE_EA_SUPPORT
    if (Filename)
    {
        status = PvfsGetFilenameAttributesXattr(Filename, &attributes);
    }
    else
    {
        status = PvfsGetFileAttributesXattr(pCcb, &attributes);
    }
#endif
#endif /* HAVE_STATFLAG_SUPPORT */

    // Fallback to generating a default attribute set

    if (!NT_SUCCESS(status))
    {
        PSTR pszRelativeFilename = NULL;

        if (Filename)
        {
            status = PvfsFileBasename(&pszRelativeFilename,Filename);
            BAIL_ON_NT_STATUS(status);
        }
        else
        {
            status = PvfsFileBasename(
                         &pszRelativeFilename,
                         pCcb->pScb->pOwnerFcb->pszFilename);
            BAIL_ON_NT_STATUS(status);
        }

        // Hide 'dot' files (except "." and "..")

        if (!RtlCStringIsEqual(pszRelativeFilename, ".", FALSE) &&
            !RtlCStringIsEqual(pszRelativeFilename, "..", FALSE))
        {
            if (*pszRelativeFilename == '.') {
                attributes |= FILE_ATTRIBUTE_HIDDEN;
            }
        }

        RTL_FREE(&pszRelativeFilename);
    }

    // Some attributes are properties on the file and not
    // not settable.  Make sure to clear and then reset if
    // necessary

    attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY);

    if (Filename)
    {
        PVFS_STAT statBuf = { 0 };

        status = PvfsSysStat(Filename, &statBuf);
        BAIL_ON_NT_STATUS(status);

        if (S_ISDIR(statBuf.s_mode))
        {
            attributes |= FILE_ATTRIBUTE_DIRECTORY;
        }
        else
        {
            attributes |= FILE_ATTRIBUTE_ARCHIVE;
        }
#ifdef HAVE_STATFLAG_SUPPORT
        PvfsGetFileAttributesFromStat(&statBuf, &attributes);
#endif
    }
    else
    {
#ifdef HAVE_STATFLAG_SUPPORT
        PVFS_STAT statBuf = { 0 };
#endif

        if (pCcb->CreateOptions & FILE_DIRECTORY_FILE)
        {
            attributes |= FILE_ATTRIBUTE_DIRECTORY;
        }
        else
        {
            attributes |= FILE_ATTRIBUTE_ARCHIVE;
        }
#ifdef HAVE_STATFLAG_SUPPORT
        status = PvfsSysFstat(pCcb->fd, &statBuf);
        BAIL_ON_NT_STATUS(status);

        PvfsGetFileAttributesFromStat(&statBuf, &attributes);
#endif
    }

    // FILE_ATTRIBUTE_NORMAL is only valid when the file
    // has no other attributes set (according to SetFileAttribues()
    // in the MS SDK)

    if (attributes == 0)
    {
        attributes = FILE_ATTRIBUTE_NORMAL;
    }

    *pAttributes = attributes;

error:

    return status;
}

////////////////////////////////////////////////////////////////////////

NTSTATUS
PvfsGetFileAttributes(
    IN PPVFS_CCB pCcb,
    OUT PFILE_ATTRIBUTES pAttributes
    )
{
    return PvfspGetAttributes(
               NULL,
               pCcb,
               pAttributes);
}

////////////////////////////////////////////////////////////////////////

NTSTATUS
PvfsGetFilenameAttributes(
    IN PCSTR Filename,
    OUT PFILE_ATTRIBUTES pAttributes
    )
{
    return PvfspGetAttributes(
               Filename,
               NULL,
               pAttributes);
}

////////////////////////////////////////////////////////////////////////

static
NTSTATUS
PvfspSetAttributes(
    IN OPTIONAL PCSTR Filename,
    IN OPTIONAL PPVFS_CCB pCcb,
    IN FILE_ATTRIBUTES Attributes
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    FILE_ATTRIBUTES AttribNotSettable = FILE_ATTRIBUTE_ENCRYPTED |
                                        FILE_ATTRIBUTE_DEVICE |
                                        FILE_ATTRIBUTE_COMPRESSED |
                                        FILE_ATTRIBUTE_REPARSE_POINT |
                                        FILE_ATTRIBUTE_SPARSE_FILE;

    if ((!Filename && !pCcb) || (Filename && pCcb))
    {
        // Have to have only one of a Filename or a CCB
        status = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(status);
    }

    if (Attributes == 0)
    {
        // 0 means do nothing (FILE_ATTRIBUTE_NORMAL means reset)
        status = STATUS_SUCCESS;
        goto error;
    }

    /* Use PvfsSetFileAttributesEx() for IoControl */
#if 0
    if (Attributes & AttribNotSettable)
    {
        status = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(status);
    }
#else
    /* Clear "un-settable" bits for now rather than failing.
       Windows XP Explorer.exer drags and drop of sparse files
       sets the FILE_ATTRIBUTE_SPARSE_FILE in the NTcreate&X */

    Attributes &= ~AttribNotSettable;
#endif

    /* Clear some bits that should not be stored */
    Attributes &= ~(FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_NORMAL);


#if defined(HAVE_STATFLAG_SUPPORT)
    if (Filename)
    {
        status = PvfsSetFilenameAttributesFlags(Filename, Attributes);
    }
    else
    {
        status = PvfsSetFileAttributesFlags(pCcb, Attributes);
    }
#elif defined(HAVE_EA_SUPPORT)
    if (Filename)
    {
        status = PvfsSetFilenameAttributesXattr(Filename, Attributes);
    }
    else
    {
        status = PvfsSetFileAttributesXattr(pCcb, Attributes);
    }
#else
    status = STATUS_SUCCESS;
#endif

    BAIL_ON_NT_STATUS(status);

    if (pCcb)
    {
        pCcb->ChangeEvent |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
        if (IsSetFlag(pCcb->Flags, PVFS_CCB_FLAG_CREATE_COMPLETE))
        {
            PvfsNotifyScheduleFullReport(
                pCcb->pScb->pOwnerFcb,
                pCcb->ChangeEvent,
                FILE_ACTION_MODIFIED,
                pCcb->pScb->pOwnerFcb->pszFilename);
            pCcb->ChangeEvent = 0;
        }
    }

error:

    return status;
}

////////////////////////////////////////////////////////////////////////

NTSTATUS
PvfsSetFileAttributes(
    IN PPVFS_CCB pCcb,
    IN FILE_ATTRIBUTES Attributes
    )
{
    return PvfspSetAttributes(
               NULL,
               pCcb,
               Attributes);
}

////////////////////////////////////////////////////////////////////////

NTSTATUS
PvfsSetFilenameAttributes(
    IN PCSTR Filename,
    IN FILE_ATTRIBUTES Attributes
    )
{
    return PvfspSetAttributes(
               Filename,
               NULL,
               Attributes);
}

/****************************************************************
 Used by the DeviceIoControl for setting attributes
 ***************************************************************/

NTSTATUS
PvfsSetFileAttributesEx(
    IN PPVFS_CCB pCcb,
    IN FILE_ATTRIBUTES Attributes
    )
{
    NTSTATUS ntError = STATUS_ACCESS_DENIED;
    FILE_ATTRIBUTES AttribNotSettable = FILE_ATTRIBUTE_ENCRYPTED |
                                        FILE_ATTRIBUTE_DEVICE;
    FILE_ATTRIBUTES AttribNotSupported = FILE_ATTRIBUTE_COMPRESSED |
                                        FILE_ATTRIBUTE_REPARSE_POINT |
                                        FILE_ATTRIBUTE_SPARSE_FILE;

    /* Not all attributes are settable */

    if (Attributes & AttribNotSettable) {
        ntError = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(ntError);
    }

    /* Not all attributes are supported */

    if (Attributes & AttribNotSupported) {
        ntError = STATUS_NOT_SUPPORTED;
        BAIL_ON_NT_STATUS(ntError);
    }

    /* Clear some bits that should not be stored */

    Attributes &= ~(FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_NORMAL);

#ifdef HAVE_EA_SUPPORT
    ntError = PvfsSetFileAttributesXattr(pCcb, Attributes);
#endif

    BAIL_ON_NT_STATUS(ntError);

cleanup:
    return ntError;

error:
    goto cleanup;
}

////////////////////////////////////////////////////////////////////////

VOID
PvfsAttributeSetArchive(
    IN PPVFS_CCB pCcb
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    FILE_ATTRIBUTES attributes = 0;
    BOOLEAN fcbLocked = FALSE;

    if (PvfsIsDefaultStream(pCcb->pScb))
    {
        // File::$DATA
        status = PvfsGetFileAttributes(pCcb, &attributes);
        BAIL_ON_NT_STATUS(status);

        attributes |= FILE_ATTRIBUTE_ARCHIVE;

        status = PvfsSetFileAttributes(pCcb, attributes);
        BAIL_ON_NT_STATUS(status);
    }
    else
    {
        LWIO_LOCK_RWMUTEX_EXCLUSIVE(
            fcbLocked,
            &pCcb->pScb->pOwnerFcb->BaseControlBlock.RwLock);

        // File:Stream:$DATA
        status = PvfsGetFilenameAttributes(
                     pCcb->pScb->pOwnerFcb->pszFilename,
                     &attributes);
        BAIL_ON_NT_STATUS(status);

        attributes |= FILE_ATTRIBUTE_ARCHIVE;

        status = PvfsSetFilenameAttributes(
                     pCcb->pScb->pOwnerFcb->pszFilename,
                     attributes);

        BAIL_ON_NT_STATUS(status);

        LWIO_UNLOCK_RWMUTEX(
            fcbLocked,
            &pCcb->pScb->pOwnerFcb->BaseControlBlock.RwLock);
    }


error:
    LWIO_UNLOCK_RWMUTEX(
        fcbLocked,
        &pCcb->pScb->pOwnerFcb->BaseControlBlock.RwLock);

    return;
}
