utime.c 7.5 KB
Newer Older
S
Shengliang Guan 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
/*****************************************************************************\
*                                                                             *
*   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 <errno.h>
#include <sys/time.h> /* Must be included before any direct or indirect <windows.h> inclusion */
#include <utime.h>

#include "debugm.h"

#if defined(_DEBUG)
#include <stdio.h>
#endif /* defined(_DEBUG) */

#ifdef _WIN32

#include <windows.h>
#include <io.h> /* For  MSVC's _get_osfhandle() */
#include <unistd.h> /* 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) */