diff options
Diffstat (limited to 'src/util.c')
-rw-r--r-- | src/util.c | 77 |
1 files changed, 77 insertions, 0 deletions
@@ -423,6 +423,60 @@ create_backup (char const *to, const struct stat *to_st, bool leave_original) } } +static bool +symlink_target_is_valid (char const *target, char const *to) +{ + bool is_valid; + + if (IS_ABSOLUTE_FILE_NAME (to)) + is_valid = true; + else if (IS_ABSOLUTE_FILE_NAME (target)) + is_valid = false; + else + { + unsigned int depth = 0; + char const *t; + + is_valid = true; + t = to; + while (*t) + { + while (*t && ! ISSLASH (*t)) + t++; + if (ISSLASH (*t)) + { + while (ISSLASH (*t)) + t++; + depth++; + } + } + + t = target; + while (*t) + { + if (*t == '.' && *++t == '.' && (! *++t || ISSLASH (*t))) + { + if (! depth--) + { + is_valid = false; + break; + } + } + else + { + while (*t && ! ISSLASH (*t)) + t++; + depth++; + } + while (ISSLASH (*t)) + t++; + } + } + + /* Allow any symlink target if we are in the filesystem root. */ + return is_valid || cwd_is_root (to); +} + /* Move a file FROM (where *FROM_NEEDS_REMOVAL is nonzero if FROM needs removal when cleaning up at the end of execution, and where *FROMST is FROM's status if known), @@ -1665,3 +1719,26 @@ int stat_file (char const *filename, struct stat *st) return xstat (filename, st) == 0 ? 0 : errno; } + +/* Check if we are in the root of a particular filesystem namespace ("/" on + UNIX or a particular drive's root on DOS-like systems). */ +bool +cwd_is_root (char const *name) +{ + unsigned int prefix_len = FILE_SYSTEM_PREFIX_LEN (name); + char root[prefix_len + 2]; + struct stat st; + dev_t root_dev; + ino_t root_ino; + + memcpy (root, name, prefix_len); + root[prefix_len] = '/'; + root[prefix_len + 1] = 0; + if (stat (root, &st)) + return false; + root_dev = st.st_dev; + root_ino = st.st_ino; + if (stat (".", &st)) + return false; + return root_dev == st.st_dev && root_ino == st.st_ino; +} |