X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/654175798bdbdd6403e10c8fa74e8586b3612ea1..23c5aef18e5a6faba538ff06f83e09835af637d7:/util.c
diff --git a/util.c b/util.c
index 001694b1..16d8f6cc 100644
--- a/util.c
+++ b/util.c
@@ -358,31 +358,6 @@ void kill_all(int sig)
}
}
-/* like strncpy but does not 0 fill the buffer and always null
- terminates (thus it can use maxlen+1 space in d) */
-void strlcpy(char *d, char *s, int maxlen)
-{
- int len = strlen(s);
- if (len > maxlen) len = maxlen;
- memcpy(d, s, len);
- d[len] = 0;
-}
-
-/* like strncat but does not 0 fill the buffer and always null
- terminates (thus it can use maxlen+1 space in d) */
-void strlcat(char *d, char *s, int maxlen)
-{
- int len1 = strlen(d);
- int len2 = strlen(s);
- if (len1+len2 > maxlen) {
- len2 = maxlen-len1;
- }
- if (len2 > 0) {
- memcpy(d+len1, s, len2);
- d[len1+len2] = 0;
- }
-}
-
/* turn a user name into a uid */
int name_to_uid(char *name, uid_t *uid)
{
@@ -425,7 +400,7 @@ int lock_range(int fd, int offset, int len)
}
-static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
+static void glob_expand_one(char *s, char **argv, int *argc, int maxargs, int sanitize_paths)
{
#if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
if (!*s) s = ".";
@@ -438,7 +413,9 @@ static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
if (!*s) s = ".";
- argv[*argc] = strdup(s);
+ s = strdup(s);
+ sanitize_path(s);
+ argv[*argc] = s;
memset(&globbuf, 0, sizeof(globbuf));
glob(argv[*argc], 0, NULL, &globbuf);
@@ -457,7 +434,7 @@ static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
#endif
}
-void glob_expand(char *base1, char **argv, int *argc, int maxargs)
+void glob_expand(char *base1, char **argv, int *argc, int maxargs, int sanitize_paths)
{
char *s = argv[*argc];
char *p, *q;
@@ -481,11 +458,11 @@ void glob_expand(char *base1, char **argv, int *argc, int maxargs)
while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
/* split it at this point */
*p = 0;
- glob_expand_one(q, argv, argc, maxargs);
+ glob_expand_one(q, argv, argc, maxargs, sanitize_paths);
q = p+strlen(base);
}
- if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs);
+ if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs, sanitize_paths);
free(s);
free(base);
@@ -502,14 +479,13 @@ void strlower(char *s)
}
}
-/* this is like vsnprintf but the 'n' limit does not include
- the terminating null. So if you have a 1024 byte buffer then
- pass 1023 for n */
+/* this is like vsnprintf but it always null terminates, so you
+ can fit at most n-1 chars in */
int vslprintf(char *str, int n, const char *format, va_list ap)
{
int ret = vsnprintf(str, n, format, ap);
- if (ret > n || ret < 0) {
- str[n] = 0;
+ if (ret >= n || ret < 0) {
+ str[n-1] = 0;
return -1;
}
str[ret] = 0;
@@ -579,6 +555,74 @@ void clean_fname(char *name)
}
}
+/*
+ * Make path appear as if a chroot had occurred:
+ * 1. remove leading "/" (or replace with "." if at end)
+ * 2. remove leading ".." components
+ * 3. delete any other "
/.." (recursively)
+ * While we're at it, remove double slashes and "." components like
+ * clean_fname does(), but DON'T remove a trailing slash because that
+ * is sometimes significant on command line arguments.
+ * Can only shrink paths, so sanitizes in place.
+ * Contributed by Dave Dykstra
+ */
+
+void sanitize_path(char *p)
+{
+ char *start, *sanp;
+
+ start = p;
+ sanp = p;
+ while (*p == '/') {
+ /* remove leading slashes */
+ p++;
+ }
+ while (*p != '\0') {
+ /* this loop iterates once per filename component in p.
+ * both p (and sanp if the original had a slash) should
+ * always be left pointing after a slash
+ */
+ if ((*p == '.') && ((*(p+1) == '/') || (*(p+1) == '\0'))) {
+ /* skip "." component */
+ while (*++p == '/') {
+ /* skip following slashes */
+ ;
+ }
+ } else if ((*p == '.') && (*(p+1) == '.') &&
+ ((*(p+2) == '/') || (*(p+2) == '\0'))) {
+ /* skip ".." component followed by slash or end */
+ p += 2;
+ if (*p == '/')
+ p++;
+ if (sanp != start) {
+ /* back up sanp one level */
+ --sanp; /* now pointing at slash */
+ while ((sanp > start) && (*(sanp - 1) != '/')) {
+ /* skip back up to slash */
+ sanp--;
+ }
+ }
+ } else {
+ while (1) {
+ /* copy one component through next slash */
+ *sanp++ = *p++;
+ if ((*p == '\0') || (*(p-1) == '/')) {
+ while (*p == '/') {
+ /* skip multiple slashes */
+ p++;
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (sanp == start) {
+ /* ended up with nothing, so put in "." component */
+ *sanp++ = '.';
+ }
+ *sanp = '\0';
+}
+
static char curr_dir[MAXPATHLEN];
@@ -594,6 +638,8 @@ char *push_dir(char *dir, int save)
getcwd(curr_dir, sizeof(curr_dir)-1);
}
+ if (!dir) return NULL; /* this call was probably just to initialize */
+
if (chdir(dir)) return NULL;
if (save) {
@@ -601,10 +647,10 @@ char *push_dir(char *dir, int save)
}
if (*dir == '/') {
- strlcpy(curr_dir, dir, sizeof(curr_dir)-1);
+ strlcpy(curr_dir, dir, sizeof(curr_dir));
} else {
- strlcat(curr_dir,"/", sizeof(curr_dir)-1);
- strlcat(curr_dir,dir, sizeof(curr_dir)-1);
+ strlcat(curr_dir,"/", sizeof(curr_dir));
+ strlcat(curr_dir,dir, sizeof(curr_dir));
}
clean_fname(curr_dir);
@@ -623,7 +669,7 @@ int pop_dir(char *dir)
return ret;
}
- strlcpy(curr_dir, dir, sizeof(curr_dir)-1);
+ strlcpy(curr_dir, dir, sizeof(curr_dir));
free(dir);
@@ -634,8 +680,8 @@ int pop_dir(char *dir)
to ensure that signed/unsigned usage is consistent between machines. */
int u_strcmp(const char *cs1, const char *cs2)
{
- const uchar *s1 = (uchar *)cs1;
- const uchar *s2 = (uchar *)cs2;
+ const uchar *s1 = (const uchar *)cs1;
+ const uchar *s2 = (const uchar *)cs2;
while (*s1 && *s2 && (*s1 == *s2)) {
s1++; s2++;
@@ -716,52 +762,6 @@ int unsafe_symlink(char *dest, char *src)
return (depth < 0);
}
-/*
- * Make path appear as if a chroot had occurred:
- * 1. remove leading "/" (or replace with "." if at end)
- * 2. remove leading ".." components
- * 3. delete any other "/.." (recursively)
- * Return a malloc'ed copy.
- * Contributed by Dave Dykstra
- */
-
-char *sanitize_path(char *p)
-{
- char *copy, *copyp;
-
- copy = (char *) malloc(strlen(p)+1);
- copyp = copy;
- while (*p != '\0') {
- if ((*p == '/') && (copyp == copy)) {
- /* remove leading slash */
- p++;
- }
- else if ((*p == '.') && (*(p+1) == '.') &&
- ((*(p+2) == '/') || (*(p+2) == '\0'))) {
- /* remove .. followed by slash or end */
- p += 2;
- if (copyp != copy) {
- /* backup the copy one level */
- while ((--copyp != copy) && (*copyp == '/'))
- /* skip trailing slashes */
- ;
- while ((copyp != copy) && (*copyp != '/'))
- /* skip back through slash */
- copyp--;
- }
- } else {
- /* copy one component */
- while (1) {
- *copyp++ = *p++;
- if ((*p == '\0') || (*(p-1) == '/'))
- break;
- }
- }
- }
- *copyp = '\0';
- return(copy);
-}
-
/****************************************************************************
return the date and time as a string
@@ -774,7 +774,7 @@ char *timestring(time_t t)
#ifdef HAVE_STRFTIME
strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %T",tm);
#else
- strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf)-1);
+ strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf));
#endif
if (TimeBuf[strlen(TimeBuf)-1] == '\n') {