/* -*- 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 (c) 2013 Spectra Logic Corporation.
 * 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
 */

/*
 * Module Name:
 *
 *        acl_freebsd_sd.c
 *
 * Abstract:
 *
 *        Likewise Posix File System Driver (PVFS)
 *
 *        BSD File Flags NTFS File Attribute implementation
 *
 * Authors: Ken Merry <kenm@spectralogic.com>
 */

#include "pvfs.h"

#ifdef HAVE_STATFLAG_SUPPORT
VOID
PvfsGetFileAttributesFromStat(
    IN PPVFS_STAT pStat,
    OUT PFILE_ATTRIBUTES pAttributes
    )
{
    if (pStat->s_flags & UF_READONLY)
        *pAttributes |= FILE_ATTRIBUTE_READONLY;
    else
        *pAttributes &= ~FILE_ATTRIBUTE_READONLY;

    if (pStat->s_flags & UF_SYSTEM)
        *pAttributes |= FILE_ATTRIBUTE_SYSTEM;
    else
        *pAttributes &= ~FILE_ATTRIBUTE_SYSTEM;

    if (pStat->s_flags & UF_SPARSE)
        *pAttributes |= FILE_ATTRIBUTE_SPARSE_FILE;
    else
        *pAttributes &= ~FILE_ATTRIBUTE_SPARSE_FILE;

    if (pStat->s_flags & UF_OFFLINE)
        *pAttributes |= FILE_ATTRIBUTE_OFFLINE;
    else
        *pAttributes &= ~FILE_ATTRIBUTE_OFFLINE;

    if (pStat->s_flags & UF_REPARSE)
        *pAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
    else
        *pAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;

    if (pStat->s_flags & UF_HIDDEN)
        *pAttributes |= FILE_ATTRIBUTE_HIDDEN;
    else
        *pAttributes &= ~FILE_ATTRIBUTE_HIDDEN;

    if (pStat->s_flags & UF_ARCHIVE)
        *pAttributes |= FILE_ATTRIBUTE_ARCHIVE;
    else
        *pAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;

}

static
VOID
PvfsSetFileFlagsFromAttributes(
    IN FILE_ATTRIBUTES Attributes,
    OUT fflags_t *fflags
    )
{
    if (Attributes & FILE_ATTRIBUTE_READONLY)
        *fflags |= UF_READONLY;
    else
        *fflags &= ~UF_READONLY;

    if (Attributes & FILE_ATTRIBUTE_HIDDEN)
        *fflags |= UF_HIDDEN;
    else
        *fflags &= ~UF_HIDDEN;

    if (Attributes & FILE_ATTRIBUTE_SYSTEM)
        *fflags |= UF_SYSTEM;
    else
        *fflags &= ~UF_SYSTEM;
    
    if (Attributes & FILE_ATTRIBUTE_OFFLINE)
        *fflags |= UF_OFFLINE;
    else
        *fflags &= ~UF_OFFLINE;

    if (Attributes & FILE_ATTRIBUTE_ARCHIVE)
        *fflags |= UF_ARCHIVE;
    else
        *fflags &= ~UF_ARCHIVE;

    /*
     * The sparse and reparse point bits are currently filtered out in
     * PvfspSetAttributes().
     */
    if (Attributes & FILE_ATTRIBUTE_SPARSE_FILE)
        *fflags |= UF_SPARSE;
    else
        *fflags &= ~UF_SPARSE;

    /* The reparse bit is currently rejected by ZFS, and we really don't
       have a good way to handle it in FreeBSD.  So we clear it
       unconditionally. */
/*
    if (Attributes & FILE_ATTRIBUTE_REPARSE_POINT)
        *fflags |= UF_REPARSE;
    else
*/
        *fflags &= ~UF_REPARSE;

}

static
NTSTATUS
PvfsSetAttributesFlags(
    IN OPTIONAL PCSTR Filename,
    IN PPVFS_CCB pCcb,
    IN FILE_ATTRIBUTES Attributes
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    fflags_t NewFlags;
    PVFS_STAT Stat;
    int FlagSetCount = 1;
    int unixerr = 0;
    int i;

    /* First get the current file flags */
    if (Filename) {
        ntError = PvfsSysStat(Filename, &Stat);
        BAIL_ON_NT_STATUS(ntError);
    } else {
        ntError = PvfsSysFstat(pCcb->fd, &Stat);
        BAIL_ON_NT_STATUS(ntError);
    }

    NewFlags = Stat.s_flags;

    /* The attributes modify the existing file flags.  This allows the
       presence of other flags that don't currently map to Windows
       file attributes. */
    PvfsSetFileFlagsFromAttributes(Attributes, &NewFlags);

    /* If we do anything other than clear the archive flag by itself, ZFS
       will set the flag because of the other flag changes.  So if the
       UF_ARCHIVE flag is being cleared, and any other flags are
       being changed, then we need to clear the UF_ARCHIVE bit again in a
       separate subsequent call.  So we'll effectively issue the same call
       twice, and the second call will lead to the archive bit being
       cleared. */

    if (((NewFlags & UF_ARCHIVE) == 0) &&
        ((NewFlags & ~UF_ARCHIVE) != (Stat.s_flags & ~UF_ARCHIVE))) {
        FlagSetCount = 2;
    }

    for (i = 0; i < FlagSetCount; i++) {
        if (Filename) {
            if (chflags(Filename, NewFlags) == -1) {
                PVFS_BAIL_ON_UNIX_ERROR(unixerr, ntError);
            }
        } else {
            if (fchflags(pCcb->fd, NewFlags) == -1) {
                PVFS_BAIL_ON_UNIX_ERROR(unixerr, ntError);
            }
        }
    }

cleanup:
    return ntError;

error:
    goto cleanup;
}

NTSTATUS
PvfsSetFilenameAttributesFlags(
    IN PCSTR Filename,
    IN FILE_ATTRIBUTES Attributes
    )
{
    return PvfsSetAttributesFlags(
               Filename,
               NULL,
               Attributes);
}

NTSTATUS
PvfsSetFileAttributesFlags(
    IN PPVFS_CCB pCcb,
    IN FILE_ATTRIBUTES Attributes
    )
{
    return PvfsSetAttributesFlags(
               NULL,
               pCcb,
               Attributes);
}


#endif  /* HAVE_STATFLAG_SUPPORT */
