Skip to content

Commit 59b2586

Browse files
author
kalibera
committed
Improved handling of links, including directory symlinks on Windows.
git-svn-id: https://svn.r-project.org/R/trunk@88326 00db46b3-68df-0310-9c12-caf00c1e9a41
1 parent f4d1628 commit 59b2586

File tree

1 file changed

+59
-28
lines changed

1 file changed

+59
-28
lines changed

src/library/utils/R/tar.R

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ untar2 <- function(tarfile, files = NULL, list = FALSE, exdir = ".",
150150
if (grepl("^[a-zA-Z]:", path)) {
151151
drv <- sub("^([a-zA-Z]:).*", "\\1", path)
152152
warning(sprintf("removing drive '%s'", drv))
153-
path <- sub("^([a-zA-Z]:).*", "", path)
153+
path <- sub("^([a-zA-Z]:)", "", path)
154154
}
155155
path <- gsub("\\\\", "/", path)
156156
}
@@ -164,19 +164,29 @@ untar2 <- function(tarfile, files = NULL, list = FALSE, exdir = ".",
164164
if (".." %in% parts)
165165
stop("path contains '..'")
166166
if (length(parts) == 0)
167-
stop("path is empty")
168-
do.call(file.path, as.list(parts))
167+
return(".")
168+
p <- ""
169+
for(el in parts) {
170+
if (nzchar(p))
171+
p <- file.path(p, el)
172+
else
173+
p <- el
174+
if(isTRUE(nzchar(Sys.readlink(p), keepNA=TRUE)))
175+
stop("cannot extract through symlink")
176+
}
177+
p
169178
}
170179
mydir.create <- function(path, ..., .checkPath = TRUE) {
171180
## for Windows' sake
172181
path <- sub("[\\/]$", "", path)
173182
## address path traversal vulnerability (PR17853):
174183
if (.checkPath)
175184
path <- checkPath(path)
176-
if(dir.exists(path)) return()
177-
if(!dir.create(path, showWarnings = TRUE, recursive = TRUE, ...))
178-
stop(gettextf("failed to create directory %s", sQuote(path)),
179-
domain = NA)
185+
if(!dir.exists(path) &&
186+
!dir.create(path, showWarnings = TRUE, recursive = TRUE, ...))
187+
stop(gettextf("failed to create directory %s", sQuote(path)),
188+
domain = NA)
189+
path
180190
}
181191

182192
warn1 <- character()
@@ -250,8 +260,10 @@ untar2 <- function(tarfile, files = NULL, list = FALSE, exdir = ".",
250260
dothis <- !list
251261
if(dothis && length(files)) dothis <- name %in% files
252262
if(dothis) {
253-
mydir.create(dirname(name))
254-
out <- file(name, "wb")
263+
dname <- mydir.create(dirname(name))
264+
fname <- file.path(dname, basename(name))
265+
unlink(fname)
266+
out <- file(fname, "wb")
255267
}
256268
for(i in seq_len(ceiling(size/512L))) {
257269
block <- readBin(con, "raw", n = 512L)
@@ -264,8 +276,8 @@ untar2 <- function(tarfile, files = NULL, list = FALSE, exdir = ".",
264276
}
265277
if(dothis) {
266278
close(out)
267-
Sys.chmod(name, mode, FALSE) # override umask
268-
if(restore_times) Sys.setFileTime(name, ft)
279+
Sys.chmod(fname, mode, FALSE) # override umask
280+
if(restore_times) Sys.setFileTime(fname, ft)
269281
}
270282
} else if(ctype %in% c("1", "2")) {
271283
## hard and symbolic links
@@ -276,27 +288,43 @@ untar2 <- function(tarfile, files = NULL, list = FALSE, exdir = ".",
276288
if(!is.null(llink)) {name2 <- llink; llink <- NULL}
277289
if(!list) {
278290
if(ctype == "1") {
279-
mydir.create(dirname(name))
280-
unlink(name)
291+
dname <- mydir.create(dirname(name))
292+
fname <- file.path(dname, basename(name))
293+
unlink(fname)
281294
if (!file.link(name2, name)) { # will give a warning
282295
## link failed, so try a file copy
283-
if(file.copy(name2, name))
284-
warn1 <- c(warn1, "restoring hard link as a file copy")
296+
if(file.copy(name2, fname))
297+
warn1 <- c(warn1, "restoring hard link as a file copy")
285298
else
286-
warning(gettextf("failed to copy %s to %s", sQuote(name2), sQuote(name)), domain = NA)
299+
warning(gettextf("failed to copy %s to %s", sQuote(name2), sQuote(fname)), domain = NA)
287300
}
288301
} else {
289302
if(.Platform$OS.type == "windows") {
290-
## this will not work for links to dirs
291-
mydir.create(dirname(name))
292-
from <- file.path(dirname(name), name2)
293-
if (!file.copy(from, name))
294-
warning(gettextf("failed to copy %s to %s", sQuote(from), sQuote(name)), domain = NA)
295-
else
296-
warn1 <- c(warn1, "restoring symbolic link as a file copy")
303+
dname <- mydir.create(dirname(name))
304+
fname <- file.path(dname, basename(name))
305+
unlink(fname)
306+
from <- file.path(dname, name2)
307+
if (dir.exists(from)) {
308+
tmpd <- tempfile(pattern = "untar_", tmpdir = dname)
309+
dir.create(tmpd)
310+
if (!file.copy(from, tmpd, recursive = TRUE) ||
311+
!file.rename(file.path(tmpd, basename(name2)), fname) ||
312+
!unlink(tmpd, recursive = TRUE))
313+
warning(gettextf("failed to copy %s to %s", sQuote(from), sQuote(fname)), domain = NA)
314+
else
315+
warn1 <- c(warn1, "restoring symbolic link as a file copy")
316+
} else if (!file.exists(from)) {
317+
warning(gettextf("cannot restore symbolic link from %s to %s as a file copy, because the source doesn't exist; try extracting again?",
318+
sQuote(from), sQuote(fname)), domain = NA)
319+
} else {
320+
if (!file.copy(from, fname))
321+
warning(gettextf("failed to copy %s to %s", sQuote(from), sQuote(fname)), domain = NA)
322+
else
323+
warn1 <- c(warn1, "restoring symbolic link as a file copy")
324+
}
297325
} else {
298-
mydir.create(dirname(name))
299-
od0 <- setwd(dirname(name))
326+
dname <- mydir.create(dirname(name))
327+
od0 <- setwd(dname)
300328
nm <- basename(name)
301329
unlink(nm)
302330
if(!file.symlink(name2, nm)) { # will give a warning
@@ -317,9 +345,12 @@ untar2 <- function(tarfile, files = NULL, list = FALSE, exdir = ".",
317345
## directory
318346
contents <- c(contents, name)
319347
if(!list) {
320-
mydir.create(name)
321-
Sys.chmod(name, mode, TRUE) # FIXME: check result
322-
## no point is setting time, as dir will be populated later.
348+
dname <- mydir.create(dirname(name))
349+
fname <- file.path(dname, basename(name))
350+
unlink(fname)
351+
if(!dir.exists(fname) && !dir.create(fname, mode = mode))
352+
stop(gettextf("failed to create directory %s", sQuote(fname)))
353+
## no point in setting time, as dir will be populated later.
323354
}
324355
} else if(ctype == "6") {
325356
## 6 is a fifo

0 commit comments

Comments
 (0)