/* Symlink to above file. */
if (canSymlink()) {
- assertMakeSymlink("symlink", "file");
+ assertMakeSymlink("symlink", "file", 0);
fprintf(filelist, "symlink\n");
if (is_LargeInode("symlink")) {
strncat(result,
/* "symlink" */
if (canSymlink()) {
- assertMakeSymlink("symlink", "file1");
+ assertMakeSymlink("symlink", "file1", 0);
fprintf(list, "symlink\n");
}
fprintf(filelist, "file\n");
/* Symlink to above file. */
- assertMakeSymlink("symlink", "file");
+ assertMakeSymlink("symlink", "file", 0);
fprintf(filelist, "symlink\n");
fclose(filelist);
/* "symlink" */
if (canSymlink()) {
- assertMakeSymlink("symlink", "file");
+ assertMakeSymlink("symlink", "file", 0);
fprintf(filelist, "symlink\n");
}
en = create_filesystem_object(a);
} else if (en == EEXIST) {
mode_t st_mode;
+ mode_t lst_mode;
+ BY_HANDLE_FILE_INFORMATION lst;
/*
* We know something is in the way, but we don't know what;
* we need to find out before we go any further.
*/
int r = 0;
+ int dirlnk = 0;
+
/*
* The SECURE_SYMLINK logic has already removed a
* symlink to a dir if the client wants that. So
* follow the symlink if we're creating a dir.
- */
- if (S_ISDIR(a->mode))
- r = file_information(a, a->name, &a->st, &st_mode, 0);
- /*
* If it's not a dir (or it's a broken symlink),
* then don't follow it.
+ *
+ * Windows distinguishes file and directory symlinks.
+ * A file symlink may erroneously point to a directory
+ * and a directory symlink to a file. Windows does not follow
+ * such symlinks. We always need both source and target
+ * information.
*/
- if (r != 0 || !S_ISDIR(a->mode))
- r = file_information(a, a->name, &a->st, &st_mode, 1);
+ r = file_information(a, a->name, &lst, &lst_mode, 1);
if (r != 0) {
archive_set_error(&a->archive, errno,
"Can't stat existing object");
return (ARCHIVE_FAILED);
+ } else if (S_ISLNK(lst_mode)) {
+ if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ dirlnk = 1;
+ /* In case of a symlink we need target information */
+ r = file_information(a, a->name, &a->st, &st_mode, 0);
+ if (r != 0) {
+ a->st = lst;
+ st_mode = lst_mode;
+ }
+ } else {
+ a->st = lst;
+ st_mode = lst_mode;
}
/*
}
if (!S_ISDIR(st_mode)) {
- /* A non-dir is in the way, unlink it. */
- if (disk_unlink(a->name) != 0) {
+ /* Edge case: a directory symlink pointing to a file */
+ if (dirlnk) {
+ if (disk_rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't unlink directory symlink");
+ return (ARCHIVE_FAILED);
+ }
+ } else if (disk_unlink(a->name) != 0) {
+ /* A non-dir is in the way, unlink it. */
archive_set_error(&a->archive, errno,
"Can't unlink already-existing object");
return (ARCHIVE_FAILED);
p = a->path_safe.s;
while ((*pn != '\0') && (*p == *pn))
++p, ++pn;
+ /* Skip leading backslashes */
+ while (*pn == '\\')
+ ++pn;
c = pn[0];
/* Keep going until we've checked the entire name. */
while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) {
} else if (S_ISLNK(st_mode)) {
if (c == '\0') {
/*
- * Last element is symlink; remove it
- * so we can overwrite it with the
+ * Last element is a file or directory symlink.
+ * Remove it so we can overwrite it with the
* item being extracted.
*/
if (st.dwFileAttributes &
return (0);
} else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
/* User asked us to remove problems. */
- if (disk_unlink(a->name) != 0) {
+ if (st.dwFileAttributes &
+ FILE_ATTRIBUTE_DIRECTORY) {
+ r = disk_rmdir(a->name);
+ } else {
+ r = disk_unlink(a->name);
+ }
+ if (r != 0) {
archive_set_error(&a->archive, 0,
"Cannot remove intervening "
"symlink %ls", a->name);
return (ARCHIVE_FAILED);
}
}
+ pn[0] = c;
+ pn++;
}
pn[0] = c;
/* We've checked and/or cleaned the whole path, so remember it. */
assertMakeDir("h", 0755);
assertChdir("h");
assertMakeDir("d1", 0755);
- assertMakeSymlink("ld1", "d1/");
+ assertMakeSymlink("ld1", "d1", 1);
assertMakeFile("d1/file1", 0644, "d1/file1");
assertMakeFile("d1/file2", 0644, "d1/file2");
- assertMakeSymlink("d1/link1", "file1");
- assertMakeSymlink("d1/linkX", "fileX");
- assertMakeSymlink("link2", "d1/file2");
- assertMakeSymlink("linkY", "d1/fileY");
+ assertMakeSymlink("d1/link1", "file1", 0);
+ assertMakeSymlink("d1/linkX", "fileX", 0);
+ assertMakeSymlink("link2", "d1/file2", 0);
+ assertMakeSymlink("linkY", "d1/fileY", 0);
assertChdir("..");
assert((ae = archive_entry_new()) != NULL);
assertMakeDir("l", 0755);
assertChdir("l");
assertMakeDir("d1", 0755);
- assertMakeSymlink("ld1", "d1/");
+ assertMakeSymlink("ld1", "d1", 1);
assertMakeFile("d1/file1", 0644, "d1/file1");
assertMakeFile("d1/file2", 0644, "d1/file2");
- assertMakeSymlink("d1/link1", "file1");
- assertMakeSymlink("d1/linkX", "fileX");
- assertMakeSymlink("link2", "d1/file2");
- assertMakeSymlink("linkY", "d1/fileY");
+ assertMakeSymlink("d1/link1", "file1", 0);
+ assertMakeSymlink("d1/linkX", "fileX", 0);
+ assertMakeSymlink("link2", "d1/file2", 0);
+ assertMakeSymlink("linkY", "d1/fileY", 0);
assertChdir("..");
/* Note: this test uses archive_read_next_header()
assertMakeDir("d1/d2/d3", 0755);
assertMakeDir("d2", 0755);
assertMakeFile("d2/file1", 0644, "d2/file1");
- assertMakeSymlink("d1/d2/ld1", "../../d1/");
- assertMakeSymlink("d1/d2/ld2", "../../d2/");
+ assertMakeSymlink("d1/d2/ld1", "../../d1", 1);
+ assertMakeSymlink("d1/d2/ld2", "../../d2", 1);
assertChdir("..");
assert((ae = archive_entry_new()) != NULL);
/* Symlink to above file. */
if (canSymlink())
- assertMakeSymlink("symlink", "file");
+ assertMakeSymlink("symlink", "file", 0);
/* Directory. */
assertMakeDir("dir", 0775);
sprintf(buff, "s/%s", filenames[i]);
sprintf(buff2, "../f/%s", filenames[i]);
failure("buff=\"%s\" buff2=\"%s\"", buff, buff2);
- assertMakeSymlink(buff, buff2);
+ assertMakeSymlink(buff, buff2, 0);
}
/* Create a dir named "d/abcdef...". */
buff[0] = 'd';
assertMakeDir("in", 0755);
assertChdir("in");
assertMakeDir("d1", 0755);
- assertMakeSymlink("ld1", "d1");
+ assertMakeSymlink("ld1", "d1", 1);
assertMakeFile("d1/file1", 0644, "d1/file1");
assertMakeFile("d1/file2", 0644, "d1/file2");
- assertMakeSymlink("d1/link1", "file1");
- assertMakeSymlink("d1/linkX", "fileX");
- assertMakeSymlink("link2", "d1/file2");
- assertMakeSymlink("linkY", "d1/fileY");
+ assertMakeSymlink("d1/link1", "file1", 0);
+ assertMakeSymlink("d1/linkX", "fileX", 0);
+ assertMakeSymlink("link2", "d1/file2", 0);
+ assertMakeSymlink("linkY", "d1/fileY", 0);
assertChdir("..");
/* Test 1: Without -H */
assertMakeDir("in", 0755);
assertChdir("in");
assertMakeDir("d1", 0755);
- assertMakeSymlink("ld1", "d1");
+ assertMakeSymlink("ld1", "d1", 1);
assertMakeFile("d1/file1", 0644, "d1/file1");
assertMakeFile("d1/file2", 0644, "d1/file2");
- assertMakeSymlink("d1/link1", "file1");
- assertMakeSymlink("d1/linkX", "fileX");
- assertMakeSymlink("link2", "d1/file2");
- assertMakeSymlink("linkY", "d1/fileY");
+ assertMakeSymlink("d1/link1", "file1", 0);
+ assertMakeSymlink("d1/linkX", "fileX", 0);
+ assertMakeSymlink("link2", "d1/file2", 0);
+ assertMakeSymlink("linkY", "d1/fileY", 0);
assertChdir("..");
/* Test 1: Without -L */
assertMakeDir("test3", 0755);
assertChdir("test3");
assertMakeDir("realDir", 0755);
- assertMakeSymlink("d1", "realDir");
+ assertMakeSymlink("d1", "realDir", 1);
r = systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog);
assert(r != 0);
assertIsSymlink("d1", "realDir");
assertMakeDir("test4", 0755);
assertChdir("test4");
assertMakeDir("realDir", 0755);
- assertMakeSymlink("d1", "realDir");
+ assertMakeSymlink("d1", "realDir", 1);
assertEqualInt(0,
systemf("%s -xUf ../archive.tar >test.out 2>test.err", testprog));
assertIsDir("d1", -1);
assertMakeDir("test5", 0755);
assertChdir("test5");
assertMakeDir("realDir", 0755);
- assertMakeSymlink("d1", "realDir");
+ assertMakeSymlink("d1", "realDir", 1);
assertEqualInt(0,
systemf("%s -xPf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
assertIsSymlink("d1", "realDir");
assertMakeDir("test6", 0755);
assertChdir("test6");
assertMakeDir("realDir", 0755);
- assertMakeSymlink("d1", "realDir");
+ assertMakeSymlink("d1", "realDir", 1);
assertEqualInt(0,
systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
assertIsSymlink("d1", "realDir");
assertChdir("test7");
assertMakeDir("d1", 0755);
assertMakeFile("d1/realfile1", 0644, "realfile1");
- assertMakeSymlink("d1/file1", "d1/realfile1");
+ assertMakeSymlink("d1/file1", "d1/realfile1", 0);
assertEqualInt(0,
systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
assertIsReg("d1/file1", umasked(0644));
assertChdir("test8");
assertMakeDir("d1", 0755);
assertMakeFile("d1/realfile1", 0644, "realfile1");
- assertMakeSymlink("d1/file1", "d1/realfile1");
+ assertMakeSymlink("d1/file1", "d1/realfile1", 0);
assertEqualInt(0,
systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
assertIsReg("d1/file1", umasked(0644));
assertMakeFile("in/d1/bar", 0644, "bar");
if (canSymlink()) {
assertMakeFile("in/d1/realfile", 0644, "realfile");
- assertMakeSymlink("in/d1/symlink", "realfile");
+ assertMakeSymlink("in/d1/symlink", "realfile", 0);
}
assertMakeFile("in/d1/hardlink1", 0644, "hardlinkedfile");
assertMakeHardlink("in/d1/hardlink2", "in/d1/hardlink1");
assertMakeHardlink("l1", "d1/d2/f1");
assertMakeHardlink("d1/l2", "d1/d2/f1");
if (canSymlink()) {
- assertMakeSymlink("s1", "d1/d2/f1");
- assertMakeSymlink("d1/s2", "d2/f1");
+ assertMakeSymlink("s1", "d1/d2/f1", 0);
+ assertMakeSymlink("d1/s2", "d2/f1", 0);
}
assertChdir("..");
/* "dir" is a symlink to an existing "dest1/real_dir" */
assertMakeDir("dest1/real_dir", 0755);
if (canSymlink()) {
- assertMakeSymlink("dest1/dir", "real_dir");
+ assertMakeSymlink("dest1/dir", "real_dir", 1);
/* "dir2" is a symlink to a non-existing "real_dir2" */
- assertMakeSymlink("dest1/dir2", "real_dir2");
+ assertMakeSymlink("dest1/dir2", "real_dir2", 1);
} else {
skipping("Symlinks are not supported on this platform");
}
/* "dir3" is a symlink to an existing "non_dir3" */
assertMakeFile("dest1/non_dir3", 0755, "abcdef");
if (canSymlink())
- assertMakeSymlink("dest1/dir3", "non_dir3");
+ assertMakeSymlink("dest1/dir3", "non_dir3", 1);
/* "file" is a symlink to existing "real_file" */
assertMakeFile("dest1/real_file", 0755, "abcdefg");
if (canSymlink()) {
- assertMakeSymlink("dest1/file", "real_file");
+ assertMakeSymlink("dest1/file", "real_file", 0);
/* "file2" is a symlink to non-existing "real_file2" */
- assertMakeSymlink("dest1/file2", "real_file2");
+ assertMakeSymlink("dest1/file2", "real_file2", 0);
}
assertEqualInt(0, systemf("%s -xf test.tar -C dest1", testprog));
/* "dir" is a symlink to existing "real_dir" */
assertMakeDir("dest2/real_dir", 0755);
if (canSymlink())
- assertMakeSymlink("dest2/dir", "real_dir");
+ assertMakeSymlink("dest2/dir", "real_dir", 1);
/* "dir2" is a symlink to a non-existing "real_dir2" */
if (canSymlink())
- assertMakeSymlink("dest2/dir2", "real_dir2");
+ assertMakeSymlink("dest2/dir2", "real_dir2", 1);
/* "dir3" is a symlink to an existing "non_dir3" */
assertMakeFile("dest2/non_dir3", 0755, "abcdefgh");
if (canSymlink())
- assertMakeSymlink("dest2/dir3", "non_dir3");
+ assertMakeSymlink("dest2/dir3", "non_dir3", 1);
/* "file" is a symlink to existing "real_file" */
assertMakeFile("dest2/real_file", 0755, "abcdefghi");
if (canSymlink())
- assertMakeSymlink("dest2/file", "real_file");
+ assertMakeSymlink("dest2/file", "real_file", 0);
/* "file2" is a symlink to non-existing "real_file2" */
if (canSymlink())
- assertMakeSymlink("dest2/file2", "real_file2");
+ assertMakeSymlink("dest2/file2", "real_file2", 0);
assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog));
/* "dir4" is a symlink to existing "real_dir" */
if (canSymlink())
- assertMakeSymlink("dest2/dir4", "real_dir");
+ assertMakeSymlink("dest2/dir4", "real_dir", 1);
assertEqualInt(0, systemf("%s -xPf test2.tar -C dest2", testprog));
/* dest2/dir and dest2/dir4 symlinks should be followed */
assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents)
#define assertMakeHardlink(newfile, oldfile) \
assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile)
-#define assertMakeSymlink(newfile, linkto) \
- assertion_make_symlink(__FILE__, __LINE__, newfile, linkto)
+#define assertMakeSymlink(newfile, linkto, targetIsDir) \
+ assertion_make_symlink(__FILE__, __LINE__, newfile, linkto, targetIsDir)
#define assertSetNodump(path) \
assertion_set_nodump(__FILE__, __LINE__, path)
#define assertUmask(mask) \
int assertion_make_dir(const char *, int, const char *, int);
int assertion_make_file(const char *, int, const char *, int, int, const void *);
int assertion_make_hardlink(const char *, int, const char *newpath, const char *);
-int assertion_make_symlink(const char *, int, const char *newpath, const char *);
+int assertion_make_symlink(const char *, int, const char *newpath, const char *, int);
int assertion_non_empty_file(const char *, int, const char *);
int assertion_set_nodump(const char *, int, const char *);
int assertion_text_file_contents(const char *, int, const char *buff, const char *f);
}
static int
-my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
+my_CreateSymbolicLinkA(const char *linkname, const char *target,
+ int targetIsDir)
{
static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD);
DWORD attrs;
static int set;
int ret, tmpflags;
- char *tgt, *p;
+ int flags = 0;
+ char *src, *tgt, *p;
if (!set) {
set = 1;
f = GetFunctionKernel32("CreateSymbolicLinkA");
tgt = malloc(strlen(target) + 1);
if (tgt == NULL)
return (0);
+ src = malloc(strlen(linkname) + 1);
+ if (src == NULL) {
+ free(tgt);
+ return (0);
+ }
/*
* Translate slashes to backslashes
*/
+ p = src;
+ while(*linkname != '\0') {
+ if (*linkname == '/')
+ *p = '\\';
+ else
+ *p = *linkname;
+ linkname++;
+ p++;
+ }
+ *p = '\0';
+
p = tgt;
while(*target != '\0') {
if (*target == '/')
* If the target equals ".", ".." or ends with a slash, it always
* points to a directory. In this case we can set the directory flag.
*/
- if (strcmp(tgt, ".") == 0 || strcmp(tgt, "..") == 0 ||
- *(p - 1) == '\\') {
+ if (targetIsDir) {
#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
#else
flags |= 0x1;
#endif
- /* Now we remove trailing backslashes */
- p--;
- while(*p == '\\') {
- *p = '\0';
- p--;
- }
}
#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
DeleteFileA(linkname);
}
- ret = (*f)(linkname, tgt, tmpflags);
+ ret = (*f)(src, tgt, tmpflags);
/*
* Prior to Windows 10 the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
* is not undestood
*/
if (!ret)
- ret = (*f)(linkname, tgt, flags);
+ ret = (*f)(src, tgt, flags);
+ free(src);
free(tgt);
return (ret);
}
return(0);
}
-/* Create a symlink and report any failures. */
+/*
+ * Create a symlink and report any failures.
+ *
+ * Windows symlinks need to know if the target is a directory.
+ */
int
assertion_make_symlink(const char *file, int line,
- const char *newpath, const char *linkto)
+ const char *newpath, const char *linkto, int targetIsDir)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
- int targetIsDir = 0; /* TODO: Fix this */
assertion_count(file, line);
if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir))
return (1);
#elif HAVE_SYMLINK
+ (void)targetIsDir; /* UNUSED */
assertion_count(file, line);
if (0 == symlink(linkto, newpath))
return (1);
+#else
+ (void)targetIsDir; /* UNUSED */
#endif
failure_start(file, line, "Could not create symlink");
logprintf(" New link: %s\n", newpath);