Skip to content
Snippets Groups Projects
Commit 2488f8ea authored by Kathleen Brade's avatar Kathleen Brade Committed by Pier Angelo Vendrame
Browse files

Bug 12647: Support symlinks in the updater.

parent 67fa7020
No related branches found
No related tags found
1 merge request!941Bug 42449: Rebase Tor Browser alpha onto Firefox 115.9.0esr
......@@ -5,6 +5,7 @@
# update instructions which is used to remove files and directories that are no
# longer present in a complete update. The current working directory is used for
# the location to enumerate and to create the precomplete file.
# For symlinks, remove instructions are always generated.
import io
import os
......@@ -45,6 +46,9 @@ def get_build_entries(root_path):
rel_path_dir = os.path.join(parent_dir_rel_path, dir_name)
rel_path_dir = rel_path_dir.replace("\\", "/") + "/"
if rel_path_dir.find("distribution/") == -1:
if os.path.islink(rel_path_dir[:-1]):
rel_file_path_set.add(rel_path_dir[:-1])
else:
rel_dir_path_set.add(rel_path_dir)
rel_file_path_list = list(rel_file_path_set)
......
......@@ -16,7 +16,7 @@
* updatev3.manifest
* -----------------
* method = "add" | "add-if" | "add-if-not" | "patch" | "patch-if" |
* "remove" | "rmdir" | "rmrfdir" | type
* "remove" | "rmdir" | "rmrfdir" | "addsymlink" | type
*
* 'add-if-not' adds a file if it doesn't exist.
*
......@@ -493,7 +493,8 @@ static const NS_tchar* get_relative_path(const NS_tchar* fullpath) {
* Whether the path is a directory path. Defaults to false.
* @return valid filesystem path or nullptr if the path checks fail.
*/
static NS_tchar* get_valid_path(NS_tchar** line, bool isdir = false) {
static NS_tchar* get_valid_path(NS_tchar** line, bool isdir = false,
bool islinktarget = false) {
NS_tchar* path = mstrtok(kQuote, line);
if (!path) {
LOG(("get_valid_path: unable to determine path: " LOG_S, *line));
......@@ -529,11 +530,13 @@ static NS_tchar* get_valid_path(NS_tchar** line, bool isdir = false) {
path[NS_tstrlen(path) - 1] = NS_T('\0');
}
if (!islinktarget) {
// Don't allow relative paths that resolve to a parent directory.
if (NS_tstrstr(path, NS_T("..")) != nullptr) {
LOG(("get_valid_path: paths must not contain '..': " LOG_S, path));
return nullptr;
}
}
return path;
}
......@@ -572,7 +575,7 @@ static void ensure_write_permissions(const NS_tchar* path) {
(void)_wchmod(path, _S_IREAD | _S_IWRITE);
#else
struct stat fs;
if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR)) {
if (!lstat(path, &fs) && !S_ISLNK(fs.st_mode) && !(fs.st_mode & S_IWUSR)) {
(void)chmod(path, fs.st_mode | S_IWUSR);
}
#endif
......@@ -759,11 +762,9 @@ static int ensure_copy(const NS_tchar* path, const NS_tchar* dest) {
return READ_ERROR;
}
# ifdef XP_UNIX
if (S_ISLNK(ss.st_mode)) {
return ensure_copy_symlink(path, dest);
}
# endif
AutoFile infile(ensure_open(path, NS_T("rb"), ss.st_mode));
if (!infile) {
......@@ -850,11 +851,13 @@ static int ensure_copy_recursive(const NS_tchar* path, const NS_tchar* dest,
return READ_ERROR;
}
#ifdef XP_UNIX
#ifndef XP_WIN
if (S_ISLNK(sInfo.st_mode)) {
return ensure_copy_symlink(path, dest);
}
#endif
#ifdef XP_UNIX
// Ignore Unix domain sockets. See #20691.
if (S_ISSOCK(sInfo.st_mode)) {
return 0;
......@@ -917,7 +920,7 @@ static int rename_file(const NS_tchar* spath, const NS_tchar* dpath,
}
struct NS_tstat_t spathInfo;
rv = NS_tstat(spath, &spathInfo);
rv = NS_tlstat(spath, &spathInfo); // Get info about file or symlink.
if (rv) {
LOG(("rename_file: failed to read file status info: " LOG_S ", "
"err: %d",
......@@ -925,7 +928,12 @@ static int rename_file(const NS_tchar* spath, const NS_tchar* dpath,
return READ_ERROR;
}
if (!S_ISREG(spathInfo.st_mode)) {
#ifdef XP_WIN
if (!S_ISREG(spathInfo.st_mode))
#else
if (!S_ISREG(spathInfo.st_mode) && !S_ISLNK(spathInfo.st_mode))
#endif
{
if (allowDirs && !S_ISDIR(spathInfo.st_mode)) {
LOG(("rename_file: path present, but not a file: " LOG_S ", err: %d",
spath, errno));
......@@ -934,7 +942,12 @@ static int rename_file(const NS_tchar* spath, const NS_tchar* dpath,
LOG(("rename_file: proceeding to rename the directory"));
}
if (!NS_taccess(dpath, F_OK)) {
#ifdef XP_WIN
if (!NS_taccess(dpath, F_OK))
#else
if (!S_ISLNK(spathInfo.st_mode) && !NS_taccess(dpath, F_OK))
#endif
{
if (ensure_remove(dpath)) {
LOG(
("rename_file: destination file exists and could not be "
......@@ -1053,7 +1066,19 @@ static int backup_restore(const NS_tchar* path, const NS_tchar* relPath) {
NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
NS_T("%s") BACKUP_EXT, relPath);
if (NS_taccess(backup, F_OK)) {
bool isLink = false;
#ifndef XP_WIN
struct stat linkInfo;
int rv = lstat(backup, &linkInfo);
if (rv) {
LOG(("backup_restore: cannot get info for backup file: " LOG_S ", err: %d",
relBackup, errno));
return OK;
}
isLink = S_ISLNK(linkInfo.st_mode);
#endif
if (!isLink && NS_taccess(backup, F_OK)) {
LOG(("backup_restore: backup file doesn't exist: " LOG_S, relBackup));
return OK;
}
......@@ -1071,8 +1096,18 @@ static int backup_discard(const NS_tchar* path, const NS_tchar* relPath) {
NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
NS_T("%s") BACKUP_EXT, relPath);
bool isLink = false;
#ifndef XP_WIN
struct stat linkInfo;
int rv2 = lstat(backup, &linkInfo);
if (rv2) {
return OK; // File does not exist; nothing to do.
}
isLink = S_ISLNK(linkInfo.st_mode);
#endif
// Nothing to discard
if (NS_taccess(backup, F_OK)) {
if (!isLink && NS_taccess(backup, F_OK)) {
return OK;
}
......@@ -1160,7 +1195,7 @@ class Action {
class RemoveFile : public Action {
public:
RemoveFile() : mSkip(0) {}
RemoveFile() : mSkip(0), mIsLink(0) {}
int Parse(NS_tchar* line) override;
int Prepare() override;
......@@ -1171,6 +1206,7 @@ class RemoveFile : public Action {
mozilla::UniquePtr<NS_tchar[]> mFile;
mozilla::UniquePtr<NS_tchar[]> mRelPath;
int mSkip;
int mIsLink;
};
int RemoveFile::Parse(NS_tchar* line) {
......@@ -1193,16 +1229,26 @@ int RemoveFile::Parse(NS_tchar* line) {
}
int RemoveFile::Prepare() {
int rv;
#ifndef XP_WIN
struct stat linkInfo;
rv = lstat(mFile.get(), &linkInfo);
mIsLink = ((0 == rv) && S_ISLNK(linkInfo.st_mode));
#endif
if (!mIsLink) {
// Skip the file if it already doesn't exist.
int rv = NS_taccess(mFile.get(), F_OK);
rv = NS_taccess(mFile.get(), F_OK);
if (rv) {
mSkip = 1;
mProgressCost = 0;
return OK;
}
}
LOG(("PREPARE REMOVEFILE " LOG_S, mRelPath.get()));
if (!mIsLink) {
// Make sure that we're actually a file...
struct NS_tstat_t fileInfo;
rv = NS_tstat(mFile.get(), &fileInfo);
......@@ -1216,6 +1262,7 @@ int RemoveFile::Prepare() {
LOG(("path present, but not a file: " LOG_S, mFile.get()));
return DELETE_ERROR_EXPECTED_FILE;
}
}
NS_tchar* slash = (NS_tchar*)NS_tstrrchr(mFile.get(), NS_T('/'));
if (slash) {
......@@ -1966,6 +2013,92 @@ void PatchIfFile::Finish(int status) {
PatchFile::Finish(status);
}
#ifndef XP_WIN
class AddSymlink : public Action {
public:
AddSymlink() : mAdded(false) {}
virtual int Parse(NS_tchar* line);
virtual int Prepare();
virtual int Execute();
virtual void Finish(int status);
private:
mozilla::UniquePtr<NS_tchar[]> mLinkPath;
mozilla::UniquePtr<NS_tchar[]> mRelPath;
mozilla::UniquePtr<NS_tchar[]> mTarget;
bool mAdded;
};
int AddSymlink::Parse(NS_tchar* line) {
// format "<linkname>" "target"
NS_tchar* validPath = get_valid_path(&line);
if (!validPath) return PARSE_ERROR;
mRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
NS_tstrcpy(mRelPath.get(), validPath);
mLinkPath.reset(get_full_path(validPath));
if (!mLinkPath) {
return PARSE_ERROR;
}
// consume whitespace between args
NS_tchar* q = mstrtok(kQuote, &line);
if (!q) return PARSE_ERROR;
validPath = get_valid_path(&line, false, true);
if (!validPath) return PARSE_ERROR;
mTarget = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
NS_tstrcpy(mTarget.get(), validPath);
return OK;
}
int AddSymlink::Prepare() {
LOG(("PREPARE ADDSYMLINK " LOG_S " -> " LOG_S, mRelPath.get(),
mTarget.get()));
return OK;
}
int AddSymlink::Execute() {
LOG(("EXECUTE ADDSYMLINK " LOG_S " -> " LOG_S, mRelPath.get(),
mTarget.get()));
// First make sure that we can actually get rid of any existing file or link.
struct stat linkInfo;
int rv = lstat(mLinkPath.get(), &linkInfo);
if ((0 == rv) && !S_ISLNK(linkInfo.st_mode)) {
rv = NS_taccess(mLinkPath.get(), F_OK);
}
if (rv == 0) {
rv = backup_create(mLinkPath.get());
if (rv) return rv;
} else {
rv = ensure_parent_dir(mLinkPath.get());
if (rv) return rv;
}
// Create the link.
rv = symlink(mTarget.get(), mLinkPath.get());
if (!rv) {
mAdded = true;
}
return rv;
}
void AddSymlink::Finish(int status) {
LOG(("FINISH ADDSYMLINK " LOG_S " -> " LOG_S, mRelPath.get(), mTarget.get()));
// When there is an update failure and a link has been added it is removed
// here since there might not be a backup to replace it.
if (status && mAdded) NS_tremove(mLinkPath.get());
backup_finish(mLinkPath.get(), mRelPath.get(), status);
}
#endif
//-----------------------------------------------------------------------------
#ifdef XP_WIN
......@@ -4760,6 +4893,10 @@ int DoUpdate() {
action = new AddIfNotFile();
} else if (NS_tstrcmp(token, NS_T("patch-if")) == 0) { // Patch if exists
action = new PatchIfFile();
#ifndef XP_WIN
} else if (NS_tstrcmp(token, NS_T("addsymlink")) == 0) {
action = new AddSymlink();
#endif
} else {
LOG(("DoUpdate: unknown token: " LOG_S, token));
free(buf);
......
......@@ -126,6 +126,15 @@ make_add_if_instruction() {
echo "add-if \"$f\" \"$f\"" >> "$filev3"
}
make_addsymlink_instruction() {
link="$1"
target="$2"
filev3="$3"
verbose_notice " addsymlink: $link -> $target"
echo "addsymlink \"$link\" \"$target\"" >> "$filev3"
}
make_patch_instruction() {
f="$1"
filev3="$2"
......@@ -216,3 +225,19 @@ list_dirs() {
done < "${temp_dirlist}"
rm "${temp_dirlist}"
}
# List all symbolic links in the current directory, stripping leading "./"
list_symlinks() {
count=0
find . -type l \
| sed 's/\.\/\(.*\)/\1/' \
| sort -r > "temp-symlinklist"
while read symlink; do
target=$(readlink "$symlink")
eval "${1}[$count]=\"$symlink\""
eval "${2}[$count]=\"$target\""
(( count++ ))
done < "temp-symlinklist"
rm "temp-symlinklist"
}
......@@ -71,6 +71,7 @@ if [ ! -f "precomplete" ]; then
fi
list_files files
list_symlinks symlinks symlink_targets
popd
......@@ -120,6 +121,15 @@ for ((i=0; $i<$num_files; i=$i+1)); do
targetfiles="$targetfiles \"$f\""
done
notice ""
notice "Adding symlink add instructions to update manifests"
num_symlinks=${#symlinks[*]}
for ((i=0; $i<$num_symlinks; i=$i+1)); do
link="${symlinks[$i]}"
target="${symlink_targets[$i]}"
make_addsymlink_instruction "$link" "$target" "$updatemanifestv3"
done
# Append remove instructions for any dead files.
notice ""
notice "Adding file and directory remove instructions from file 'removed-files'"
......
......@@ -148,6 +148,7 @@ fi
list_files oldfiles
list_dirs olddirs
list_symlinks oldsymlinks oldsymlink_targets
popd
......@@ -165,6 +166,7 @@ fi
list_dirs newdirs
list_files newfiles
list_symlinks newsymlinks newsymlink_targets
popd
......@@ -293,6 +295,23 @@ for ((i=0; $i<$num_oldfiles; i=$i+1)); do
fi
done
# Remove and re-add symlinks
notice ""
notice "Adding symlink remove/add instructions to update manifests"
num_oldsymlinks=${#oldsymlinks[*]}
for ((i=0; $i<$num_oldsymlinks; i=$i+1)); do
link="${oldsymlinks[$i]}"
verbose_notice " remove: $link"
echo "remove \"$link\"" >> "$updatemanifestv3"
done
num_newsymlinks=${#newsymlinks[*]}
for ((i=0; $i<$num_newsymlinks; i=$i+1)); do
link="${newsymlinks[$i]}"
target="${newsymlink_targets[$i]}"
make_addsymlink_instruction "$link" "$target" "$updatemanifestv3"
done
# Newly added files
notice ""
notice "Adding file add instructions to update manifests"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment