/*****************************************************************************\ * * * Filename utime.c * * * * Description Updated utime() and port of standard C library's lutime() * * * * Notes TO DO: Create W, A, U versions of ResolveLinks(), then * * create W, A, U versions of utime(). * * * * History * * 2014-02-12 JFL Created this module. * * 2014-06-04 JFL Fixed minors issues in debugging code. * * 2014-07-02 JFL Added support for pathnames >= 260 characters. * * 2016-08-25 JFL Added missing routine utimeA(). * * * * © Copyright 2016 Hewlett Packard Enterprise Development LP * * Licensed under the Apache 2.0 license - www.apache.org/licenses/LICENSE-2.0 * \*****************************************************************************/ #define _UTF8_SOURCE /* Generate the UTF-8 version of Windows print routines */ #define _CRT_SECURE_NO_WARNINGS 1 /* Avoid Visual C++ security warnings */ #include #include /* Must be included before any direct or indirect inclusion */ #include #include "debugm.h" #if defined(_DEBUG) #include #endif /* defined(_DEBUG) */ #ifdef _WIN32 #include #include /* For MSVC's _get_osfhandle() */ #include /* For MsvcLibX's ResolveLinks() */ /* Convert a Windows FILETIME to a Unix time_t. A FILETIME is the number of 100-nanosecond intervals since January 1, 1601. A time_t is the number of 1-second intervals since January 1, 1970. */ time_t Filetime2Time_t(FILETIME *pFT) { ULARGE_INTEGER ull; ull.LowPart = pFT->dwLowDateTime; ull.HighPart = pFT->dwHighDateTime; return (time_t)(ull.QuadPart / 10000000ULL - 11644473600ULL); } void Time_t2Filetime(time_t ft, FILETIME *pFT) { ULARGE_INTEGER ull; ull.QuadPart = ft + 11644473600ULL; ull.QuadPart *= 10000000UL; pFT->dwLowDateTime = ull.LowPart; pFT->dwHighDateTime = ull.HighPart; } DEBUG_CODE( int Utimbuf2String(char *buf, size_t bufsize, const struct utimbuf *times) { struct tm *pTime; int n; if (!times) { return _snprintf(buf, bufsize, "NULL"); } pTime = localtime(&(times->modtime)); /* Time of last data modification */ n = _snprintf(buf, bufsize, "{%4d-%02d-%02d %02d:%02d:%02d, ...}", pTime->tm_year + 1900, pTime->tm_mon + 1, pTime->tm_mday, pTime->tm_hour, pTime->tm_min, pTime->tm_sec); return n; } ); /* Low level subroutine used by all the other routines below. */ int hutime(HANDLE hFile, const struct utimbuf *times) { FILETIME ftLastAccess; /* last access time */ FILETIME ftLastWrite; /* last write time */ int iErr; struct utimbuf now; if (!times) { now.actime = now.modtime = time(NULL); times = &now; } Time_t2Filetime(times->actime, &ftLastAccess); Time_t2Filetime(times->modtime, &ftLastWrite); iErr = !SetFileTime(hFile, NULL, &ftLastAccess, &ftLastWrite); if (iErr) { errno = Win32ErrorToErrno(); return -1; } return 0; } /* Same as 'utime', but does not follow symbolic links. */ int lutimeW(const WCHAR *path, const struct utimbuf *times) { DWORD dwAttr; DWORD dwFlagsAndAttributes; int iErr; HANDLE hLink; DEBUG_CODE({ char buf[100]; char szUtf8[UTF8_PATH_MAX]; Utimbuf2String(buf, sizeof(buf), times); DEBUG_WSTR2UTF8(path, szUtf8, sizeof(szUtf8)); DEBUG_ENTER(("lutime(\"%s\", %s);\n", szUtf8, buf)); }); dwAttr = GetFileAttributesW(path); if (dwAttr == INVALID_FILE_ATTRIBUTES) { errno = ENOENT; RETURN_INT_COMMENT(-1, ("File does not exist\n")); } dwFlagsAndAttributes = FILE_FLAG_OPEN_REPARSE_POINT; if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) dwFlagsAndAttributes |= FILE_FLAG_BACKUP_SEMANTICS; hLink = CreateFileW(path, /* lpFileName, */ FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess, */ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode, */ NULL, /* lpSecurityAttributes, */ OPEN_EXISTING, /* dwCreationDisposition, */ dwFlagsAndAttributes, /* dwFlagsAndAttributes, */ NULL /* hTemplateFile */ ); XDEBUG_PRINTF(("CreateFile() = 0x%p\n", hLink)); if (hLink == INVALID_HANDLE_VALUE) { errno = Win32ErrorToErrno(); RETURN_INT_COMMENT(-1, ("Cannot open the pathname\n")); } iErr = hutime(hLink, times); CloseHandle(hLink); RETURN_INT_COMMENT(iErr, ("errno = %d\n", iErr ? errno : 0)); } int lutimeA(const char *path, const struct utimbuf *times) { WCHAR wszPath[PATH_MAX]; int n; /* Convert the pathname to a unicode string, with the proper extension prefixes if it's longer than 260 bytes */ n = MultiByteToWidePath(CP_ACP, /* CodePage, (CP_ACP, CP_OEMCP, CP_UTF8, ...) */ path, /* lpMultiByteStr, */ wszPath, /* lpWideCharStr, */ COUNTOF(wszPath) /* cchWideChar, */ ); if (!n) { errno = Win32ErrorToErrno(); DEBUG_ENTER(("lutimeA(\"%s\", %p);\n", path, times)); RETURN_INT_COMMENT(-1, ("errno=%d - %s\n", errno, strerror(errno))); } return lutimeW(wszPath, times); } int lutimeU(const char *path, const struct utimbuf *times) { WCHAR wszPath[PATH_MAX]; int n; /* Convert the pathname to a unicode string, with the proper extension prefixes if it's longer than 260 bytes */ n = MultiByteToWidePath(CP_UTF8, /* CodePage, (CP_ACP, CP_OEMCP, CP_UTF8, ...) */ path, /* lpMultiByteStr, */ wszPath, /* lpWideCharStr, */ COUNTOF(wszPath) /* cchWideChar, */ ); if (!n) { errno = Win32ErrorToErrno(); DEBUG_ENTER(("lutimeU(\"%s\", %p);\n", path, times)); RETURN_INT_COMMENT(-1, ("errno=%d - %s\n", errno, strerror(errno))); } return lutimeW(wszPath, times); } /* Same as 'utime', but takes an open file descriptor instead of a name. */ int futime(int fd, const struct utimbuf *times) { return hutime((HANDLE)_get_osfhandle(fd), times); } /* Change the file access time to times->actime and its modification time to times->modtime. */ int utimeA(const char *file, const struct utimbuf *times) { char buf[PATH_MAX]; int iErr; DEBUG_CODE({ char buf[100]; Utimbuf2String(buf, sizeof(buf), times); DEBUG_ENTER(("utime(\"%s\", %s);\n", file, buf)); }); iErr = ResolveLinksA(file, buf, sizeof(buf)); if (iErr) RETURN_INT_COMMENT(iErr, ("Cannot resolve the link\n")); iErr = lutimeA(buf, times); RETURN_INT_COMMENT(iErr, ("errno = %d\n", iErr ? errno : 0)); } /* Change the file access time to times->actime and its modification time to times->modtime. */ int utimeU(const char *file, const struct utimbuf *times) { char buf[UTF8_PATH_MAX]; int iErr; DEBUG_CODE({ char buf[100]; Utimbuf2String(buf, sizeof(buf), times); DEBUG_ENTER(("utime(\"%s\", %s);\n", file, buf)); }); iErr = ResolveLinksU(file, buf, sizeof(buf)); if (iErr) RETURN_INT_COMMENT(iErr, ("Cannot resolve the link\n")); iErr = lutimeU(buf, times); RETURN_INT_COMMENT(iErr, ("errno = %d\n", iErr ? errno : 0)); } #endif /* defined(_WIN32) */