/* pfs.c: * **************************************************************** * Copyright (C) 2003 Tom Lord * * See the file "COPYING" for further information about * the copyright and warranty status of this work. */ #include "hackerlab/bugs/panic.h" #include "hackerlab/bugs/exception.h" #include "hackerlab/char/str.h" #include "hackerlab/fs/file-names.h" #include "hackerlab/fs/cwd.h" #include "hackerlab/vu/safe.h" #include "hackerlab/arrays/ar.h" #include "libfsutils/tmp-files.h" #include "libarch/archive.h" #include "libarch/archives.h" #include "libarch/pfs-dav.h" #include "libarch/pfs-sftp.h" #include "libarch/pfs-ftp.h" #include "libarch/pfs-fs.h" #include "libarch/pfs.h" #include "libarch/cached-archive.h" /* __STDC__ prototypes for static functions */ static int dot_listings_equal (rel_table a, rel_table b); void arch_pfs_rmrf_file (struct arch_pfs_session * pfs, t_uchar * path) { int dir_check; dir_check = arch_pfs_is_dir (pfs, path); if (!dir_check) { arch_pfs_rm (pfs, path, 1); } else if (dir_check > 0) { rel_table contents = 0; int x; contents = arch_pfs_directory_files (pfs, path, 1); for (x = 0; x < rel_n_records (contents); ++x) if (str_cmp (".", contents[x][0]) && str_cmp ("..", contents[x][0])) { t_uchar * subpath = 0; subpath = file_name_in_vicinity (0, path, contents[x][0]); arch_pfs_rmrf_file (pfs, subpath); lim_free (0, subpath); } arch_pfs_rmdir (pfs, path, 1); rel_free_table (contents); } /* dir_check < 0 indicates an error, presumably ENOENT */ } int arch_pfs_set_file_contents (struct arch_pfs_session *pfs, t_uchar const *path, t_uchar const *contents, int soft_errors) { t_uchar * tmp_path = tmp_file_name_in_tmp (",,pfs-sftp-file-contents"); int fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0000); int err; safe_unlink (tmp_path); safe_printfmt (fd, "%s\n", contents); safe_lseek (fd, (off_t)0, SEEK_SET); err = pfs->vtable->put_file (pfs, (t_uchar *)path, 0666, fd, 1); safe_close (fd); lim_free (0, tmp_path); return err; } int arch_pfs_set_file_contents_in_dir (struct arch_pfs_session *pfs, t_uchar const *dir, t_uchar const *relpath, t_uchar const *contents, int soft_errors) { t_uchar * tmp_path = file_name_in_vicinity (0, dir, relpath); int result = arch_pfs_set_file_contents (pfs, tmp_path, contents, soft_errors); lim_free (0, tmp_path); return result; } void arch_pfs_pfs_make_archive (t_uchar * name, t_uchar * uri, t_uchar * version, t_uchar *mirror_of, int dot_listing_lossage, int signed_archive) { struct arch_pfs_session *pfs; t_uchar * meta_info_path = 0; t_uchar * tmp_path; t_uchar * archdir; t_uchar * hosturi; /* TODO: handle URIs like 'sftp://amit@cvs:~f' semi-reasonably * The following code can mangle URIs, since it doesn't ignore the protocol * part. IMHO, it doesn't belong in a general function like this * one. - aaron.bentley@utoronto.ca */ tmp_path = file_name_from_directory (0, uri); /* remove any trailing '/' */ archdir = file_name_tail (0, tmp_path); hosturi = file_name_directory (0, tmp_path); lim_free (0, tmp_path); pfs = arch_pfs_connect (hosturi, 0); pfs->vtable->mkdir (pfs, archdir, 0777, 0); meta_info_path = file_name_in_vicinity (0, archdir, "=meta-info"); pfs->vtable->mkdir (pfs, meta_info_path, 0777, 0); if (mirror_of) { arch_pfs_set_file_contents_in_dir (pfs, meta_info_path, "name", mirror_of, 0); arch_pfs_set_file_contents_in_dir (pfs, meta_info_path, "mirror", mirror_of, 0); } else arch_pfs_set_file_contents_in_dir (pfs, meta_info_path, "name", name, 0); if (dot_listing_lossage) arch_pfs_set_file_contents_in_dir (pfs, meta_info_path, "http-blows", "it sure does", 0); if (signed_archive) arch_pfs_set_file_contents_in_dir (pfs, meta_info_path, "signed-archive", "system cracking is (nearly always) lame", 0); arch_pfs_set_file_contents_in_dir (pfs, archdir, ".archive-version", version, 0); if (dot_listing_lossage) { arch_pfs_update_listing_file (pfs, meta_info_path); arch_pfs_update_root_listing_file (pfs, archdir); } lim_free (0, meta_info_path); lim_free (0, hosturi); lim_free (0, archdir); } /* TODO: layer violation here - sftp urls are not the only * ones to probe - RBC 20050130 */ extern int arch_valid_uri (t_uchar * uri) { int answer = 1; if (arch_pfs_sftp_supported_protocol (uri)) { t_uchar * user = 0, * hostname = 0, * port = 0; char * path = 0; answer = (arch_pfs_sftp_parse_uri (&user, &hostname, &port, &path, uri) == 0); lim_free (0, user); lim_free (0, hostname); lim_free (0, port); lim_free (0, path); } return answer; } struct arch_pfs_session * arch_pfs_connect (t_uchar * uri, int soft_errors) { struct arch_pfs_session * answer; if (arch_pfs_dav_supported_protocol (uri)) { answer = arch_pfs_dav_connect (uri, soft_errors); } else if (arch_pfs_sftp_supported_protocol (uri)) { return arch_pfs_sftp_connect (uri, soft_errors); } else if (arch_pfs_ftp_supported_protocol (uri)) { return arch_pfs_ftp_connect (uri, soft_errors); } else { return arch_pfs_fs_connect (uri, soft_errors); } if (!answer && !soft_errors) panic ("Failed to connect to archive with soft_errors off"); return answer; } void arch_pfs_disconnect (struct arch_pfs_session **session) { if (*session) (*session)->vtable->disconnect (session); *session = NULL; } int arch_pfs_is_local(t_uchar const * uri) { t_uchar * colon = str_chr_index (uri, ':'); t_uchar * slash = str_chr_index (uri, '/'); return (colon == 0 || (slash != 0 && slash < colon ) ); } /* TODO: move this to hackerlab or fsutils */ static t_uchar * path_normalise (t_uchar const * in_path) { /* FIXME, this is a random hack for now. * expected semantics are - /./'s are removed * foo/../ are removed */ t_uchar * result = NULL; rel_table sections = rel_delim_split (in_path, "/"); rel_table answer_sections = 0; int index = 0; for (index = 0; index < rel_n_records(sections); ++index) { int answer_len; if (!str_cmp(sections[index][0], ".")) continue; else if (!str_cmp(sections[index][0], "..") && (answer_len = rel_n_records(answer_sections)) && str_cmp (answer_sections[answer_len - 1][0], "..")) { rel_remove_records (&answer_sections, answer_len - 1, answer_len - 1); } else rel_add_records (&answer_sections, rel_copy_record (sections[index]), 0); } result = str_save (0, ""); for (index = 0; index < rel_n_records(answer_sections); ++index) { result = str_realloc_cat_many (0, result, "/", answer_sections[index][0], str_end); } rel_free_table(sections); rel_free_table(answer_sections); return result; } t_uchar * arch_pfs_abs_path (t_uchar * path) { if (!arch_pfs_is_local (path)) return str_save (0, path); else return arch_abs_path (path); } /** * \brief Determines the absolute filesystem path for a file * \param path The path to absolutize * \return the absolute path */ t_uchar * arch_abs_path (t_uchar const * const path) { t_uchar * cwd; t_uchar * temp; t_uchar * result; if (!str_length (path)) return safe_current_working_directory (); if (path[0]=='/') return path_normalise (path); cwd = safe_current_working_directory (); temp = str_realloc_cat (0, file_name_as_directory(0, cwd), path); result = path_normalise(temp); lim_free (0, cwd); lim_free (0, temp); return result; } t_uchar * arch_pfs_file_contents (struct arch_pfs_session * pfs, t_uchar * path, int soft_errors) { return pfs->vtable->file_contents (pfs, path, soft_errors); } rel_table arch_pfs_directory_files (struct arch_pfs_session * pfs, t_uchar * path, int soft_errors) { return pfs->vtable->directory_files (pfs, path, soft_errors); } int arch_pfs_file_exists (struct arch_pfs_session * pfs, t_uchar * path) { return pfs->vtable->file_exists (pfs, path); } int arch_pfs_get_file (struct arch_pfs_session * pfs, int out_fd, t_uchar * path, int soft_errors) { return pfs->vtable->get_file (pfs, out_fd, path, soft_errors); } int arch_pfs_put_file (struct arch_pfs_session * pfs, t_uchar * path, mode_t perms, int in_fd, int soft_errors) { return pfs->vtable->put_file (pfs, path, perms, in_fd, soft_errors); } int arch_pfs_put_atomic (struct arch_pfs_session * pfs, t_uchar ** errstr, t_uchar * path, mode_t perms, int in_fd, int replace, int soft_errors) { int result = 0; t_uchar * temp_dir = file_name_directory_file (0, path); t_uchar * tmp_file = talloc_tmp_file_name (talloc_context, temp_dir, ",,upload-tmp"); arch_pfs_rmrf_file (pfs, tmp_file); if (!arch_pfs_put_file (pfs, tmp_file, perms, in_fd, soft_errors)) { if (replace) arch_pfs_rmrf_file (pfs, path); if (arch_pfs_rename (pfs, errstr, tmp_file, path, soft_errors)) result = -1; } else result = -1; lim_free (0, temp_dir); talloc_free (tmp_file); return result; } int arch_pfs_mkdir (struct arch_pfs_session * pfs, t_uchar * path, mode_t perms, int soft_errors) { return pfs->vtable->mkdir (pfs, path, perms, soft_errors); } int arch_pfs_rename (struct arch_pfs_session * pfs, t_uchar ** errstr, t_uchar * from, t_uchar * to, int soft_errors) { return pfs->vtable->rename (pfs, errstr, from, to, soft_errors); } int arch_pfs_is_dir (struct arch_pfs_session * pfs, t_uchar * path) { return pfs->vtable->is_dir (pfs, path); } int arch_pfs_rmdir (struct arch_pfs_session * pfs, t_uchar * path, int soft_errors) { return pfs->vtable->rmdir (pfs, path, soft_errors); } int arch_pfs_rm (struct arch_pfs_session * pfs, t_uchar * path, int soft_errors) { return pfs->vtable->rm (pfs, path, soft_errors); } void arch_pfs_update_listing_file (struct arch_pfs_session * session, t_uchar * dir) { arch_pfs_update_listing_file_full (session, dir, 0); } void arch_pfs_update_root_listing_file (struct arch_pfs_session * session, t_uchar *dir) { rel_table archive_version_list = 0; rel_add_records (&archive_version_list, rel_make_record (".archive-version", 0), 0); arch_pfs_update_listing_file_full (session, dir, archive_version_list); rel_free_table (archive_version_list); } void arch_pfs_update_listing_file_full (struct arch_pfs_session * session, t_uchar * dir, rel_table additional_files) { t_uchar * tmp_path = 0; int tmp_fd = 0; t_uchar * dot_listing_path = 0; t_uchar * dot_listing_tmp = 0; rel_table files_before = 0; rel_table files_after = 0; tmp_path = tmp_file_name_in_tmp (",,pfs-dot-listing"); tmp_fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0000); safe_unlink (tmp_path); dot_listing_path = file_name_in_vicinity (0, dir, ".listing"); dot_listing_tmp = archive_tmp_file_name (dir, ",,dot-listing"); while (1) { int x; files_before = arch_pfs_directory_files (session, dir, 0); if (files_before) rel_sort_table_by_field (0, files_before, 0); /* TODO: remove files in files_before that are present in additional_files */ for (x = 0; x < rel_n_records (files_before); ++x) { if (('.' != files_before[x][0][0]) && (',' != files_before[x][0][0])) { safe_printfmt (tmp_fd, "%s\r\n", files_before[x][0]); } } if (additional_files) for (x = 0; x < rel_n_records (additional_files); ++x) { safe_printfmt (tmp_fd, "%s\r\n", additional_files[x][0]); } safe_lseek (tmp_fd, (off_t)0, SEEK_SET); arch_pfs_put_atomic (session, 0, dot_listing_path, 0444, tmp_fd, 1, 0); files_after = arch_pfs_directory_files (session, dir, 0); if (files_after) rel_sort_table_by_field (0, files_after, 0); if (!dot_listings_equal (files_before, files_after)) { safe_ftruncate (tmp_fd, (long)0); rel_free_table (files_before); rel_free_table (files_after); files_before = 0; files_after = 0; } else { break; } } safe_close (tmp_fd); lim_free (0, dot_listing_path); lim_free (0, dot_listing_tmp); rel_free_table (files_before); rel_free_table (files_after); lim_free (0, tmp_path); } static int dot_listings_equal (rel_table a, rel_table b) { int ax; int bx; if ((!a && b) || (a && !b)) return 0; ax = 0; bx = 0; while ((ax < rel_n_records (a)) || (bx < rel_n_records (b))) { if ((ax < rel_n_records (a)) && (a[ax][0][0] == '.')) { ++ax; continue; } if ((ax < rel_n_records (a)) && (a[ax][0][0] == ',')) { ++ax; continue; } if ((bx < rel_n_records (b)) && (b[bx][0][0] == '.')) { ++bx; continue; } if ((bx < rel_n_records (b)) && (b[bx][0][0] == ',')) { ++bx; continue; } if ((ax == rel_n_records (a)) || (bx == rel_n_records (b))) return 0; if (str_cmp (a[ax][0], b[bx][0])) return 0; ++ax; ++bx; } if ((ax != rel_n_records (a)) || (bx != rel_n_records (b))) return 0; return 1; } /** * \brief adjust a parsed uri for user-data-entry heuristics * * \param uri the uri to apply heuristics too */ void arch_uri_heuristics (ne_uri *parsed_uri) { char *at_pos = str_chr_index (parsed_uri->host, '@'); if (!at_pos) return; parsed_uri->userinfo = str_replace (parsed_uri->userinfo, str_alloc_cat (0, parsed_uri->userinfo, "@")); parsed_uri->userinfo = str_replace (parsed_uri->userinfo, str_alloc_cat_n (0, parsed_uri->userinfo, parsed_uri->host, at_pos - parsed_uri->host)); parsed_uri->host = str_replace (parsed_uri->host, str_save (0, at_pos + 1)); } /* RFC2396 spake: * "Data must be escaped if it does not have a representation * using an unreserved character". */ /* Lookup table: character classes from 2396. (This is overkill) */ #define SP 0 /* space = */ #define CO 0 /* control = */ #define DE 0 /* delims = "<" | ">" | "#" | "%" | <"> */ #define UW 0 /* unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" */ #define MA 1 /* mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" */ #define AN 2 /* alphanum = alpha | digit */ #define RE 2 /* reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," */ static const char uri_chars[128] = { /* +2 +4 +6 +8 +10 +12 +14 */ /* 0 */ CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, /* 16 */ CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, /* 32 */ SP, MA, DE, DE, RE, DE, RE, MA, MA, MA, MA, RE, RE, MA, MA, RE, /* 48 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, RE, RE, DE, RE, DE, RE, /* 64 */ RE, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, /* 80 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, UW, UW, UW, UW, MA, /* 96 */ UW, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, /* 112 */ AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, AN, UW, UW, UW, MA, CO }; #define ESCAPE(ch) (((const signed char)(ch) < 0 || \ uri_chars[(unsigned int)(ch)] == 0)) char *oldneon_ne_path_escape(const char *abs_path) { const char *pnt; char *ret, *retpos; int count = 0; for (pnt = abs_path; *pnt != '\0'; pnt++) { if (ESCAPE(*pnt)) { count++; } } if (count == 0) { return ne_strdup(abs_path); } /* An escaped character is "%xx", i.e., two MORE * characters than the original string */ retpos = ret = ne_malloc(strlen(abs_path) + 2*count + 1); for (pnt = abs_path; *pnt != '\0'; pnt++) { if (ESCAPE(*pnt)) { /* Escape it - % */ sprintf(retpos, "%%%02x", (unsigned char) *pnt); retpos += 3; } else { /* It's cool */ *retpos++ = *pnt; } } *retpos = '\0'; return ret; } /** * \brief escape a location * * will eventuall break into sections and be smart about things */ t_uchar * escape_location (t_uchar const *location) { return oldneon_ne_path_escape(location); } /** * \brief unescape a location * * will eventuall break into sections and be smart about things */ t_uchar * unescape_location (t_uchar const *location) { return ne_path_unescape(location); } /* tag: Tom Lord Thu Jun 5 15:12:22 2003 (pfs.c) */