diff --git a/src/blob.c b/src/blob.c index 12f468ef7..c95d018e2 100644 --- a/src/blob.c +++ b/src/blob.c @@ -119,9 +119,9 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat ssize_t read_len; if (!islnk) - read_len = read(fd, buffer, sizeof(buffer)); + read_len = gitfo_read(fd, buffer, sizeof(buffer)); else - read_len = readlink(full_path, buffer, sizeof(buffer)); + read_len = gitfo_readlink(full_path, buffer, sizeof(buffer)); if (read_len < 0) { if (!islnk) diff --git a/src/fileops.c b/src/fileops.c index 2e5717e9e..98fc53a50 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -175,14 +175,6 @@ int gitfo_exists(const char *path) return access(path, F_OK); } -int gitfo_shallow_exists(const char *path) -{ - assert(path); - - struct stat st; - return gitfo_lstat(path, &st); -} - git_off_t gitfo_size(git_file fd) { struct stat sb; @@ -609,3 +601,134 @@ int gitfo_getcwd(char *buffer_out, size_t size) return GIT_SUCCESS; } + +#ifdef GIT_WIN32 +static inline time_t filetime_to_time_t(const FILETIME *ft) +{ + long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; + winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ + winTime /= 10000000; /* Nano to seconds resolution */ + return (time_t)winTime; +} + +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 (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + fMode |= _S_IFLNK; + + buf->st_ino = 0; + buf->st_gid = 0; + buf->st_uid = 0; + buf->st_nlink = 1; + buf->st_mode = fMode; + buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ + buf->st_dev = buf->st_rdev = (_getdrive() - 1); + 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)); + return GIT_SUCCESS; + } + + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + case ERROR_SHARING_BUFFER_EXCEEDED: + return GIT_EOSERR; + + case ERROR_BUFFER_OVERFLOW: + case ERROR_NOT_ENOUGH_MEMORY: + return GIT_ENOMEM; + + default: + return GIT_EINVALIDPATH; + } +} + +int gitfo_lstat__w32(const char *file_name, struct stat *buf) +{ + int namelen, error; + char alt_name[GIT_PATH_MAX]; + + if ((error = do_lstat(file_name, buf)) == GIT_SUCCESS) + return GIT_SUCCESS; + + /* if file_name ended in a '/', Windows returned ENOENT; + * try again without trailing slashes + */ + if (error != GIT_EINVALIDPATH) + return git__throw(GIT_EOSERR, "Failed to lstat file"); + + namelen = strlen(file_name); + if (namelen && file_name[namelen-1] != '/') + return git__throw(GIT_EOSERR, "Failed to lstat file"); + + while (namelen && file_name[namelen-1] == '/') + --namelen; + + if (!namelen || namelen >= GIT_PATH_MAX) + return git__throw(GIT_ENOMEM, "Failed to lstat file"); + + memcpy(alt_name, file_name, namelen); + alt_name[namelen] = 0; + return do_lstat(alt_name, buf); +} +int gitfo_readlink__w32(const char *link, char *target, size_t target_len) +{ + HANDLE hFile; + DWORD dwRet; + + hFile = CreateFile(link, // file to open + GENERIC_READ, // open for reading + FILE_SHARE_READ, // share for reading + NULL, // default security + OPEN_EXISTING, // existing file only + FILE_FLAG_BACKUP_SEMANTICS, // normal file + NULL); // no attr. template + + if (hFile == INVALID_HANDLE_VALUE) + return GIT_EOSERR; + + dwRet = GetFinalPathNameByHandleA(hFile, target, target_len, VOLUME_NAME_DOS); + if (dwRet >= target_len) + return GIT_ENOMEM; + + CloseHandle(hFile); + + if (dwRet > 4) { + /* Skip first 4 characters if they are "\\?\" */ + if (target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') { + char tmp[MAXPATHLEN]; + unsigned int offset = 4; + dwRet -= 4; + + /* \??\UNC\ */ + if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') { + offset += 2; + dwRet -= 2; + target[offset] = '\\'; + } + + memcpy(tmp, target + offset, dwRet); + memcpy(target, tmp, dwRet); + } + } + + target[dwRet] = '\0'; + return dwRet; +} + +#endif diff --git a/src/fileops.h b/src/fileops.h index e20ce6f28..4e6007ad4 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -65,7 +65,6 @@ typedef struct { /* file io buffer */ } gitfo_buf; extern int gitfo_exists(const char *path); -extern int gitfo_shallow_exists(const char *path); extern int gitfo_open(const char *path, int flags); extern int gitfo_creat(const char *path, int mode); extern int gitfo_creat_force(const char *path, int mode); @@ -95,7 +94,14 @@ extern int gitfo_mv_force(const char *from, const char *to); #define gitfo_stat(p,b) stat(p, b) #define gitfo_fstat(f,b) fstat(f, b) -#define gitfo_lstat(p,b) lstat(p,b) + +#ifdef GIT_WIN32 +# define gitfo_lstat(p,b) gitfo_lstat__w32(p,b) +# define gitfo_readlink(a, b, c) gitfo_readlink__w32(a, b, c) +#else +# define gitfo_lstat(p,b) lstat(p,b) +# define gitfo_readlink(a, b, c) readlink(a, b, c) +#endif #define gitfo_unlink(p) unlink(p) #define gitfo_rmdir(p) rmdir(p) diff --git a/src/index.c b/src/index.c index 798d9e918..60b65848d 100644 --- a/src/index.c +++ b/src/index.c @@ -411,11 +411,8 @@ static int index_init_entry(git_index_entry *entry, git_index *index, const char git__joinpath(full_path, index->repository->path_workdir, rel_path); - if (gitfo_shallow_exists(full_path) < 0) - return git__throw(GIT_ENOTFOUND, "Failed to initialize entry. %s does not exist", full_path); - if (gitfo_lstat(full_path, &st) < 0) - return git__throw(GIT_EOSERR, "Failed to initialize entry. %s appears to be corrupted", full_path); + return git__throw(GIT_EOSERR, "Failed to initialize entry. '%s' cannot be opened", full_path); if (stage < 0 || stage > 3) return git__throw(GIT_ERROR, "Failed to initialize entry. Invalid stage %i", stage); diff --git a/src/msvc-compat.h b/src/msvc-compat.h index 1ec85f91b..6f38e482d 100644 --- a/src/msvc-compat.h +++ b/src/msvc-compat.h @@ -12,10 +12,13 @@ # define stat _stat64 # define fstat _fstat64 +#define _S_IFLNK 0120000 + /* stat: file mode type testing macros */ # define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) # define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) # define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO) +# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) /* case-insensitive string comparison */ # define strcasecmp _stricmp