]> asedeno.scripts.mit.edu Git - git.git/blobdiff - compat/mingw.c
Merge branch 'tr/maint-1.6.0-send-email-irt'
[git.git] / compat / mingw.c
index 45733f9e04eb87409a896898cb5a92b37e87a920..171fa85e4ae9fa026bbb72e2e8b8b098d2ba22ed 100644 (file)
@@ -1,8 +1,122 @@
 #include "../git-compat-util.h"
+#include "win32.h"
 #include "../strbuf.h"
 
 unsigned int _CRT_fmode = _O_BINARY;
 
+static int err_win_to_posix(DWORD winerr)
+{
+       int error = ENOSYS;
+       switch(winerr) {
+       case ERROR_ACCESS_DENIED: error = EACCES; break;
+       case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
+       case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
+       case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
+       case ERROR_ALREADY_EXISTS: error = EEXIST; break;
+       case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
+       case ERROR_BAD_COMMAND: error = EIO; break;
+       case ERROR_BAD_DEVICE: error = ENODEV; break;
+       case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
+       case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
+       case ERROR_BAD_FORMAT: error = ENOEXEC; break;
+       case ERROR_BAD_LENGTH: error = EINVAL; break;
+       case ERROR_BAD_PATHNAME: error = ENOENT; break;
+       case ERROR_BAD_PIPE: error = EPIPE; break;
+       case ERROR_BAD_UNIT: error = ENODEV; break;
+       case ERROR_BAD_USERNAME: error = EINVAL; break;
+       case ERROR_BROKEN_PIPE: error = EPIPE; break;
+       case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
+       case ERROR_BUSY: error = EBUSY; break;
+       case ERROR_BUSY_DRIVE: error = EBUSY; break;
+       case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
+       case ERROR_CANNOT_MAKE: error = EACCES; break;
+       case ERROR_CANTOPEN: error = EIO; break;
+       case ERROR_CANTREAD: error = EIO; break;
+       case ERROR_CANTWRITE: error = EIO; break;
+       case ERROR_CRC: error = EIO; break;
+       case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
+       case ERROR_DEVICE_IN_USE: error = EBUSY; break;
+       case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
+       case ERROR_DIRECTORY: error = EINVAL; break;
+       case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
+       case ERROR_DISK_CHANGE: error = EIO; break;
+       case ERROR_DISK_FULL: error = ENOSPC; break;
+       case ERROR_DRIVE_LOCKED: error = EBUSY; break;
+       case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
+       case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
+       case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
+       case ERROR_FILE_EXISTS: error = EEXIST; break;
+       case ERROR_FILE_INVALID: error = ENODEV; break;
+       case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
+       case ERROR_GEN_FAILURE: error = EIO; break;
+       case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
+       case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
+       case ERROR_INVALID_ACCESS: error = EACCES; break;
+       case ERROR_INVALID_ADDRESS: error = EFAULT; break;
+       case ERROR_INVALID_BLOCK: error = EFAULT; break;
+       case ERROR_INVALID_DATA: error = EINVAL; break;
+       case ERROR_INVALID_DRIVE: error = ENODEV; break;
+       case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
+       case ERROR_INVALID_FLAGS: error = EINVAL; break;
+       case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
+       case ERROR_INVALID_HANDLE: error = EBADF; break;
+       case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
+       case ERROR_INVALID_NAME: error = EINVAL; break;
+       case ERROR_INVALID_OWNER: error = EINVAL; break;
+       case ERROR_INVALID_PARAMETER: error = EINVAL; break;
+       case ERROR_INVALID_PASSWORD: error = EPERM; break;
+       case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
+       case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
+       case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
+       case ERROR_INVALID_WORKSTATION: error = EACCES; break;
+       case ERROR_IO_DEVICE: error = EIO; break;
+       case ERROR_IO_INCOMPLETE: error = EINTR; break;
+       case ERROR_LOCKED: error = EBUSY; break;
+       case ERROR_LOCK_VIOLATION: error = EACCES; break;
+       case ERROR_LOGON_FAILURE: error = EACCES; break;
+       case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
+       case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
+       case ERROR_MORE_DATA: error = EPIPE; break;
+       case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
+       case ERROR_NOACCESS: error = EFAULT; break;
+       case ERROR_NONE_MAPPED: error = EINVAL; break;
+       case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
+       case ERROR_NOT_READY: error = EAGAIN; break;
+       case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
+       case ERROR_NO_DATA: error = EPIPE; break;
+       case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
+       case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
+       case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
+       case ERROR_OPEN_FAILED: error = EIO; break;
+       case ERROR_OPEN_FILES: error = EBUSY; break;
+       case ERROR_OPERATION_ABORTED: error = EINTR; break;
+       case ERROR_OUTOFMEMORY: error = ENOMEM; break;
+       case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
+       case ERROR_PATH_BUSY: error = EBUSY; break;
+       case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
+       case ERROR_PIPE_BUSY: error = EBUSY; break;
+       case ERROR_PIPE_CONNECTED: error = EPIPE; break;
+       case ERROR_PIPE_LISTENING: error = EPIPE; break;
+       case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
+       case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
+       case ERROR_READ_FAULT: error = EIO; break;
+       case ERROR_SEEK: error = EIO; break;
+       case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
+       case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
+       case ERROR_SHARING_VIOLATION: error = EACCES; break;
+       case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
+       case ERROR_SWAPERROR: error = ENOENT; break;
+       case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
+       case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
+       case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
+       case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
+       case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
+       case ERROR_WRITE_FAULT: error = EIO; break;
+       case ERROR_WRITE_PROTECT: error = EROFS; break;
+       }
+       return error;
+}
+
 #undef open
 int mingw_open (const char *filename, int oflags, ...)
 {
@@ -31,12 +145,6 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
        return (time_t)winTime;
 }
 
-static inline size_t size_to_blocks(size_t s)
-{
-       return (s+511)/512;
-}
-
-extern int _getdrive( void );
 /* We keep the do_lstat code in a separate function to avoid recursion.
  * When a path ends with a slash, the stat will fail with ENOENT. In
  * this case, we strip the trailing slashes and stat again.
@@ -45,46 +153,20 @@ static int do_lstat(const char *file_name, struct stat *buf)
 {
        WIN32_FILE_ATTRIBUTE_DATA fdata;
 
-       if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) {
-               int fMode = S_IREAD;
-               if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-                       fMode |= S_IFDIR;
-               else
-                       fMode |= S_IFREG;
-               if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
-                       fMode |= S_IWRITE;
-
+       if (!(errno = get_file_attr(file_name, &fdata))) {
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
-               buf->st_mode = fMode;
-               buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
-               buf->st_blocks = size_to_blocks(buf->st_size);
-               buf->st_dev = _getdrive() - 1;
+               buf->st_nlink = 1;
+               buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
+               buf->st_size = fdata.nFileSizeLow |
+                       (((off_t)fdata.nFileSizeHigh)<<32);
+               buf->st_dev = buf->st_rdev = 0; /* not used by Git */
                buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
                buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
-               errno = 0;
                return 0;
        }
-
-       switch (GetLastError()) {
-       case ERROR_ACCESS_DENIED:
-       case ERROR_SHARING_VIOLATION:
-       case ERROR_LOCK_VIOLATION:
-       case ERROR_SHARING_BUFFER_EXCEEDED:
-               errno = EACCES;
-               break;
-       case ERROR_BUFFER_OVERFLOW:
-               errno = ENAMETOOLONG;
-               break;
-       case ERROR_NOT_ENOUGH_MEMORY:
-               errno = ENOMEM;
-               break;
-       default:
-               errno = ENOENT;
-               break;
-       }
        return -1;
 }
 
@@ -94,7 +176,7 @@ static int do_lstat(const char *file_name, struct stat *buf)
  * complete. Note that Git stat()s are redirected to mingw_lstat()
  * too, since Windows doesn't really handle symlinks that well.
  */
-int mingw_lstat(const char *file_name, struct mingw_stat *buf)
+int mingw_lstat(const char *file_name, struct stat *buf)
 {
        int namelen;
        static char alt_name[PATH_MAX];
@@ -122,8 +204,7 @@ int mingw_lstat(const char *file_name, struct mingw_stat *buf)
 }
 
 #undef fstat
-#undef stat
-int mingw_fstat(int fd, struct mingw_stat *buf)
+int mingw_fstat(int fd, struct stat *buf)
 {
        HANDLE fh = (HANDLE)_get_osfhandle(fd);
        BY_HANDLE_FILE_INFORMATION fdata;
@@ -133,39 +214,18 @@ int mingw_fstat(int fd, struct mingw_stat *buf)
                return -1;
        }
        /* direct non-file handles to MS's fstat() */
-       if (GetFileType(fh) != FILE_TYPE_DISK) {
-               struct stat st;
-               if (fstat(fd, &st))
-                       return -1;
-               buf->st_ino = st.st_ino;
-               buf->st_gid = st.st_gid;
-               buf->st_uid = st.st_uid;
-               buf->st_mode = st.st_mode;
-               buf->st_size = st.st_size;
-               buf->st_blocks = size_to_blocks(buf->st_size);
-               buf->st_dev = st.st_dev;
-               buf->st_atime = st.st_atime;
-               buf->st_mtime = st.st_mtime;
-               buf->st_ctime = st.st_ctime;
-               return 0;
-       }
+       if (GetFileType(fh) != FILE_TYPE_DISK)
+               return _fstati64(fd, buf);
 
        if (GetFileInformationByHandle(fh, &fdata)) {
-               int fMode = S_IREAD;
-               if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-                       fMode |= S_IFDIR;
-               else
-                       fMode |= S_IFREG;
-               if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
-                       fMode |= S_IWRITE;
-
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
-               buf->st_mode = fMode;
-               buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
-               buf->st_blocks = size_to_blocks(buf->st_size);
-               buf->st_dev = _getdrive() - 1;
+               buf->st_nlink = 1;
+               buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
+               buf->st_size = fdata.nFileSizeLow |
+                       (((off_t)fdata.nFileSizeHigh)<<32);
+               buf->st_dev = buf->st_rdev = 0; /* not used by Git */
                buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
                buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
@@ -283,8 +343,13 @@ int poll(struct pollfd *ufds, unsigned int nfds, int timeout)
 {
        int i, pending;
 
-       if (timeout != -1)
+       if (timeout >= 0) {
+               if (nfds == 0) {
+                       Sleep(timeout);
+                       return 0;
+               }
                return errno = EINVAL, error("poll timeout not supported");
+       }
 
        /* When there is only one fd to wait for, then we pretend that
         * input is available and let the actual wait happen when the
@@ -586,12 +651,16 @@ static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
                 * would normally create a console window. But
                 * since we'll be redirecting std streams, we do
                 * not need the console.
+                * It is necessary to use DETACHED_PROCESS
+                * instead of CREATE_NO_WINDOW to make ssh
+                * recognize that it has no console.
                 */
-               flags = CREATE_NO_WINDOW;
+               flags = DETACHED_PROCESS;
        } else {
                /* There is already a console. If we specified
-                * CREATE_NO_WINDOW here, too, Windows would
+                * DETACHED_PROCESS here, too, Windows would
                 * disassociate the child from the console.
+                * The same is true for CREATE_NO_WINDOW.
                 * Go figure!
                 */
                flags = 0;
@@ -1049,3 +1118,24 @@ void mingw_open_html(const char *unixpath)
        printf("Launching default browser to display HTML ...\n");
        ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
 }
+
+int link(const char *oldpath, const char *newpath)
+{
+       typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
+       static T create_hard_link = NULL;
+       if (!create_hard_link) {
+               create_hard_link = (T) GetProcAddress(
+                       GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
+               if (!create_hard_link)
+                       create_hard_link = (T)-1;
+       }
+       if (create_hard_link == (T)-1) {
+               errno = ENOSYS;
+               return -1;
+       }
+       if (!create_hard_link(newpath, oldpath, NULL)) {
+               errno = err_win_to_posix(GetLastError());
+               return -1;
+       }
+       return 0;
+}