Skip to content

Commit 6ea9f68

Browse files
dschomjcheetham
authored andcommitted
Merge branch 'kblees/kb/symlinks'
Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 6b6402a + 890e2cc commit 6ea9f68

File tree

8 files changed

+528
-165
lines changed

8 files changed

+528
-165
lines changed

compat/mingw.c

Lines changed: 482 additions & 146 deletions
Large diffs are not rendered by default.

compat/mingw.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,6 @@ struct utsname {
125125
* trivial stubs
126126
*/
127127

128-
static inline int readlink(const char *path, char *buf, size_t bufsiz)
129-
{ errno = ENOSYS; return -1; }
130-
static inline int symlink(const char *oldpath, const char *newpath)
131-
{ errno = ENOSYS; return -1; }
132128
static inline int fchmod(int fildes, mode_t mode)
133129
{ errno = ENOSYS; return -1; }
134130
#ifndef __MINGW64_VERSION_MAJOR
@@ -222,6 +218,8 @@ int setitimer(int type, struct itimerval *in, struct itimerval *out);
222218
int sigaction(int sig, struct sigaction *in, struct sigaction *out);
223219
int link(const char *oldpath, const char *newpath);
224220
int uname(struct utsname *buf);
221+
int symlink(const char *target, const char *link);
222+
int readlink(const char *path, char *buf, size_t bufsiz);
225223

226224
/*
227225
* replacements of existing functions

compat/win32.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
#include <windows.h>
77
#endif
88

9-
static inline int file_attr_to_st_mode (DWORD attr)
9+
static inline int file_attr_to_st_mode (DWORD attr, DWORD tag)
1010
{
1111
int fMode = S_IREAD;
12-
if (attr & FILE_ATTRIBUTE_DIRECTORY)
12+
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && tag == IO_REPARSE_TAG_SYMLINK)
13+
fMode |= S_IFLNK;
14+
else if (attr & FILE_ATTRIBUTE_DIRECTORY)
1315
fMode |= S_IFDIR;
1416
else
1517
fMode |= S_IFREG;

compat/win32/dirent.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
1818
xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
1919

2020
/* Set file type, based on WIN32_FIND_DATA */
21-
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
21+
if ((fdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
22+
&& fdata->dwReserved0 == IO_REPARSE_TAG_SYMLINK)
23+
ent->d_type = DT_LNK;
24+
else if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2225
ent->d_type = DT_DIR;
2326
else
2427
ent->d_type = DT_REG;

compat/win32/fscache.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,13 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache,
202202
fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
203203
fdata->EaSize : 0;
204204

205-
fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
206-
fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
207-
fse->u.s.st_size = fdata->EndOfFile.LowPart |
208-
(((off_t)fdata->EndOfFile.HighPart) << 32);
205+
fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
206+
fdata->EaSize);
207+
fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
208+
S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
209+
fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
210+
fdata->EndOfFile.LowPart |
211+
(((off_t)fdata->EndOfFile.HighPart) << 32);
209212
filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
210213
&(fse->u.s.st_atim));
211214
filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime),
@@ -581,6 +584,18 @@ int fscache_lstat(const char *filename, struct stat *st)
581584
return -1;
582585
}
583586

587+
/*
588+
* Special case symbolic links: FindFirstFile()/FindNextFile() did not
589+
* provide us with the length of the target path.
590+
*/
591+
if (fse->u.s.st_size == MAX_LONG_PATH && S_ISLNK(fse->st_mode)) {
592+
char buf[MAX_LONG_PATH];
593+
int len = readlink(filename, buf, sizeof(buf) - 1);
594+
595+
if (len > 0)
596+
fse->u.s.st_size = len;
597+
}
598+
584599
/* copy stat data */
585600
st->st_ino = 0;
586601
st->st_gid = 0;

lockfile.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ static void trim_last_path_component(struct strbuf *path)
1919
int i = path->len;
2020

2121
/* back up past trailing slashes, if any */
22-
while (i && path->buf[i - 1] == '/')
22+
while (i && is_dir_sep(path->buf[i - 1]))
2323
i--;
2424

2525
/*
2626
* then go backwards until a slash, or the beginning of the
2727
* string
2828
*/
29-
while (i && path->buf[i - 1] != '/')
29+
while (i && !is_dir_sep(path->buf[i - 1]))
3030
i--;
3131

3232
strbuf_setlen(path, i);

read-cache.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,17 @@ int ie_modified(struct index_state *istate,
469469
* then we know it is.
470470
*/
471471
if ((changed & DATA_CHANGED) &&
472+
#ifdef GIT_WINDOWS_NATIVE
473+
/*
474+
* Work around Git for Windows v2.27.0 fixing a bug where symlinks'
475+
* target path lengths were not read at all, and instead recorded
476+
* as 4096: now, all symlinks would appear as modified.
477+
*
478+
* So let's just special-case symlinks with a target path length
479+
* (i.e. `sd_size`) of 4096 and force them to be re-checked.
480+
*/
481+
(!S_ISLNK(st->st_mode) || ce->ce_stat_data.sd_size != MAX_LONG_PATH) &&
482+
#endif
472483
(S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
473484
return changed;
474485

strbuf.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -562,24 +562,22 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)
562562
return sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;
563563
}
564564

565-
#define STRBUF_MAXLINK (2*PATH_MAX)
566-
567565
int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
568566
{
569567
size_t oldalloc = sb->alloc;
570568

571569
if (hint < 32)
572570
hint = 32;
573571

574-
while (hint < STRBUF_MAXLINK) {
572+
for (;;) {
575573
ssize_t len;
576574

577-
strbuf_grow(sb, hint);
578-
len = readlink(path, sb->buf, hint);
575+
strbuf_grow(sb, hint + 1);
576+
len = readlink(path, sb->buf, hint + 1);
579577
if (len < 0) {
580578
if (errno != ERANGE)
581579
break;
582-
} else if (len < hint) {
580+
} else if (len <= hint) {
583581
strbuf_setlen(sb, len);
584582
return 0;
585583
}

0 commit comments

Comments
 (0)