/**
 * 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;
}
Ejemplo n.º 2
0
/**
 * Translate a TAR header to an IPRT object info structure with additional UNIX
 * attributes.
 *
 * This completes the validation done by rtZipTarHdrValidate.
 *
 * @returns VINF_SUCCESS if valid, appropriate VERR_TAR_XXX if not.
 * @param   pThis               The TAR reader instance.
 * @param   pObjInfo            The object info structure (output).
 */
static int rtZipTarReaderGetFsObjInfo(PRTZIPTARREADER pThis, PRTFSOBJINFO pObjInfo)
{
    /*
     * Zap the whole structure, this takes care of unused space in the union.
     */
    RT_ZERO(*pObjInfo);

    /*
     * Convert the TAR field in RTFSOBJINFO order.
     */
    int         rc;
    int64_t     i64Tmp;
#define GET_TAR_NUMERIC_FIELD_RET(a_Var, a_Field) \
        do { \
            rc = rtZipTarHdrFieldToNum(a_Field, sizeof(a_Field), false /*fOctalOnly*/, &i64Tmp); \
            if (RT_FAILURE(rc)) \
                return rc; \
            (a_Var) = i64Tmp; \
            if ((a_Var) != i64Tmp) \
                return VERR_TAR_NUM_VALUE_TOO_LARGE; \
        } while (0)

    GET_TAR_NUMERIC_FIELD_RET(pObjInfo->cbObject,        pThis->Hdr.Common.size);
    pObjInfo->cbAllocated = RT_ALIGN_64(pObjInfo->cbObject, 512);
    int64_t c64SecModTime;
    GET_TAR_NUMERIC_FIELD_RET(c64SecModTime,             pThis->Hdr.Common.mtime);
    RTTimeSpecSetSeconds(&pObjInfo->ChangeTime,          c64SecModTime);
    RTTimeSpecSetSeconds(&pObjInfo->ModificationTime,    c64SecModTime);
    RTTimeSpecSetSeconds(&pObjInfo->AccessTime,          c64SecModTime);
    RTTimeSpecSetSeconds(&pObjInfo->BirthTime,           c64SecModTime);
    if (c64SecModTime != RTTimeSpecGetSeconds(&pObjInfo->ModificationTime))
        return VERR_TAR_NUM_VALUE_TOO_LARGE;
    GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.fMode,      pThis->Hdr.Common.mode);
    pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
    GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.uid, pThis->Hdr.Common.uid);
    GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.gid, pThis->Hdr.Common.gid);
    pObjInfo->Attr.u.Unix.cHardlinks    = 1;
    pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
    pObjInfo->Attr.u.Unix.INodeId       = 0;
    pObjInfo->Attr.u.Unix.fFlags        = 0;
    pObjInfo->Attr.u.Unix.GenerationId  = 0;
    pObjInfo->Attr.u.Unix.Device        = 0;
    switch (pThis->enmType)
    {
        case RTZIPTARTYPE_POSIX:
        case RTZIPTARTYPE_GNU:
            if (   pThis->Hdr.Common.typeflag == RTZIPTAR_TF_CHR
                || pThis->Hdr.Common.typeflag == RTZIPTAR_TF_BLK)
            {
                uint32_t uMajor, uMinor;
                GET_TAR_NUMERIC_FIELD_RET(uMajor,        pThis->Hdr.Common.devmajor);
                GET_TAR_NUMERIC_FIELD_RET(uMinor,        pThis->Hdr.Common.devminor);
                pObjInfo->Attr.u.Unix.Device    = RTDEV_MAKE(uMajor, uMinor);
                if (   uMajor != RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device)
                    || uMinor != RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device))
                    return VERR_TAR_DEV_VALUE_TOO_LARGE;
            }
            break;

        default:
            if (   pThis->Hdr.Common.typeflag == RTZIPTAR_TF_CHR
                || pThis->Hdr.Common.typeflag == RTZIPTAR_TF_BLK)
                return VERR_TAR_UNKNOWN_TYPE_FLAG;
    }

#undef GET_TAR_NUMERIC_FIELD_RET

    /*
     * Massage the result a little bit.
     * Also validate some more now that we've got the numbers to work with.
     */
    if (   (pObjInfo->Attr.fMode & ~RTFS_UNIX_MASK)
        && pThis->enmType == RTZIPTARTYPE_POSIX)
        return VERR_TAR_BAD_MODE_FIELD;
    pObjInfo->Attr.fMode &= RTFS_UNIX_MASK;

    RTFMODE fModeType = 0;
    switch (pThis->Hdr.Common.typeflag)
    {
        case RTZIPTAR_TF_OLDNORMAL:
        case RTZIPTAR_TF_NORMAL:
        case RTZIPTAR_TF_CONTIG:
        {
            const char *pszEnd = strchr(pThis->szName, '\0');
            if (pszEnd == &pThis->szName[0] || pszEnd[-1] != '/')
                fModeType |= RTFS_TYPE_FILE;
            else
                fModeType |= RTFS_TYPE_DIRECTORY;
            break;
        }

        case RTZIPTAR_TF_LINK:
            if (pObjInfo->cbObject != 0)
#if 0 /* too strict */
                return VERR_TAR_SIZE_NOT_ZERO;
#else
                pObjInfo->cbObject = pObjInfo->cbAllocated = 0;
#endif
            fModeType |= RTFS_TYPE_FILE; /* no better idea for now */
            break;

        case RTZIPTAR_TF_SYMLINK:
            fModeType |= RTFS_TYPE_SYMLINK;
            break;

        case RTZIPTAR_TF_CHR:
            fModeType |= RTFS_TYPE_DEV_CHAR;
            break;

        case RTZIPTAR_TF_BLK:
            fModeType |= RTFS_TYPE_DEV_BLOCK;
            break;

        case RTZIPTAR_TF_DIR:
            fModeType |= RTFS_TYPE_DIRECTORY;
            break;

        case RTZIPTAR_TF_FIFO:
            fModeType |= RTFS_TYPE_FIFO;
            break;

        case RTZIPTAR_TF_GNU_LONGLINK:
        case RTZIPTAR_TF_GNU_LONGNAME:
            /* ASSUMES RTFS_TYPE_XXX uses the same values as GNU stored in the mode field. */
            fModeType = pObjInfo->Attr.fMode & RTFS_TYPE_MASK;
            switch (fModeType)
            {
                case RTFS_TYPE_FILE:
                case RTFS_TYPE_DIRECTORY:
                case RTFS_TYPE_SYMLINK:
                case RTFS_TYPE_DEV_BLOCK:
                case RTFS_TYPE_DEV_CHAR:
                case RTFS_TYPE_FIFO:
                    break;

                default:
                case 0:
                    return VERR_TAR_UNKNOWN_TYPE_FLAG; /** @todo new status code */
            }

        default:
            return VERR_TAR_UNKNOWN_TYPE_FLAG; /* Should've been caught in validate. */
    }
    if (   (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
        && (pObjInfo->Attr.fMode & RTFS_TYPE_MASK) != fModeType)
        return VERR_TAR_MODE_WITH_TYPE;
    pObjInfo->Attr.fMode &= ~RTFS_TYPE_MASK;
    pObjInfo->Attr.fMode |= fModeType;

    switch (pThis->Hdr.Common.typeflag)
    {
        case RTZIPTAR_TF_CHR:
        case RTZIPTAR_TF_BLK:
        case RTZIPTAR_TF_DIR:
        case RTZIPTAR_TF_FIFO:
            pObjInfo->cbObject    = 0;
            pObjInfo->cbAllocated = 0;
            break;
    }

    return VINF_SUCCESS;
}
Ejemplo n.º 3
0
int main()
{
    RTTIMESPEC      Now;
    RTTIMESPEC      Ts1;
    RTTIMESPEC      Ts2;
    RTTIME          T1;
    RTTIME          T2;
#ifdef RTTIME_INCL_TIMEVAL
    struct timeval  Tv1;
    struct timeval  Tv2;
    struct timespec Tsp1;
    struct timespec Tsp2;
#endif
    RTTEST          hTest;

    int rc = RTTestInitAndCreate("tstRTTimeSpec", &hTest);
    if (rc)
        return rc;

    /*
     * Simple test with current time.
     */
    RTTestSub(hTest, "Current time (UTC)");
    CHECK_NZ(RTTimeNow(&Now));
    CHECK_NZ(RTTimeExplode(&T1, &Now));
    RTTestIPrintf(RTTESTLVL_ALWAYS, "   %RI64 ns - %s\n", RTTimeSpecGetNano(&Now), ToString(&T1));
    CHECK_NZ(RTTimeImplode(&Ts1, &T1));
    if (!RTTimeSpecIsEqual(&Ts1, &Now))
        RTTestIFailed("%RI64 != %RI64\n", RTTimeSpecGetNano(&Ts1), RTTimeSpecGetNano(&Now));

    /*
     * Simple test with current local time.
     */
    RTTestSub(hTest, "Current time (local)");
    CHECK_NZ(RTTimeLocalNow(&Now));
    CHECK_NZ(RTTimeExplode(&T1, &Now));
    RTTestIPrintf(RTTESTLVL_ALWAYS, "   %RI64 ns - %s\n", RTTimeSpecGetNano(&Now), ToString(&T1));
    CHECK_NZ(RTTimeImplode(&Ts1, &T1));
    if (!RTTimeSpecIsEqual(&Ts1, &Now))
        RTTestIFailed("%RI64 != %RI64\n", RTTimeSpecGetNano(&Ts1), RTTimeSpecGetNano(&Now));

    /*
     * Some simple tests with fixed dates (just checking for smoke).
     */
    RTTestSub(hTest, "Smoke");
    TEST_NS(INT64_C(0));
    CHECK_TIME(&T1, 1970,01,01, 00,00,00,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    TEST_NS(INT64_C(86400000000000));
    CHECK_TIME(&T1, 1970,01,02, 00,00,00,        0,   2, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    TEST_NS(INT64_C(1));
    CHECK_TIME(&T1, 1970,01,01, 00,00,00,        1,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    TEST_NS(INT64_C(-1));
    CHECK_TIME(&T1, 1969,12,31, 23,59,59,999999999, 365, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /*
     * Test the limits.
     */
    RTTestSub(hTest, "Extremes");
    TEST_NS(INT64_MAX);
    TEST_NS(INT64_MIN);
    TEST_SEC(1095379198);
    CHECK_TIME(&T1, 2004, 9,16, 23,59,58,        0, 260, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    TEST_SEC(1095379199);
    CHECK_TIME(&T1, 2004, 9,16, 23,59,59,        0, 260, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    TEST_SEC(1095379200);
    CHECK_TIME(&T1, 2004, 9,17, 00,00,00,        0, 261, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    TEST_SEC(1095379201);
    CHECK_TIME(&T1, 2004, 9,17, 00,00,01,        0, 261, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);


    /*
     * Test normalization (UTC).
     */
    RTTestSub(hTest, "Normalization (UTC)");
    /* simple */
    CHECK_NZ(RTTimeNow(&Now));
    CHECK_NZ(RTTimeExplode(&T1, &Now));
    T2 = T1;
    CHECK_NZ(RTTimeNormalize(&T1));
    if (memcmp(&T1, &T2, sizeof(T1)))
        RTTestIFailed("simple normalization failed\n");
    CHECK_NZ(RTTimeImplode(&Ts1, &T1));
    CHECK_NZ(RTTimeSpecIsEqual(&Ts1, &Now));

    /* a few partial dates. */
    memset(&T1, 0, sizeof(T1));
    SET_TIME(  &T1, 1970,01,01, 00,00,00,        0,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1970,01,01, 00,00,00,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1970,00,00, 00,00,00,        1,   1, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1970,01,01, 00,00,00,        1,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2007,12,06, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1968,01,30, 00,19,24,        5,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1968,01,30, 00,19,24,        5,  30, 1, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);

    SET_TIME(  &T1, 1969,01,31, 00, 9, 2,        7,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,01,31, 00, 9, 2,        7,  31, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,03,31, 00, 9, 2,        7,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,03,31, 00, 9, 2,        7,  90, 0, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,31, 00,00,00,        9,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,31, 00,00,00,        9, 365, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,30, 00,00,00,       30,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,30, 00,00,00,       30, 364, 1, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,00,00, 00,00,00,       30, 363, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,29, 00,00,00,       30, 363, 0, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,00,00, 00,00,00,       30, 362, 6, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,28, 00,00,00,       30, 362, 6, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,27, 00,00,00,       30,   0, 5, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,27, 00,00,00,       30, 361, 5, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,00,00, 00,00,00,       30, 360, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,26, 00,00,00,       30, 360, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,25, 00,00,00,       12,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,25, 00,00,00,       12, 359, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,24, 00,00,00,       16,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,24, 00,00,00,       16, 358, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /* outside the year table range */
    SET_TIME(  &T1, 1200,01,30, 00,00,00,        2,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1200,01,30, 00,00,00,        2,  30, 6, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);

    SET_TIME(  &T1, 2555,11,29, 00,00,00,        2,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2555,11,29, 00,00,00,        2, 333, 5, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2555,00,00, 00,00,00,        3, 333, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2555,11,29, 00,00,00,        3, 333, 5, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /* time overflow */
    SET_TIME(  &T1, 1969,12,30, 255,255,255, UINT32_MAX, 364, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1970,01, 9, 19,19,19,294967295,   9, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /* date overflow */
    SET_TIME(  &T1, 2007,11,36, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2007,10,67, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2007,10,98, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2008,01,06, 02,15,23,        1,   6, 6, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);

    SET_TIME(  &T1, 2006,24,06, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2003,60,37, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2008,01,06, 02,15,23,        1,   6, 6, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);

    SET_TIME(  &T1, 2003,00,00, 02,15,23,        1,1801, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /*
     * Conversions.
     */
#define CHECK_NSEC(Ts1, T2) \
    do { \
        RTTIMESPEC TsTmp; \
        RTTESTI_CHECK_MSG( RTTimeSpecGetNano(&(Ts1)) == RTTimeSpecGetNano(RTTimeImplode(&TsTmp, &(T2))), \
                          ("line %d: %RI64, %RI64\n", __LINE__, \
                           RTTimeSpecGetNano(&(Ts1)),   RTTimeSpecGetNano(RTTimeImplode(&TsTmp, &(T2)))) ); \
    } while (0)
    RTTestSub(hTest, "Conversions, positive");
    SET_TIME(&T1, 1980,01,01, 00,00,00,        0,   1, 1, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetDosSeconds(&Ts2,    0) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetDosSeconds(&Ts2) == 0);
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1980,01,01, 00,00,00,        0,   1, 1, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetNtTime(&Ts2,    INT64_C(119600064000000000)) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetNtTime(&Ts2) == INT64_C(119600064000000000));
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1970,01,01, 00,00,01,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetSeconds(&Ts2,    1) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetSeconds(&Ts2) == 1);
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1970,01,01, 00,00,01,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetMilli(&Ts2,    1000) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetMilli(&Ts2) == 1000);
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1970,01,01, 00,00,01,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetMicro(&Ts2,    1000000) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetMicro(&Ts2) == 1000000);
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1970,01,01, 00,00,01,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetNano(&Ts2,    1000000000) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetNano(&Ts2) == 1000000000);
    CHECK_NSEC(Ts2, T1);

#ifdef RTTIME_INCL_TIMEVAL
    SET_TIME(&T1, 1970,01,01, 00,00,01,     5000,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    Tv1.tv_sec  = 1;
    Tv1.tv_usec = 5;
    RTTESTI_CHECK(RTTimeSpecSetTimeval(&Ts2, &Tv1) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetMicro(&Ts2) == 1000005);
    CHECK_NSEC(Ts2, T1);
    RTTESTI_CHECK(RTTimeSpecGetTimeval(&Ts2, &Tv2) == &Tv2);
    RTTESTI_CHECK(Tv1.tv_sec == Tv2.tv_sec); RTTESTI_CHECK(Tv1.tv_usec == Tv2.tv_usec);
#endif

#ifdef RTTIME_INCL_TIMESPEC
    SET_TIME(&T1, 1970,01,01, 00,00,01,        5,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    Tsp1.tv_sec  = 1;
    Tsp1.tv_nsec = 5;
    RTTESTI_CHECK(RTTimeSpecSetTimespec(&Ts2, &Tsp1) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetNano(&Ts2) == 1000000005);
    CHECK_NSEC(Ts2, T1);
    RTTESTI_CHECK(RTTimeSpecGetTimespec(&Ts2, &Tsp2) == &Tsp2);
    RTTESTI_CHECK(Tsp1.tv_sec == Tsp2.tv_sec); RTTESTI_CHECK(Tsp1.tv_nsec == Tsp2.tv_nsec);
#endif


    RTTestSub(hTest, "Conversions, negative");

#ifdef RTTIME_INCL_TIMEVAL
    SET_TIME(&T1, 1969,12,31, 23,59,58,999995000, 365, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    Tv1.tv_sec  = -2;
    Tv1.tv_usec = 999995;
    RTTESTI_CHECK(RTTimeSpecSetTimeval(&Ts2, &Tv1) == &Ts2);
    RTTESTI_CHECK_MSG(RTTimeSpecGetMicro(&Ts2) == -1000005, ("%RI64\n", RTTimeSpecGetMicro(&Ts2)));
    CHECK_NSEC(Ts2, T1);
    RTTESTI_CHECK(RTTimeSpecGetTimeval(&Ts2, &Tv2) == &Tv2);
    RTTESTI_CHECK(Tv1.tv_sec == Tv2.tv_sec); RTTESTI_CHECK(Tv1.tv_usec == Tv2.tv_usec);
#endif

#ifdef RTTIME_INCL_TIMESPEC
    SET_TIME(&T1, 1969,12,31, 23,59,58,999999995, 365, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    Tsp1.tv_sec  = -2;
    Tsp1.tv_nsec = 999999995;
    RTTESTI_CHECK(RTTimeSpecSetTimespec(&Ts2, &Tsp1) == &Ts2);
    RTTESTI_CHECK_MSG(RTTimeSpecGetNano(&Ts2) == -1000000005, ("%RI64\n", RTTimeSpecGetMicro(&Ts2)));
    CHECK_NSEC(Ts2, T1);
    RTTESTI_CHECK(RTTimeSpecGetTimespec(&Ts2, &Tsp2) == &Tsp2);
    RTTESTI_CHECK(Tsp1.tv_sec == Tsp2.tv_sec); RTTESTI_CHECK(Tsp1.tv_nsec == Tsp2.tv_nsec);
#endif

    /*
     * Summary
     */
    return RTTestSummaryAndDestroy(hTest);
}