/**
 * Normalizes the give mode mask.
 *
 * It will create the missing unix or dos mask from the other (one
 * of them is required by all APIs), and guess the file type if that's
 * missing.
 *
 * @returns Normalized file mode.
 * @param   fMode       The mode mask that may contain a partial/incomplete mask.
 * @param   pszName     The filename which this applies to (exe check).
 * @param   cbName      The length of that filename. (optional, set 0)
 */
RTFMODE rtFsModeNormalize(RTFMODE fMode, const char *pszName, size_t cbName)
{
    if (!(fMode & RTFS_UNIX_MASK))
        fMode = rtFsModeFromDos(fMode, pszName, cbName);
    else if (!(fMode & RTFS_DOS_MASK))
        fMode = rtFsModeFromUnix(fMode, pszName, cbName);
    else if (!(fMode & RTFS_TYPE_MASK))
        fMode |= fMode & RTFS_DOS_DIRECTORY ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE;
    else if (RTFS_IS_DIRECTORY(fMode))
        fMode |= RTFS_DOS_DIRECTORY;
    return fMode;
}
/**
 * Internal worker function which setups RTFSOBJINFO based on a UNIX stat struct.
 *
 * @param   pObjInfo        The file system object info structure to setup.
 * @param   pStat           The stat structure to use.
 * @param   pszName         The filename which this applies to (exe/hidden check).
 * @param   cbName          The length of that filename. (optional, set 0)
 */
void rtFsConvertStatToObjInfo(PRTFSOBJINFO pObjInfo, const struct stat *pStat, const char *pszName, unsigned cbName)
{
    pObjInfo->cbObject    = pStat->st_size;
    pObjInfo->cbAllocated = pStat->st_blocks * DEV_BSIZE;

#ifdef HAVE_STAT_NSEC
    RTTimeSpecAddNano(RTTimeSpecSetSeconds(&pObjInfo->AccessTime,       pStat->st_atime),     pStat->st_atimensec);
    RTTimeSpecAddNano(RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, pStat->st_mtime),     pStat->st_mtimensec);
    RTTimeSpecAddNano(RTTimeSpecSetSeconds(&pObjInfo->ChangeTime,       pStat->st_ctime),     pStat->st_ctimensec);
# ifdef HAVE_STAT_BIRTHTIME
    RTTimeSpecAddNano(RTTimeSpecSetSeconds(&pObjInfo->BirthTime,        pStat->st_birthtime), pStat->st_birthtimensec);
# endif

#elif defined(HAVE_STAT_TIMESPEC_BRIEF)
    RTTimeSpecSetTimespec(&pObjInfo->AccessTime,       &pStat->st_atim);
    RTTimeSpecSetTimespec(&pObjInfo->ModificationTime, &pStat->st_mtim);
    RTTimeSpecSetTimespec(&pObjInfo->ChangeTime,       &pStat->st_ctim);
# ifdef HAVE_STAT_BIRTHTIME
    RTTimeSpecSetTimespec(&pObjInfo->BirthTime,        &pStat->st_birthtim);
# endif

#elif defined(HAVE_STAT_TIMESPEC)
    RTTimeSpecSetTimespec(&pObjInfo->AccessTime,       pStat->st_atimespec);
    RTTimeSpecSetTimespec(&pObjInfo->ModificationTime, pStat->st_mtimespec);
    RTTimeSpecSetTimespec(&pObjInfo->ChangeTime,       pStat->st_ctimespec);
# ifdef HAVE_STAT_BIRTHTIME
    RTTimeSpecSetTimespec(&pObjInfo->BirthTime,        pStat->st_birthtimespec);
# endif

#else /* just the normal stuff */
    RTTimeSpecSetSeconds(&pObjInfo->AccessTime,       pStat->st_atime);
    RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, pStat->st_mtime);
    RTTimeSpecSetSeconds(&pObjInfo->ChangeTime,       pStat->st_ctime);
# ifdef HAVE_STAT_BIRTHTIME
    RTTimeSpecSetSeconds(&pObjInfo->BirthTime,        pStat->st_birthtime);
# endif
#endif
#ifndef HAVE_STAT_BIRTHTIME
    pObjInfo->BirthTime = pObjInfo->ChangeTime;
#endif

    /* the file mode */
    RTFMODE fMode = pStat->st_mode & RTFS_UNIX_MASK;
    Assert(RTFS_UNIX_ISUID == S_ISUID);
    Assert(RTFS_UNIX_ISGID == S_ISGID);
#ifdef S_ISTXT
    Assert(RTFS_UNIX_ISTXT == S_ISTXT);
#elif defined(S_ISVTX)
    Assert(RTFS_UNIX_ISTXT == S_ISVTX);
#else
#error "S_ISVTX / S_ISTXT isn't defined"
#endif
    Assert(RTFS_UNIX_IRWXU == S_IRWXU);
    Assert(RTFS_UNIX_IRUSR == S_IRUSR);
    Assert(RTFS_UNIX_IWUSR == S_IWUSR);
    Assert(RTFS_UNIX_IXUSR == S_IXUSR);
    Assert(RTFS_UNIX_IRWXG == S_IRWXG);
    Assert(RTFS_UNIX_IRGRP == S_IRGRP);
    Assert(RTFS_UNIX_IWGRP == S_IWGRP);
    Assert(RTFS_UNIX_IXGRP == S_IXGRP);
    Assert(RTFS_UNIX_IRWXO == S_IRWXO);
    Assert(RTFS_UNIX_IROTH == S_IROTH);
    Assert(RTFS_UNIX_IWOTH == S_IWOTH);
    Assert(RTFS_UNIX_IXOTH == S_IXOTH);
    Assert(RTFS_TYPE_FIFO == S_IFIFO);
    Assert(RTFS_TYPE_DEV_CHAR == S_IFCHR);
    Assert(RTFS_TYPE_DIRECTORY == S_IFDIR);
    Assert(RTFS_TYPE_DEV_BLOCK == S_IFBLK);
    Assert(RTFS_TYPE_FILE == S_IFREG);
    Assert(RTFS_TYPE_SYMLINK == S_IFLNK);
    Assert(RTFS_TYPE_SOCKET == S_IFSOCK);
#ifdef S_IFWHT
    Assert(RTFS_TYPE_WHITEOUT == S_IFWHT);
#endif
    Assert(RTFS_TYPE_MASK == S_IFMT);

    pObjInfo->Attr.fMode  = rtFsModeFromUnix(fMode, pszName, cbName);

    /* additional unix attribs */
    pObjInfo->Attr.enmAdditional          = RTFSOBJATTRADD_UNIX;
    pObjInfo->Attr.u.Unix.uid             = pStat->st_uid;
    pObjInfo->Attr.u.Unix.gid             = pStat->st_gid;
    pObjInfo->Attr.u.Unix.cHardlinks      = pStat->st_nlink;
    pObjInfo->Attr.u.Unix.INodeIdDevice   = pStat->st_dev;
    pObjInfo->Attr.u.Unix.INodeId         = pStat->st_ino;
#ifdef HAVE_STAT_FLAGS
    pObjInfo->Attr.u.Unix.fFlags          = pStat->st_flags;
#else
    pObjInfo->Attr.u.Unix.fFlags          = 0;
#endif
#ifdef HAVE_STAT_GEN
    pObjInfo->Attr.u.Unix.GenerationId    = pStat->st_gen;
#else
    pObjInfo->Attr.u.Unix.GenerationId    = 0;
#endif
    pObjInfo->Attr.u.Unix.Device          = pStat->st_rdev;
}