/** * This function emulates the @result of readlink("@referer") with * respect to @tracee, where @referer is a strict subpath of "/proc". * This function returns the length of @result if the readlink was * emulated, 0 otherwise. * * Unlike readlink(), this function includes the nul terminating byte * to @result (but this byte is not counted in the returned value). */ size_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char referer[PATH_MAX]) { Action action; char base[PATH_MAX]; char *component; assert(compare_paths("/proc", referer) == PATH1_IS_PREFIX); /* It's safe to use strrchr() here since @referer was * previously canonicalized. */ strcpy(base, referer); component = strrchr(base, '/'); /* These cases are not possible: @referer is supposed to be a * canonicalized subpath of "/proc". */ assert(component != NULL && component != base); component[0] = '\0'; component++; if (component[0] == '\0') return 0; action = readlink_proc(tracee, result, base, component, PATH1_IS_PREFIX); return (action == CANONICALIZE ? strlen(result) : 0); }
/** * Check if the translated @host_path belongs to the guest rootfs, * that is, isn't from a binding. */ bool belongs_to_guestfs(const Tracee *tracee, const char *host_path) { Comparison comparison; comparison = compare_paths(get_root(tracee), host_path); return (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX); }
/** * Bind content of @vectors over /proc/{@ptracee->pid}/auxv. This * function returns -1 if an error occurred, otherwise 0. */ static int bind_proc_pid_auxv(const Tracee *ptracee) { word_t vectors_address; ElfAuxVector *vectors; const char *guest_path; const char *host_path; Binding *binding; int status; vectors_address = get_elf_aux_vectors_address(ptracee); if (vectors_address == 0) return -1; vectors = fetch_elf_aux_vectors(ptracee, vectors_address); if (vectors == NULL) return -1; /* Path to these ELF auxiliary vectors. */ guest_path = talloc_asprintf(ptracee->ctx, "/proc/%d/auxv", ptracee->pid); if (guest_path == NULL) return -1; /* Remove binding to this path, if any. It contains ELF * auxiliary vectors of the previous execve(2). */ binding = get_binding(ptracee, GUEST, guest_path); if (binding != NULL && compare_paths(binding->guest.path, guest_path) == PATHS_ARE_EQUAL) { remove_binding_from_all_lists(ptracee, binding); TALLOC_FREE(binding); } host_path = create_temp_file(ptracee->ctx, "auxv"); if (host_path == NULL) return -1; status = fill_file_with_auxv(ptracee, host_path, vectors); if (status < 0) return -1; /* Note: this binding will be removed once ptracee gets freed. */ binding = insort_binding3(ptracee, ptracee->life_context, host_path, guest_path); if (binding == NULL) return -1; /* This temporary file (host_path) will be removed once the * binding is freed. */ talloc_reparent(ptracee->ctx, binding, host_path); return 0; }
int is_virtual_path ( char const* path ) { size_t len = 0; ZIPFSVirtualPath* cursor = sVirtualPaths; for ( ; cursor; cursor = cursor->mNext ) { char* test = cursor->mPath; len = compare_paths ( test, path ); if ( !test [ len ]) return 1; } return 0; }
//----------------------------------------------------------------// char* zipfs_get_rel_path ( char const* path ) { size_t depth = 0; size_t i = 0; size_t same; if ( !path ) return 0; if ( path [ 0 ] == '.' ) { return zipfs_bless_path ( path ); } if ( !(( path [ 0 ] == '/' ) || ( path [ 0 ] == '\\' ) || ( path [ 0 ] == ':' ))) { ZIPFSString_Set ( sBuffer, "./" ); ZIPFSString_Append ( sBuffer, path ); zipfs_bless_path ( sBuffer->mMem ); return sBuffer->mMem; } // store the expanded path in sBuffer zipfs_get_abs_filepath ( path ); same = compare_paths ( path, sWorkingPath->mMem ); if ( same == 0 ) { return zipfs_bless_path ( path ); } // count the number of steps up in the current directory for ( i = same; sWorkingPath->mMem [ i ]; ++i ) { if ( sWorkingPath->mMem [ i ] == '/' ) { depth++; } } ZIPFSString_Shift ( sBuffer, same, sBuffer->mStrLen - same, depth * 3 ); sBuffer->mStrLen = sBuffer->mStrLen - same + ( depth * 3 ); sBuffer->mMem [ sBuffer->mStrLen ] = 0; for ( i = 0; i < depth; ++i ) { size_t j = i * 3; sBuffer->mMem [ j + 0 ] = '.'; sBuffer->mMem [ j + 1 ] = '.'; sBuffer->mMem [ j + 2 ] = '/'; } return sBuffer->mMem; }
ZIPFSVirtualPath* find_virtual_path ( char const* path ) { size_t len = 0; ZIPFSVirtualPath* cursor = sVirtualPaths; for ( ; cursor; cursor = cursor->mNext ) { char* test = cursor->mPath; len = compare_paths ( test, path ); if ( !( test [ len ] || path [ len ])) break; } return cursor; }
/** * Print all bindings (verbose purpose). */ static void print_bindings(const Tracee *tracee) { const Binding *binding; if (tracee->fs->bindings.guest == NULL) return; CIRCLEQ_FOREACH_(tracee, binding, GUEST) { if (compare_paths(binding->host.path, binding->guest.path) == PATHS_ARE_EQUAL) notice(tracee, INFO, USER, "binding = %s", binding->host.path); else notice(tracee, INFO, USER, "binding = %s:%s", binding->host.path, binding->guest.path); } }
ZIPFSVirtualPath* find_next_virtual_subdir ( char const* path, ZIPFSVirtualPath* cursor ) { size_t len = 0; cursor = cursor ? cursor->mNext : sVirtualPaths; for ( ; cursor; cursor = cursor->mNext ) { char* test = cursor->mPath; len = compare_paths ( test, path ); if ( test [ len ] && ( !path [ len ])) { return cursor; } } return 0; }
ZIPFSVirtualPath* find_best_virtual_path ( char const* path ) { size_t len = 0; size_t bestlen = 0; ZIPFSVirtualPath* best = 0; ZIPFSVirtualPath* cursor = sVirtualPaths; for ( ; cursor; cursor = cursor->mNext ) { char* test = cursor->mPath; len = compare_paths ( test, path ); if (( !test [ len ]) && ( len > bestlen )) { best = cursor; bestlen = len; } } return best; }
/** * Get the binding for the given @path (relatively to the given * binding @side). */ static const Binding *get_binding(Tracee *tracee, Side side, const char path[PATH_MAX]) { const Binding *binding; size_t path_length = strlen(path); /* Sanity checks. */ assert(path != NULL && path[0] == '/'); CIRCLEQ_FOREACH_(tracee, binding, side) { Comparison comparison; const Path *ref; switch (side) { case GUEST: ref = &binding->guest; break; case HOST: ref = &binding->host; break; default: assert(0); return NULL; } comparison = compare_paths2(ref->path, ref->length, path, path_length); if ( comparison != PATHS_ARE_EQUAL && comparison != PATH1_IS_PREFIX) continue; /* Avoid false positive when a prefix of the rootfs is * used as an asymmetric binding, ex.: * * proot -m /usr:/location /usr/local/slackware */ if ( side == HOST && compare_paths(get_root(tracee), "/") != PATHS_ARE_EQUAL && belongs_to_guestfs(tracee, path)) continue; return binding; }
file_t * database_find_file (const database_t * db, const char * path) { // We maintain the invariants: All items below index low are before path. // All items at or above index high are after path. If path is present, // then its index is between low (inclusive) and high (exclusive). ssize_t low = 0; ssize_t high = db->files_end - db->files; while (high > low) { ssize_t mid = (low + high) >> 1; int c = compare_paths (db->files[mid].path, path); if (c == 0) return db->files + mid; if (c < 0) low = mid + 1; else high = mid; } return NULL; }
void bcp_implementation::output_license_info() { std::pair<const license_info*, int> licenses = get_licenses(); std::map<int, license_data>::const_iterator i, j; i = m_license_data.begin(); j = m_license_data.end(); std::ofstream os(m_dest_path.string().c_str()); if(!os) { std::string msg("Error opening "); msg += m_dest_path.string(); msg += " for output."; std::runtime_error e(msg); boost::throw_exception(e); } os << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" "<html>\n" "<head>\n" "<title>Boost Licence Dependency Information"; if(m_module_list.size() == 1) { os << " for " << *(m_module_list.begin()); } os << "</title>\n" "</head>\n" "<body>\n" "<H1>Boost Licence Dependency Information"; if(m_module_list.size() == 1) { os << " for " << *(m_module_list.begin()); } os << "</H1>\n" "<H2>Contents</h2>\n" "<pre><a href=\"#input\">Input Information</a>\n"; if(!m_bsl_summary_mode) os << "<a href=\"#summary\">Licence Summary</a>\n"; os << "<a href=\"#details\">Licence Details</a>\n"; while(i != j) { // title: os << " <A href=\"#" << make_link_target(licenses.first[i->first].license_name) << "\">" << licenses.first[i->first].license_name << "</a>\n"; ++i; } os << "<a href=\"#files\">Files with no recognised license</a>\n" "<a href=\"#authors\">Files with no recognised copyright holder</a>\n"; if(!m_bsl_summary_mode) { os << "Moving to the Boost Software License...\n" " <a href=\"#bsl-converted\">Files that can be automatically converted to the Boost Software License</a>\n" " <a href=\"#to-bsl\">Files that can be manually converted to the Boost Software License</a>\n" " <a href=\"#not-to-bsl\">Files that can <b>NOT</b> be moved to the Boost Software License</a>\n" " <a href=\"#need-bsl-authors\">Authors we need to move to the Boost Software License</a>\n" "<a href=\"#copyright\">Copyright Holder Information</a>\n"; } os << "<a href=\"#depend\">File Dependency Information</a>\n" "</pre>"; // // input Information: // os << "<a name=\"input\"></a><h2>Input Information</h2>\n"; if(m_scan_mode) os << "<P>The following files were scanned for boost dependencies:<BR>"; else os << "<P>The following Boost modules were checked:<BR>"; std::list<std::string>::const_iterator si = m_module_list.begin(); std::list<std::string>::const_iterator sj = m_module_list.end(); while(si != sj) { os << *si << "<BR>"; ++si; } os << "</p><p>The Boost path was: <code>" << m_boost_path.string() << "</code></P>"; // // extract the boost version number from the boost directory tree, // not from this app (which may have been built from a previous // version): // fileview version_file(m_boost_path / "boost/version.hpp"); static const boost::regex version_regex( "^[[:blank:]]*#[[:blank:]]*define[[:blank:]]+BOOST_VERSION[[:blank:]]+(\\d+)"); boost::cmatch what; if(boost::regex_search(version_file.begin(), version_file.end(), what, version_regex)) { int version = boost::lexical_cast<int>(what.str(1)); os << "<p>The Boost version is: " << version / 100000 << "." << version / 100 % 1000 << "." << version % 100 << "</P>\n"; } // // output each license: // i = m_license_data.begin(); j = m_license_data.end(); if(!m_bsl_summary_mode) { // // start with the summary: // os << "<a name=\"summary\"></a><h2>Licence Summary</h2>\n"; while(i != j) { // title: os << "<H3>" << licenses.first[i->first].license_name << "</H3>\n"; // license text: os << "<BLOCKQUOTE>" << licenses.first[i->first].license_text << "</BLOCKQUOTE>"; // Copyright holders: os << "<P>This license is used by " << i->second.authors.size() << " authors and " << i->second.files.size() << " files <a href=\"#" << make_link_target(licenses.first[i->first].license_name) << "\">(see details)</a>"; os << "</P></BLOCKQUOTE>\n"; ++i; } } // // and now the details: // i = m_license_data.begin(); j = m_license_data.end(); int license_index = 0; os << "<a name=\"details\"></a><h2>Licence Details</h2>\n"; while(i != j) { // title: os << "<H3><A name=\"" << make_link_target(licenses.first[i->first].license_name) << "\"></a>" << licenses.first[i->first].license_name << "</H3>\n"; // license text: os << "<BLOCKQUOTE>" << licenses.first[i->first].license_text << "</BLOCKQUOTE>"; if(!m_bsl_summary_mode || (license_index >= 3)) { // Copyright holders: os << "<P>This license is used by the following " << i->second.authors.size() << " copyright holders:</P>\n<BLOCKQUOTE><P>"; std::set<std::string>::const_iterator x, y; x = i->second.authors.begin(); y = i->second.authors.end(); while(x != y) { os << *x << "<BR>\n"; ++x; } os << "</P></BLOCKQUOTE>\n"; // Files using this license: os << "<P>This license applies to the following " << i->second.files.size() << " files:</P>\n<BLOCKQUOTE><P>"; std::set<fs::path, path_less>::const_iterator m, n; m = i->second.files.begin(); n = i->second.files.end(); while(m != n) { os << split_path(m_boost_path, *m) << "<br>\n"; ++m; } os << "</P></BLOCKQUOTE>\n"; } else { os << "<P>This license is used by " << i->second.authors.size() << " authors (list omitted for brevity).</P>\n"; os << "<P>This license applies to " << i->second.files.size() << " files (list omitted for brevity).</P>\n"; } ++license_index; ++i; } // // Output list of files not found to be under license control: // os << "<h2><a name=\"files\"></a>Files With No Recognisable Licence</h2>\n" "<P>The following " << m_unknown_licenses.size() << " files had no recognisable license information:</P><BLOCKQUOTE><P>\n"; std::set<fs::path, path_less>::const_iterator i2, j2; i2 = m_unknown_licenses.begin(); j2 = m_unknown_licenses.end(); while(i2 != j2) { os << split_path(m_boost_path, *i2) << "<br>\n"; ++i2; } os << "</p></BLOCKQUOTE>"; // // Output list of files with no found copyright holder: // os << "<h2><a name=\"authors\"></a>Files With No Recognisable Copyright Holder</h2>\n" "<P>The following " << m_unknown_authors.size() << " files had no recognisable copyright holder:</P>\n<BLOCKQUOTE><P>"; i2 = m_unknown_authors.begin(); j2 = m_unknown_authors.end(); while(i2 != j2) { os << split_path(m_boost_path, *i2) << "<br>\n"; ++i2; } os << "</p></BLOCKQUOTE>"; if(!m_bsl_summary_mode) { // // Output list of files that have been moved over to the Boost // Software License, along with enough information for human // verification. // os << "<h2><a name=\"bsl-converted\"></a>Files that can be automatically converted to the Boost Software License</h2>\n" << "<P>The following " << m_converted_to_bsl.size() << " files can be automatically converted to the Boost Software License, but require manual verification before they can be committed to CVS:</P>\n"; if (!m_converted_to_bsl.empty()) { typedef std::map<fs::path, std::pair<std::string, std::string>, path_less> ::const_iterator conv_iterator; conv_iterator i = m_converted_to_bsl.begin(), ie = m_converted_to_bsl.end(); int file_num = 1; while (i != ie) { os << "<P>[" << file_num << "] File: <tt>" << split_path(m_boost_path, i->first) << "</tt><br>\n<table border=\"1\">\n <tr>\n <td><pre>" << i->second.first << "</pre></td>\n <td><pre>" << i->second.second << "</pre></td>\n </tr>\n</table>\n"; ++i; ++file_num; } } // // Output list of files that could be moved over to the Boost Software License // os << "<h2><a name=\"to-bsl\"></a>Files that could be converted to the Boost Software License</h2>\n" "<P>The following " << m_can_migrate_to_bsl.size() << " files could be manually converted to the Boost Software License, but have not yet been:</P>\n<BLOCKQUOTE><P>"; i2 = m_can_migrate_to_bsl.begin(); j2 = m_can_migrate_to_bsl.end(); while(i2 != j2) { os << split_path(m_boost_path, *i2) << "<br>\n"; ++i2; } os << "</p></BLOCKQUOTE>"; // // Output list of files that can not be moved over to the Boost Software License // os << "<h2><a name=\"not-to-bsl\"></a>Files that can NOT be converted to the Boost Software License</h2>\n" "<P>The following " << m_cannot_migrate_to_bsl.size() << " files cannot be converted to the Boost Software License because we need the permission of more authors:</P>\n<BLOCKQUOTE><P>"; i2 = m_cannot_migrate_to_bsl.begin(); j2 = m_cannot_migrate_to_bsl.end(); while(i2 != j2) { os << split_path(m_boost_path, *i2) << "<br>\n"; ++i2; } os << "</p></BLOCKQUOTE>"; // // Output list of authors that we need permission for to move to the BSL // os << "<h2><a name=\"need-bsl-authors\"></a>Authors we need for the BSL</h2>\n" "<P>Permission of the following authors is needed before we can convert to the Boost Software License. The list of authors that have given their permission is contained in <code>more/blanket-permission.txt</code>.</P>\n<BLOCKQUOTE><P>"; std::copy(m_authors_for_bsl_migration.begin(), m_authors_for_bsl_migration.end(), std::ostream_iterator<std::string>(os, "<br>\n")); os << "</p></BLOCKQUOTE>"; // // output a table of copyright information: // os << "<H2><a name=\"copyright\"></a>Copyright Holder Information</H2><table border=\"1\">\n"; std::map<std::string, std::set<fs::path, path_less> >::const_iterator ad, ead; ad = m_author_data.begin(); ead = m_author_data.end(); while(ad != ead) { os << "<tr><td>" << ad->first << "</td><td>"; std::set<fs::path, path_less>::const_iterator fi, efi; fi = ad->second.begin(); efi = ad->second.end(); while(fi != efi) { os << split_path(m_boost_path, *fi) << " "; ++fi; } os << "</td></tr>\n"; ++ad; } os << "</table>\n"; } // // output file dependency information: // os << "<H2><a name=\"depend\"></a>File Dependency Information</H2><BLOCKQUOTE><pre>\n"; std::map<fs::path, fs::path, path_less>::const_iterator dep, last_dep; std::set<fs::path, path_less>::const_iterator fi, efi; fi = m_copy_paths.begin(); efi = m_copy_paths.end(); // if in summary mode, just figure out the "bad" files and print those only: std::set<fs::path, path_less> bad_paths; if(m_bsl_summary_mode) { bad_paths.insert(m_unknown_licenses.begin(), m_unknown_licenses.end()); bad_paths.insert(m_unknown_authors.begin(), m_unknown_authors.end()); bad_paths.insert(m_can_migrate_to_bsl.begin(), m_can_migrate_to_bsl.end()); bad_paths.insert(m_cannot_migrate_to_bsl.begin(), m_cannot_migrate_to_bsl.end()); typedef std::map<fs::path, std::pair<std::string, std::string>, path_less> ::const_iterator conv_iterator; conv_iterator i = m_converted_to_bsl.begin(), ie = m_converted_to_bsl.end(); while(i != ie) { bad_paths.insert(i->first); ++i; } fi = bad_paths.begin(); efi = bad_paths.end(); os << "<P>For brevity, only files not under the BSL are shown</P>\n"; } while(fi != efi) { os << split_path(m_boost_path, *fi); dep = m_dependencies.find(*fi); last_dep = m_dependencies.end(); std::set<fs::path, path_less> seen_deps; if (dep != last_dep) while(true) { os << " -> "; if(fs::exists(m_boost_path / dep->second)) os << split_path(m_boost_path, dep->second); else if(fs::exists(dep->second)) os << split_path(fs::path(), dep->second); else os << dep->second.string(); if(seen_deps.find(dep->second) != seen_deps.end()) { os << " <I>(Circular dependency!)</I>"; break; // circular dependency!!! } seen_deps.insert(dep->second); last_dep = dep; dep = m_dependencies.find(dep->second); if((dep == m_dependencies.end()) || (0 == compare_paths(dep->second, last_dep->second))) break; } os << "\n"; ++fi; } os << "</pre></BLOCKQUOTE>\n"; os << "</body></html>\n"; if(!os) { std::string msg("Error writing to "); msg += m_dest_path.string(); msg += "."; std::runtime_error e(msg); boost::throw_exception(e); } }
/** * Copy in @guest_path the canonicalization (see `man 3 realpath`) of * @user_path regarding to @tracee->root. The path to canonicalize * could be either absolute or relative to @guest_path. When the last * component of @user_path is a link, it is dereferenced only if * @deref_final is true -- it is useful for syscalls like lstat(2). * The parameter @recursion_level should be set to 0 unless you know * what you are doing. This function returns -errno if an error * occured, otherwise it returns 0. */ int canonicalize(Tracee *tracee, const char *user_path, bool deref_final, char guest_path[PATH_MAX], unsigned int recursion_level) { char scratch_path[PATH_MAX]; Finality finality; const char *cursor; int status; /* Avoid infinite loop on circular links. */ if (recursion_level > MAXSYMLINKS) return -ELOOP; /* Sanity checks. */ assert(user_path != NULL); assert(guest_path != NULL); assert(user_path != guest_path); if (strnlen(guest_path, PATH_MAX) >= PATH_MAX) return -ENAMETOOLONG; if (user_path[0] != '/') { /* Ensure 'guest_path' contains an absolute base of * the relative `user_path`. */ if (guest_path[0] != '/') return -EINVAL; } else strcpy(guest_path, "/"); /* Canonicalize recursely 'user_path' into 'guest_path'. */ cursor = user_path; finality = NOT_FINAL; while (!IS_FINAL(finality)) { Comparison comparison; char component[NAME_MAX]; char host_path[PATH_MAX]; finality = next_component(component, &cursor); status = (int) finality; if (status < 0) return status; if (strcmp(component, ".") == 0) { if (IS_FINAL(finality)) finality = FINAL_DOT; continue; } if (strcmp(component, "..") == 0) { pop_component(guest_path); if (IS_FINAL(finality)) finality = FINAL_SLASH; continue; } status = join_paths(2, scratch_path, guest_path, component); if (status < 0) return status; /* Resolve bindings and check that a non-final * component exists and either is a directory or is a * symlink. For this latter case, we check that the * symlink points to a directory once it is * canonicalized, at the end of this loop. */ status = substitute_binding_stat(tracee, finality, recursion_level, scratch_path, host_path); if (status < 0) return status; /* Nothing special to do if it's not a link or if we * explicitly ask to not dereference 'user_path', as * required by syscalls like lstat(2). Obviously, this * later condition does not apply to intermediate path * components. Errors are explicitly ignored since * they should be handled by the caller. */ if (status <= 0 || (finality == FINAL_NORMAL && !deref_final)) { strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, component); if (status < 0) return status; continue; } /* It's a link, so we have to dereference *and* * canonicalize to ensure we are not going outside the * new root. */ comparison = compare_paths("/proc", guest_path); switch (comparison) { case PATHS_ARE_EQUAL: case PATH1_IS_PREFIX: /* Some links in "/proc" are generated * dynamically by the kernel. PRoot has to * emulate some of them. */ status = readlink_proc(tracee, scratch_path, guest_path, component, comparison); switch (status) { case CANONICALIZE: /* The symlink is already dereferenced, * now canonicalize it. */ goto canon; case DONT_CANONICALIZE: /* If and only very final, this symlink * shouldn't be dereferenced nor canonicalized. */ if (finality == FINAL_NORMAL) { strcpy(guest_path, scratch_path); return 0; } break; default: if (status < 0) return status; } default: break; } status = readlink(host_path, scratch_path, sizeof(scratch_path)); if (status < 0) return status; else if (status == sizeof(scratch_path)) return -ENAMETOOLONG; scratch_path[status] = '\0'; /* Remove the leading "root" part if needed, it's * useful for "/proc/self/cwd/" for instance. */ status = detranslate_path(tracee, scratch_path, host_path); if (status < 0) return status; canon: /* Canonicalize recursively the referee in case it * is/contains a link, moreover if it is not an * absolute link then it is relative to * 'guest_path'. */ status = canonicalize(tracee, scratch_path, true, guest_path, recursion_level + 1); if (status < 0) return status; /* Check that a non-final canonicalized/dereferenced * symlink exists and is a directory. */ status = substitute_binding_stat(tracee, finality, recursion_level, guest_path, host_path); if (status < 0) return status; /* Here, 'guest_path' shouldn't be a symlink anymore, * unless it is a named file descriptor. */ assert(status != 1 || sscanf(guest_path, "/proc/%*d/fd/%d", &status) == 1); } /* At the exit stage of the first level of recursion, * `guest_path` is fully canonicalized but a terminating '/' * or a terminating '.' may be required to keep the initial * semantic of `user_path`. */ if (recursion_level == 0) { switch (finality) { case FINAL_NORMAL: break; case FINAL_SLASH: strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, ""); if (status < 0) return status; break; case FINAL_DOT: strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, "."); if (status < 0) return status; break; default: assert(0); } } return 0; }
/** * Translate the output arguments of the current @tracee's syscall in * the @tracee->pid process area. This function sets the result of * this syscall to @tracee->status if an error occured previously * during the translation, that is, if @tracee->status is less than 0. */ void translate_syscall_exit(Tracee *tracee) { word_t syscall_number; word_t syscall_result; int status; status = notify_extensions(tracee, SYSCALL_EXIT_START, 0, 0); if (status < 0) { poke_reg(tracee, SYSARG_RESULT, (word_t) status); goto end; } if (status > 0) return; /* Set the tracee's errno if an error occured previously during * the translation. */ if (tracee->status < 0) { poke_reg(tracee, SYSARG_RESULT, (word_t) tracee->status); goto end; } /* Translate output arguments: * - break: update the syscall result register with "status" * - goto end: nothing else to do. */ syscall_number = get_sysnum(tracee, ORIGINAL); syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT); switch (syscall_number) { case PR_brk: translate_brk_exit(tracee); goto end; case PR_getcwd: { char path[PATH_MAX]; size_t new_size; size_t size; word_t output; size = (size_t) peek_reg(tracee, ORIGINAL, SYSARG_2); if (size == 0) { status = -EINVAL; break; } /* Ensure cwd still exists. */ status = translate_path(tracee, path, AT_FDCWD, ".", false); if (status < 0) break; new_size = strlen(tracee->fs->cwd) + 1; if (size < new_size) { status = -ERANGE; break; } /* Overwrite the path. */ output = peek_reg(tracee, ORIGINAL, SYSARG_1); status = write_data(tracee, output, tracee->fs->cwd, new_size); if (status < 0) break; /* The value of "status" is used to update the returned value * in translate_syscall_exit(). */ status = new_size; break; } case PR_accept: case PR_accept4: /* Nothing special to do if no sockaddr was specified. */ if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) goto end; /* Fall through. */ case PR_getsockname: case PR_getpeername: { word_t sock_addr; word_t size_addr; word_t max_size; /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; sock_addr = peek_reg(tracee, ORIGINAL, SYSARG_2); size_addr = peek_reg(tracee, MODIFIED, SYSARG_3); max_size = peek_reg(tracee, MODIFIED, SYSARG_6); status = translate_socketcall_exit(tracee, sock_addr, size_addr, max_size); if (status < 0) break; /* Don't overwrite the syscall result. */ goto end; } #define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) #define POKE_WORD(addr, value) \ poke_word(tracee, addr, value); \ if (errno != 0) { \ status = -errno; \ break; \ } #define PEEK_WORD(addr) \ peek_word(tracee, addr); \ if (errno != 0) { \ status = -errno; \ break; \ } case PR_socketcall: { word_t args_addr; word_t sock_addr; word_t size_addr; word_t max_size; args_addr = peek_reg(tracee, ORIGINAL, SYSARG_2); switch (peek_reg(tracee, ORIGINAL, SYSARG_1)) { case SYS_ACCEPT: case SYS_ACCEPT4: /* Nothing special to do if no sockaddr was specified. */ sock_addr = PEEK_WORD(SYSARG_ADDR(2)); if (sock_addr == 0) goto end; /* Fall through. */ case SYS_GETSOCKNAME: case SYS_GETPEERNAME: /* Handle these cases below. */ status = 1; break; case SYS_BIND: case SYS_CONNECT: /* Restore the initial parameters: this memory was * overwritten at the enter stage. Remember: POKE_WORD * puts -errno in status and breaks if an error * occured. */ POKE_WORD(SYSARG_ADDR(2), peek_reg(tracee, MODIFIED, SYSARG_5)); POKE_WORD(SYSARG_ADDR(3), peek_reg(tracee, MODIFIED, SYSARG_6)); status = 0; break; default: status = 0; break; } /* Error reported by the kernel or there's nothing else to do. */ if ((int) syscall_result < 0 || status == 0) goto end; /* An error occured in SYS_BIND or SYS_CONNECT. */ if (status < 0) break; /* Remember: PEEK_WORD puts -errno in status and breaks if an * error occured. */ sock_addr = PEEK_WORD(SYSARG_ADDR(2)); size_addr = PEEK_WORD(SYSARG_ADDR(3)); max_size = peek_reg(tracee, MODIFIED, SYSARG_6); status = translate_socketcall_exit(tracee, sock_addr, size_addr, max_size); if (status < 0) break; /* Don't overwrite the syscall result. */ goto end; } #undef SYSARG_ADDR #undef PEEK_WORD #undef POKE_WORD case PR_fchdir: case PR_chdir: /* These syscalls are fully emulated, see enter.c for details * (like errors). */ status = 0; break; case PR_rename: case PR_renameat: { char old_path[PATH_MAX]; char new_path[PATH_MAX]; ssize_t old_length; ssize_t new_length; Comparison comparison; Reg old_reg; Reg new_reg; char *tmp; /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; if (syscall_number == PR_rename) { old_reg = SYSARG_1; new_reg = SYSARG_2; } else { old_reg = SYSARG_2; new_reg = SYSARG_4; } /* Get the old path, then convert it to the same * "point-of-view" as tracee->fs->cwd (guest). */ status = read_path(tracee, old_path, peek_reg(tracee, MODIFIED, old_reg)); if (status < 0) break; status = detranslate_path(tracee, old_path, NULL); if (status < 0) break; old_length = (status > 0 ? status - 1 : (ssize_t) strlen(old_path)); /* Nothing special to do if the moved path is not the * current working directory. */ comparison = compare_paths(old_path, tracee->fs->cwd); if (comparison != PATH1_IS_PREFIX && comparison != PATHS_ARE_EQUAL) { status = 0; break; } /* Get the new path, then convert it to the same * "point-of-view" as tracee->fs->cwd (guest). */ status = read_path(tracee, new_path, peek_reg(tracee, MODIFIED, new_reg)); if (status < 0) break; status = detranslate_path(tracee, new_path, NULL); if (status < 0) break; new_length = (status > 0 ? status - 1 : (ssize_t) strlen(new_path)); /* Sanity check. */ if (strlen(tracee->fs->cwd) >= PATH_MAX) { status = 0; break; } strcpy(old_path, tracee->fs->cwd); /* Update the virtual current working directory. */ substitute_path_prefix(old_path, old_length, new_path, new_length); tmp = talloc_strdup(tracee->fs, old_path); if (tmp == NULL) { status = -ENOMEM; break; } TALLOC_FREE(tracee->fs->cwd); tracee->fs->cwd = tmp; status = 0; break; } case PR_readlink: case PR_readlinkat: { char referee[PATH_MAX]; char referer[PATH_MAX]; size_t old_size; size_t new_size; size_t max_size; word_t input; word_t output; /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; old_size = syscall_result; if (syscall_number == PR_readlink) { output = peek_reg(tracee, ORIGINAL, SYSARG_2); max_size = peek_reg(tracee, ORIGINAL, SYSARG_3); input = peek_reg(tracee, MODIFIED, SYSARG_1); } else { output = peek_reg(tracee, ORIGINAL, SYSARG_3); max_size = peek_reg(tracee, ORIGINAL, SYSARG_4); input = peek_reg(tracee, MODIFIED, SYSARG_2); } if (max_size > PATH_MAX) max_size = PATH_MAX; if (max_size == 0) { status = -EINVAL; break; } /* The kernel does NOT put the NULL terminating byte for * readlink(2). */ status = read_data(tracee, referee, output, old_size); if (status < 0) break; referee[old_size] = '\0'; /* Not optimal but safe (path is fully translated). */ status = read_path(tracee, referer, input); if (status < 0) break; if (status >= PATH_MAX) { status = -ENAMETOOLONG; break; } status = detranslate_path(tracee, referee, referer); if (status < 0) break; /* The original path doesn't require any transformation, i.e * it is a symetric binding. */ if (status == 0) goto end; /* Overwrite the path. Note: the output buffer might be * initialized with zeros but it was updated with the kernel * result, and then with the detranslated result. This later * might be shorter than the former, so it's safier to add a * NULL terminating byte when possible. This problem was * exposed by IDA Demo 6.3. */ if ((size_t) status < max_size) { new_size = status - 1; status = write_data(tracee, output, referee, status); } else { new_size = max_size; status = write_data(tracee, output, referee, max_size); } if (status < 0) break; /* The value of "status" is used to update the returned value * in translate_syscall_exit(). */ status = new_size; break; } #if defined(ARCH_X86_64) case PR_uname: { struct utsname utsname; word_t address; size_t size; if (get_abi(tracee) != ABI_2) goto end; /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; address = peek_reg(tracee, ORIGINAL, SYSARG_1); status = read_data(tracee, &utsname, address, sizeof(utsname)); if (status < 0) break; /* Some 32-bit programs like package managers can be * confused when the kernel reports "x86_64". */ size = sizeof(utsname.machine); strncpy(utsname.machine, "i686", size); utsname.machine[size - 1] = '\0'; status = write_data(tracee, address, &utsname, sizeof(utsname)); if (status < 0) break; status = 0; break; } #endif case PR_execve: translate_execve_exit(tracee); goto end; case PR_ptrace: status = translate_ptrace_exit(tracee); break; case PR_wait4: case PR_waitpid: if (tracee->as_ptracer.waits_in != WAITS_IN_PROOT) goto end; status = translate_wait_exit(tracee); break; case PR_setrlimit: case PR_prlimit64: /* Error reported by the kernel. */ if ((int) syscall_result < 0) goto end; status = translate_setrlimit_exit(tracee, syscall_number == PR_prlimit64); if (status < 0) break; /* Don't overwrite the syscall result. */ goto end; default: goto end; } poke_reg(tracee, SYSARG_RESULT, (word_t) status); end: status = notify_extensions(tracee, SYSCALL_EXIT_END, status, 0); if (status < 0) poke_reg(tracee, SYSARG_RESULT, (word_t) status); }
bool operator()(const fs::path& a, const fs::path& b)const { return compare_paths(a, b) < 0; }
/** * Remove/substitute the leading part of a "translated" @path. It * returns 0 if no transformation is required (ie. symmetric binding), * otherwise it returns the size in bytes of the updated @path, * including the end-of-string terminator. On error it returns * -errno. */ int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]) { size_t prefix_length; ssize_t new_length; bool sanity_check; bool follow_binding; /* Sanity check. */ if (strnlen(path, PATH_MAX) >= PATH_MAX) return -ENAMETOOLONG; /* Don't try to detranslate relative paths (typically the * target of a relative symbolic link). */ if (path[0] != '/') return 0; /* Is it a symlink? */ if (t_referrer != NULL) { Comparison comparison; sanity_check = false; follow_binding = false; /* In some cases bindings have to be resolved. */ comparison = compare_paths("/proc", t_referrer); if (comparison == PATH1_IS_PREFIX) { /* Some links in "/proc" are generated * dynamically by the kernel. PRoot has to * emulate some of them. */ char proc_path[PATH_MAX]; strcpy(proc_path, path); new_length = readlink_proc2(tracee, proc_path, t_referrer); if (new_length < 0) return new_length; if (new_length != 0) { strcpy(path, proc_path); return new_length + 1; } /* Always resolve bindings for symlinks in * "/proc", they always point to the emulated * file-system namespace by design. */ follow_binding = true; } else if (!belongs_to_guestfs(tracee, t_referrer)) { const char *binding_referree; const char *binding_referrer; binding_referree = get_path_binding(tracee, HOST, path); binding_referrer = get_path_binding(tracee, HOST, t_referrer); assert(binding_referrer != NULL); /* Resolve bindings for symlinks that belong * to a binding and point to the same binding. * For example, if "-b /lib:/foo" is specified * and the symlink "/lib/a -> /lib/b" exists * in the host rootfs namespace, then it * should appear as "/foo/a -> /foo/b" in the * guest rootfs namespace for consistency * reasons. */ if (binding_referree != NULL) { comparison = compare_paths(binding_referree, binding_referrer); follow_binding = (comparison == PATHS_ARE_EQUAL); } } } else { sanity_check = true; follow_binding = true; } if (follow_binding) { switch (substitute_binding(tracee, HOST, path)) { case 0: return 0; case 1: return strlen(path) + 1; default: break; } } switch (compare_paths(get_root(tracee), path)) { case PATH1_IS_PREFIX: /* Remove the leading part, that is, the "root". */ prefix_length = strlen(get_root(tracee)); /* Special case when path to the guest rootfs == "/". */ if (prefix_length == 1) prefix_length = 0; new_length = strlen(path) - prefix_length; memmove(path, path + prefix_length, new_length); path[new_length] = '\0'; break; case PATHS_ARE_EQUAL: /* Special case when path == root. */ new_length = 1; strcpy(path, "/"); break; default: /* Ensure the path is within the new root. */ if (sanity_check) return -EPERM; else return 0; } return new_length + 1; }
inline bool equal_paths(const fs::path& a, const fs::path& b) { return compare_paths(a, b) == 0; }
static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent) { struct diff_queue_struct *q = &diff_queued_diff; struct combine_diff_path *p, **tail = &curr; int i, cmp; if (!n) { for (i = 0; i < q->nr; i++) { int len; const char *path; if (diff_unmodified_pair(q->queue[i])) continue; path = q->queue[i]->two->path; len = strlen(path); p = xmalloc(combine_diff_path_size(num_parent, len)); p->path = (char *) &(p->parent[num_parent]); memcpy(p->path, path, len); p->path[len] = 0; p->next = NULL; memset(p->parent, 0, sizeof(p->parent[0]) * num_parent); hashcpy(p->oid.hash, q->queue[i]->two->sha1); p->mode = q->queue[i]->two->mode; hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1); p->parent[n].mode = q->queue[i]->one->mode; p->parent[n].status = q->queue[i]->status; *tail = p; tail = &p->next; } return curr; } /* * paths in curr (linked list) and q->queue[] (array) are * both sorted in the tree order. */ i = 0; while ((p = *tail) != NULL) { cmp = ((i >= q->nr) ? -1 : compare_paths(p, q->queue[i]->two)); if (cmp < 0) { /* p->path not in q->queue[]; drop it */ *tail = p->next; free(p); continue; } if (cmp > 0) { /* q->queue[i] not in p->path; skip it */ i++; continue; } hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1); p->parent[n].mode = q->queue[i]->one->mode; p->parent[n].status = q->queue[i]->status; tail = &p->next; i++; } return curr; }
/** * This function emulates the @result of readlink("@base/@component") * with respect to @tracee, where @base belongs to "/proc" (according * to @comparison). This function returns -errno on error, an enum * @action otherwise (c.f. above). * * Unlike readlink(), this function includes the nul terminating byte * to @result. */ Action readlink_proc(const Tracee *tracee, char result[PATH_MAX], const char base[PATH_MAX], const char component[NAME_MAX], Comparison comparison) { const Tracee *known_tracee; char proc_path[64]; /* 64 > sizeof("/proc//fd/") + 2 * sizeof(#ULONG_MAX) */ int status; pid_t pid; assert(comparison == compare_paths("/proc", base)); /* Remember: comparison = compare_paths("/proc", base) */ switch (comparison) { case PATHS_ARE_EQUAL: /* Substitute "/proc/self" with "/proc/<PID>". */ if (strcmp(component, "self") != 0) return DEFAULT; status = snprintf(result, PATH_MAX, "/proc/%d", tracee->pid); if (status < 0 || status >= PATH_MAX) return -EPERM; return CANONICALIZE; case PATH1_IS_PREFIX: /* Handle "/proc/<PID>" below, where <PID> is process * monitored by PRoot. */ break; default: return DEFAULT; } pid = atoi(base + strlen("/proc/")); if (pid == 0) return DEFAULT; known_tracee = get_tracee(pid, false); if (!known_tracee) return DEFAULT; /* Handle links in "/proc/<PID>/". */ status = snprintf(proc_path, sizeof(proc_path), "/proc/%d", pid); if (status < 0 || status >= sizeof(proc_path)) return -EPERM; comparison = compare_paths(proc_path, base); switch (comparison) { case PATHS_ARE_EQUAL: #define SUBSTITUTE(name, field) \ do { \ if (strcmp(component, #name) != 0) \ break; \ \ status = strlen(known_tracee->field); \ if (status >= PATH_MAX) \ return -EPERM; \ \ strncpy(result, known_tracee->field, status + 1); \ return CANONICALIZE; \ } while (0) /* Substitute link "/proc/<PID>/???" with the content * of tracee->???. */ SUBSTITUTE(exe, exe); SUBSTITUTE(cwd, fs->cwd); //SUBSTITUTE(root); #undef SUBSTITUTE return DEFAULT; case PATH1_IS_PREFIX: /* Handle "/proc/<PID>/???" below. */ break; default: return DEFAULT; } /* Handle links in "/proc/<PID>/fd/". */ status = snprintf(proc_path, sizeof(proc_path), "/proc/%d/fd", pid); if (status < 0 || status >= sizeof(proc_path)) return -EPERM; comparison = compare_paths(proc_path, base); switch (comparison) { char *end_ptr; case PATHS_ARE_EQUAL: /* Sanity check: a number is expected. */ errno = 0; (void) strtol(component, &end_ptr, 10); if (errno != 0 || end_ptr == component) return -EPERM; /* Don't dereference "/proc/<PID>/fd/???" now: they * can point to anonymous pipe, socket, ... otherwise * they point to a path already canonicalized by the * kernel. * * Note they are still correctly detranslated in * syscall/exit.c if a monitored process uses * readlink() against any of them. */ status = snprintf(result, PATH_MAX, "%s/%s", base, component); if (status < 0 || status >= PATH_MAX) return -EPERM; return DONT_CANONICALIZE; default: return DEFAULT; } return DEFAULT; }
/** * Force current @tracee's syscall to behave as if executed by "root". * This function returns -errno if an error occured, otherwise 0. */ static int handle_sysexit_end(Tracee *tracee) { word_t sysnum; sysnum = get_sysnum(tracee, ORIGINAL); switch (sysnum) { case PR_chroot: { char path[PATH_MAX]; word_t result; word_t input; int status; /* Override only permission errors. */ result = peek_reg(tracee, CURRENT, SYSARG_RESULT); if ((int) result != -EPERM) return 0; input = peek_reg(tracee, MODIFIED, SYSARG_1); status = read_path(tracee, path, input); if (status < 0) return status; /* Only "new rootfs == current rootfs" is supported yet. */ status = compare_paths(get_root(tracee), path); if (status != PATHS_ARE_EQUAL) return 0; /* Force success. */ poke_reg(tracee, SYSARG_RESULT, 0); return 0; } case PR_setresuid: case PR_setresgid: case PR_setresuid32: case PR_setresgid32: case PR_mknod: case PR_capset: case PR_setxattr: case PR_chmod: case PR_chown: case PR_fchmod: case PR_fchown: case PR_lchown: case PR_chown32: case PR_fchown32: case PR_lchown32: case PR_fchmodat: case PR_fchownat: { word_t result; /* Override only permission errors. */ result = peek_reg(tracee, CURRENT, SYSARG_RESULT); if ((int) result != -EPERM) return 0; /* Force success. */ poke_reg(tracee, SYSARG_RESULT, 0); return 0; } case PR_getresuid: case PR_getresuid32: case PR_getresgid: case PR_getresgid32: poke_mem(tracee, peek_reg(tracee, ORIGINAL, SYSARG_1), 0); if (errno != 0) return -EFAULT; poke_mem(tracee, peek_reg(tracee, ORIGINAL, SYSARG_2), 0); if (errno != 0) return -EFAULT; poke_mem(tracee, peek_reg(tracee, ORIGINAL, SYSARG_3), 0); if (errno != 0) return -EFAULT; /* Force success. */ poke_reg(tracee, SYSARG_RESULT, 0); return 0; case PR_fstatat64: case PR_newfstatat: case PR_stat64: case PR_lstat64: case PR_fstat64: case PR_stat: case PR_lstat: case PR_fstat: { word_t result; word_t address; word_t uid, gid; Reg sysarg; /* Override only if it succeed. */ result = peek_reg(tracee, CURRENT, SYSARG_RESULT); if (result != 0) return 0; /* Get the address of the 'stat' structure. */ if (sysnum == PR_fstatat64 || sysnum == PR_newfstatat) sysarg = SYSARG_3; else sysarg = SYSARG_2; address = peek_reg(tracee, ORIGINAL, sysarg); /* Get the uid & gid values from the 'stat' structure. */ uid = peek_mem(tracee, address + offsetof_stat_uid(tracee)); if (errno != 0) uid = 0; /* Not fatal. */ gid = peek_mem(tracee, address + offsetof_stat_gid(tracee)); if (errno != 0) gid = 0; /* Not fatal. */ /* These values are 32-bit width, even on 64-bit architecture. */ uid &= 0xFFFFFFFF; gid &= 0xFFFFFFFF; /* Override only if the file is owned by the current user. * Errors are not fatal here. */ if (uid == getuid()) poke_mem(tracee, address + offsetof_stat_uid(tracee), 0); if (gid == getgid()) poke_mem(tracee, address + offsetof_stat_gid(tracee), 0); return 0; } case PR_getuid: case PR_getgid: case PR_getegid: case PR_geteuid: case PR_getuid32: case PR_getgid32: case PR_geteuid32: case PR_getegid32: case PR_setuid: case PR_setgid: case PR_setfsuid: case PR_setfsgid: case PR_setuid32: case PR_setgid32: case PR_setfsuid32: case PR_setfsgid32: /* Force success. */ poke_reg(tracee, SYSARG_RESULT, 0); return 0; default: return 0; } }