/* radare - LGPL - Copyright 2007-2023 - pancake */ #define R_LOG_ORIGIN "filter" #include #include #include #include #include #include #if R2__UNIX__ #include #include #include #endif #if __APPLE__ && __MAC_10_5 #define HAVE_COPYFILE_H 1 #else #define HAVE_COPYFILE_H 0 #endif #if HAVE_COPYFILE_H #include #endif #if _MSC_VER #include #endif #if R2__UNIX__ && !defined(__serenity__) #define RDWR_FLAGS O_RDWR | O_SYNC #else #define RDWR_FLAGS O_RDWR #endif #define BS 1024 static int file_stat(const char *file, struct stat* const pStat) { r_return_val_if_fail (file && pStat, -1); #if R2__WINDOWS__ wchar_t *wfile = r_utf8_to_utf16 (file); if (!wfile) { return -1; } int ret = _wstat (wfile, (struct _stat64i32 *)pStat); free (wfile); return ret; #else return stat (file, pStat); #endif } // r_file_new("", "bin", NULL) -> /bin // r_file_new(".", "bin", NULL) -> ./bin // r_file_new("/", "bin", NULL) -> //bin # shall we be stricts? R_API char *r_file_new(const char *root, ...) { va_list ap; va_start (ap, root); RStrBuf *sb = r_strbuf_new (""); if (!strcmp (root, "~")) { char *h = r_file_home (NULL); if (!h) { va_end (ap); r_strbuf_free (sb); return NULL; } r_strbuf_append (sb, h); free (h); } else { r_strbuf_append (sb, root); } r_strbuf_append (sb, R_SYS_DIR); const char *arg = va_arg (ap, char *); while (arg) { r_strbuf_append (sb, R_SYS_DIR); r_strbuf_append (sb, arg); arg = va_arg (ap, char *); } va_end (ap); char *path = r_strbuf_drain (sb); char *abs = r_file_abspath (path); free (path); return abs; } R_API bool r_file_truncate(const char *filename, ut64 newsize) { r_return_val_if_fail (filename, false); if (r_file_is_directory (filename)) { return false; } if (!r_file_exists (filename) || !r_file_is_regular (filename)) { return false; } int fd = r_sandbox_open (filename, RDWR_FLAGS, 0644); if (fd == -1) { return false; } #if defined(_MSC_VER) || R2__WINDOWS__ int r = _chsize (fd, newsize); #else int r = ftruncate (fd, newsize); #endif if (r != 0) { R_LOG_ERROR ("Could not resize %s file", filename); close (fd); return false; } close (fd); return true; } /* Example: str = r_file_basename ("home/inisider/Downloads/user32.dll"); // str == user32.dll */ R_API const char *r_file_basename(const char *path) { r_return_val_if_fail (path, NULL); const char *ptr = r_str_rchr (path, NULL, '/'); if (ptr) { path = ptr + 1; } else { if ((ptr = r_str_rchr (path, NULL, '\\'))) { path = ptr + 1; } } return path; } /* Example: str = r_file_basename ("home/inisider/Downloads"); // str == "home/inisider/Downloads" free (str); */ R_API char *r_file_dirname(const char *path) { r_return_val_if_fail (path, NULL); char *newpath = strdup (path); char *ptr = (char*)r_str_rchr (newpath, NULL, '/'); if (ptr) { *ptr = 0; } else { ptr = (char*)r_str_rchr (newpath, NULL, '\\'); if (ptr) { *ptr = 0; } } return newpath; } R_API bool r_file_is_c(const char *file) { r_return_val_if_fail (file, false); const char *ext = r_str_lchr (file, '.'); // TODO: add api in r_file_extension or r_str_ext for this if (ext) { ext++; if (!strcmp (ext, "cparse") || !strcmp (ext, "c") || !strcmp (ext, "h")) { return true; } } return false; } R_API bool r_file_is_regular(const char *str) { struct stat buf = {0}; if (!str || !*str || file_stat (str, &buf) == -1) { return false; } return ((S_IFREG & buf.st_mode) == S_IFREG); } R_API bool r_file_is_directory(const char *str) { struct stat buf = {0}; r_return_val_if_fail (!R_STR_ISEMPTY (str), false); if (file_stat (str, &buf) == -1) { return false; } #ifdef S_IFBLK if ((S_IFBLK & buf.st_mode) == S_IFBLK) { return false; } #endif return S_IFDIR == (S_IFDIR & buf.st_mode); } R_API bool r_file_fexists(const char *fmt, ...) { r_return_val_if_fail (fmt, false); int ret; char string[BS]; va_list ap; va_start (ap, fmt); vsnprintf (string, sizeof (string), fmt, ap); ret = r_file_exists (string); va_end (ap); return ret; } R_API bool r_file_exists(const char *str) { r_return_val_if_fail (str, false); struct stat buf = {0}; #if 1 if (file_stat (str, &buf) != 0) { return false; } #else char *absfile = r_file_abspath (str); r_return_val_if_fail (!R_STR_ISEMPTY (str), false); if (file_stat (absfile, &buf) == -1) { free (absfile); return false; } free (absfile); #endif return S_IFREG == (S_IFREG & buf.st_mode); } R_API ut64 r_file_size(const char *str) { r_return_val_if_fail (!R_STR_ISEMPTY (str), 0); struct stat buf = {0}; if (file_stat (str, &buf) != 0) { return 0; } return (ut64)buf.st_size; } R_API bool r_file_is_abspath(const char *file) { r_return_val_if_fail (!R_STR_ISEMPTY (file), 0); return ((*file && file[1] == ':') || *file == '/'); } R_API char *r_file_abspath_rel(const char *cwd, const char *file) { char *ret = NULL; if (!file || !*file || !strcmp (file, ".") || !strcmp (file, "./")) { return r_sys_getdir (); } if (strstr (file, "://")) { return strdup (file); } if (!strncmp (file, "~/", 2) || !strncmp (file, "~\\", 2)) { ret = r_file_home (file + 2); } else { #if R2__UNIX__ if (cwd && *file != '/') { ret = r_str_newf ("%s" R_SYS_DIR "%s", cwd, file); } #elif R2__WINDOWS__ // Network path if (!strncmp (file, "\\\\", 2)) { return strdup (file); } if (!strchr (file, ':')) { PTCHAR abspath = malloc (MAX_PATH * sizeof (TCHAR)); if (abspath) { PTCHAR f = r_sys_conv_utf8_to_win (file); int s = GetFullPathName (f, MAX_PATH, abspath, NULL); if (s > MAX_PATH) { R_LOG_ERROR ("r_file_abspath/GetFullPathName: Path to file too long"); } else if (!s) { r_sys_perror ("r_file_abspath/GetFullPathName"); } else { ret = r_sys_conv_win_to_utf8 (abspath); } free (abspath); free (f); } } #endif } if (!ret) { ret = strdup (file); } #if R2__UNIX__ && !__wasi__ char *abspath = realpath (ret, NULL); if (abspath) { free (ret); ret = abspath; } #endif return ret; } R_API char *r_file_abspath(const char *file) { r_return_val_if_fail (file, NULL); char *cwd = r_sys_getdir (); if (cwd) { char *ret = r_file_abspath_rel (cwd, file); free (cwd); return ret; } // v-- if getcwd returns null we fallback like this return strdup (file); } R_API char *r_file_binsh(void) { char *bin_sh = r_sys_getenv ("SHELL"); if (R_STR_ISEMPTY (bin_sh)) { free (bin_sh); bin_sh = r_file_path ("sh"); #if R2_590 if (!bin_sh) { #else if (!strcmp (bin_sh, "sh")) { free (bin_sh); #endif bin_sh = strdup (SHELL_PATH); } } return bin_sh; } #if R2_590 // Returns bin location in PATH, NULL if not found #else // Returns bin location in PATH, strdup(bin) if not found #endif R_API char *r_file_path(const char *bin) { r_return_val_if_fail (bin, NULL); char *file = NULL; char *path = NULL; char *str, *ptr; const char *extension = ""; if (r_str_startswith (bin, "./")) { return r_file_exists (bin) ? r_file_abspath (bin) : NULL; } char *path_env = r_sys_getenv ("PATH"); #if R2__WINDOWS__ if (!r_str_endswith (bin, ".exe")) { extension = ".exe"; } #endif if (path_env) { str = path = strdup (path_env); do { ptr = strchr (str, R_SYS_ENVSEP[0]); if (ptr) { *ptr = '\0'; } file = r_str_newf (R_JOIN_2_PATHS ("%s", "%s%s"), str, bin, extension); if (r_file_exists (file)) { free (path); free (path_env); return file; } if (ptr) { str = ptr + 1; } free (file); } while (ptr); } free (path_env); free (path); #if R2_590 return NULL; #else return strdup (bin); #endif } R_API char *r_stdin_slurp(int *sz) { #if __wasi__ #warning r_stdin_slurp not available for wasi return NULL; #elif R2__UNIX__ || R2__WINDOWS__ int i, ret, newfd; if ((newfd = dup (0)) < 0) { return NULL; } char *buf = malloc (BS); if (!buf) { close (newfd); return NULL; } for (i = ret = 0; i >= 0; i += ret) { char *new = realloc (buf, i + BS); if (!new) { free (buf); return NULL; } buf = new; ret = read (0, buf + i, BS); if (ret < 1) { break; } } if (i < 1) { i = 0; R_FREE (buf); } else { buf[i] = 0; dup2 (newfd, 0); close (newfd); } if (sz) { *sz = i; } if (!i) { R_FREE (buf); } return buf; #else #warning TODO r_stdin_slurp return NULL; #endif } // returns null terminated buffer with contents of the file R_API char *r_file_slurp(const char *str, R_NULLABLE size_t *usz) { r_return_val_if_fail (str, NULL); if (usz) { *usz = 0; } if (!r_file_exists (str)) { return NULL; } FILE *fd = r_sandbox_fopen (str, "rb"); if (!fd) { return NULL; } if (fseek (fd, 0, SEEK_END) == -1) { // cannot determine the size of the file } long sz = ftell (fd); if (sz < 0) { fclose (fd); return NULL; } #if __wasi__ fclose (fd); fd = r_sandbox_fopen (str, "rb"); if (!fd) { return NULL; } #endif if (!sz) { if (r_file_is_regular (str)) { char *buf = NULL; long size = 0; (void)fseek (fd, 0, SEEK_SET); do { char *nbuf = realloc (buf, size + BS); if (!nbuf) { break; } buf = nbuf; size_t r = fread (buf + size, 1, BS, fd); if (ferror (fd)) { R_FREE (buf); goto regular_err; } size += r; } while (!feof (fd)); char *nbuf = realloc (buf, size + 1); if (!nbuf) { free (buf); fclose (fd); return NULL; } buf = nbuf; buf[size] = '\0'; if (usz) { *usz = size; } regular_err: fclose (fd); return buf; } // try to read 64K sz = UT16_MAX; } rewind (fd); char *ret = (char *)malloc (sz + 1); if (!ret) { fclose (fd); return NULL; } size_t rsz = fread (ret, 1, sz, fd); if (rsz != sz) { R_LOG_WARN ("r_file_slurp: fread: truncated read (%d / %d)", (int)rsz, (int)sz); sz = rsz; } fclose (fd); ret[sz] = '\0'; if (usz) { *usz = sz; } return ret; } R_API ut8 *r_file_gzslurp(const char *str, int *outlen, int origonfail) { r_return_val_if_fail (str, NULL); if (outlen) { *outlen = 0; } size_t sz; ut8 *in = (ut8*)r_file_slurp (str, &sz); if (!in) { return NULL; } ut8 *out = r_inflate (in, (int)sz, NULL, outlen); if (!out && origonfail) { // if uncompression fails, return orig buffer ? if (outlen) { *outlen = (int)sz; } in[sz] = 0; return in; } free (in); return out; } R_API ut8 *r_file_slurp_hexpairs(const char *str, int *usz) { r_return_val_if_fail (str, NULL); if (usz) { *usz = 0; } ut8 *ret; long sz; int c, bytes = 0; FILE *fd = r_sandbox_fopen (str, "rb"); if (!fd) { return NULL; } (void) fseek (fd, 0, SEEK_END); sz = ftell (fd); (void) fseek (fd, 0, SEEK_SET); ret = (ut8*)malloc ((sz>>1)+1); if (!ret) { fclose (fd); return NULL; } for (;;) { if (fscanf (fd, " #%*[^\n]") == 1) { continue; } if (fscanf (fd, "%02x", &c) == 1) { ret[bytes++] = c; continue; } if (feof (fd)) { break; } free (ret); fclose (fd); return NULL; } ret[bytes] = '\0'; fclose (fd); if (usz) { *usz = bytes; } return ret; } R_API char *r_file_slurp_range(const char *str, ut64 off, int sz, int *osz) { char *ret; size_t read_items; FILE *fd = r_sandbox_fopen (str, "rb"); if (!fd) { return NULL; } // XXX handle out of bound reads (eof) if (fseek (fd, off, SEEK_SET) < 0) { fclose (fd); return NULL; } ret = (char *) malloc (sz + 1); if (ret) { if (osz) { *osz = (int)(size_t) fread (ret, 1, sz, fd); } else { read_items = fread (ret, 1, sz, fd); if (!read_items) { fclose (fd); return ret; } } ret[sz] = '\0'; } fclose (fd); return ret; } R_API char *r_file_slurp_random_line(const char *file) { r_return_val_if_fail (file, NULL); int i = 0; return r_file_slurp_random_line_count (file, &i); } R_API char *r_file_slurp_random_line_count(const char *file, int *line) { r_return_val_if_fail (file && line, NULL); /* Reservoir Sampling */ char *ptr = NULL, *str; size_t i, lines, selection = -1; int start = *line; if ((str = r_file_slurp (file, NULL))) { r_num_irand (); for (i = 0; str[i]; i++) { if (str[i] == '\n') { //here rand doesn't have any security implication // https://www.securecoding.cert.org/confluence/display/c/MSC30-C.+Do+not+use+the+rand()+function+for+generating+pseudorandom+numbers if (!(r_num_rand ((++(*line))))) { selection = (*line - 1); /* The line we want. */ } } } if ((selection < start) || (selection == -1)) { free (str); return NULL; } else { lines = selection - start; } if (lines > 0) { for (i = 0; str[i] && lines; i++) { if (str[i] == '\n') { lines--; } } ptr = str + i; for (i = 0; ptr[i]; i++) { if (ptr[i] == '\n') { ptr[i] = '\0'; break; } } ptr = strdup (ptr); } free (str); } return ptr; } R_API bool r_file_dump_line(const char *file, int line, const char *msg, bool replace) { r_return_val_if_fail (file, false); if (!msg || !*msg) { return false; } RStrBuf *sb = r_strbuf_new (""); int i, lines = 0; size_t sz; if (line > 0) { line--; } char *ptr = NULL, *str = r_file_slurp (file, &sz); // TODO: Implement context if (str) { for (i = 0; str[i]; i++) { if (str[i] == '\n') { lines++; } } #if 0 if (line > lines) { free (str); eprintf ("lieav lines\n"); return NULL; } #endif lines = line - 1; for (i = 0; str[i] && lines > 0; i++) { if (str[i] == '\n') { lines--; } } ptr = str + i; for (i = 0; ptr[i]; i++) { if (ptr[i] == '\n') { ptr[i] = '\0'; break; } } r_strbuf_append_n (sb, ptr, i); r_strbuf_append (sb, "\n"); r_strbuf_append (sb, msg); r_strbuf_append (sb, "\n"); if (!replace) { r_strbuf_append (sb, ptr); } r_strbuf_append (sb, ptr + i + 1); free (str); } int sblen = r_strbuf_length (sb); char *res = r_strbuf_drain (sb); eprintf ("%s\n", res); bool rc = r_file_dump (file, (const ut8*)res, sblen, false); free (res); return rc; } R_API char *r_file_slurp_line(const char *file, int line, int context) { r_return_val_if_fail (file, NULL); int i, lines = 0; size_t sz; char *ptr = NULL, *str = r_file_slurp (file, &sz); // TODO: Implement context if (str) { for (i = 0; str[i]; i++) { if (str[i] == '\n') { lines++; } } if (line > lines) { free (str); return NULL; } lines = line - 1; for (i = 0; str[i]&&lines; i++) { if (str[i] == '\n') { lines--; } } ptr = str+i; for (i = 0; ptr[i]; i++) { if (ptr[i] == '\n') { ptr[i] = '\0'; break; } } ptr = strdup (ptr); free (str); } return ptr; } R_API char *r_file_slurp_lines_from_bottom(const char *file, int line) { r_return_val_if_fail (file, NULL); int i, lines = 0; size_t sz; char *ptr = NULL, *str = r_file_slurp (file, &sz); // TODO: Implement context if (str) { for (i = 0; str[i]; i++) { if (str[i] == '\n') { lines++; } } if (line > lines) { return str; // number of lines requested in more than present, return all } i--; for (; str[i] && line; i--) { if (str[i] == '\n') { line--; } } ptr = str + i; ptr = strdup (ptr); free (str); } return ptr; } R_API char *r_file_slurp_lines(const char *file, int line, int count) { r_return_val_if_fail (file, NULL); int i, lines = 0; size_t sz; char *ptr = NULL, *str = r_file_slurp (file, &sz); // TODO: Implement context if (str) { for (i = 0; str[i]; i++) { if (str[i] == '\n') { lines++; } } if (line > lines) { free (str); return NULL; } lines = line - 1; for (i = 0; str[i] && lines; i++) { if (str[i] == '\n') { lines--; } } ptr = str+i; for (i = 0; ptr[i]; i++) { if (ptr[i] == '\n') { if (count) { count--; } else { ptr[i] = '\0'; break; } } } ptr = strdup (ptr); free (str); } return ptr; } R_API char *r_file_root(const char *root, const char *path) { r_return_val_if_fail (root && path, NULL); char *ret, *s = r_str_replace (strdup (path), "..", "", 1); // XXX ugly hack while (strstr (s, "..")) { s = r_str_replace (s, "..", "", 1); } while (strstr (s, "./")) { s = r_str_replace (s, "./", "", 1); } while (strstr (s, "//")) { s = r_str_replace (s, "//", "", 1); } ret = r_str_append (strdup (root), R_SYS_DIR); ret = r_str_append (ret, s); free (s); return ret; } R_API bool r_file_hexdump(const char *file, const ut8 *buf, int len, int append) { FILE *fd; int i,j; if (!file || !*file || !buf || len < 0) { R_LOG_ERROR ("r_file_hexdump file: %s buf: %p", file, buf); return false; } if (append) { fd = r_sandbox_fopen (file, "ab"); } else { r_sys_truncate (file, 0); fd = r_sandbox_fopen (file, "wb"); } if (!fd) { R_LOG_ERROR ("Cannot open '%s' for writing", file); return false; } for (i = 0; i < len; i += 16) { int l = R_MIN (16, len - i); fprintf (fd, "0x%08"PFMT64x" ", (ut64)i); for (j = 0; j + 2 <= l; j += 2) { fprintf (fd, "%02x%02x ", buf[i +j], buf[i+j+1]); } if (j < l) { fprintf (fd, "%02x ", buf[i + j]); j += 2; } if (j < 16) { fprintf (fd, "%*s ", (16 - j) / 2 * 5, ""); } for (j = 0; j < 16; j++) { fprintf (fd, "%c", j < l && IS_PRINTABLE (buf[i + j])? buf[i+j]: '.'); } fprintf (fd, "\n"); } fclose (fd); return true; } R_API bool r_file_touch(const char *file) { r_return_val_if_fail (file, false); return r_file_dump (file, NULL, 0, true); } R_API bool r_file_dump(const char *file, const ut8 *buf, int len, bool append) { r_return_val_if_fail (!R_STR_ISEMPTY (file), false); FILE *fd; if (append) { fd = r_sandbox_fopen (file, "ab"); } else { r_sys_truncate (file, 0); fd = r_sandbox_fopen (file, "wb"); } if (!fd) { R_LOG_ERROR ("Cannot open '%s' for writing", file); return false; } if (buf) { if (len < 0) { len = strlen ((const char *)buf); } if (len > 0 && fwrite (buf, len, 1, fd) != 1) { r_sys_perror ("r_file_dump: fwrite: error\n"); fclose (fd); return false; } } fclose (fd); return true; } R_API bool r_file_move(const char *src, const char *dst) { r_return_val_if_fail (!R_STR_ISEMPTY (src) && !R_STR_ISEMPTY (dst), false); if (r_sandbox_enable (0)) { return false; } // rename fails when files are in different mountpoints // in this situation it needs to be copied and removed if (rename (src, dst) != 0) { char *a = r_str_escape (src); char *b = r_str_escape (dst); char *input = r_str_newf ("\"%s\" \"%s\"", a, b); #if R2__WINDOWS__ int rc = r_sys_cmdf ("move %s", input); #else int rc = r_sys_cmdf ("mv %s", input); #endif free (a); free (b); free (input); return rc == 0; } return true; } R_API bool r_file_rm(const char *file) { r_return_val_if_fail (!R_STR_ISEMPTY (file), false); if (r_sandbox_enable (0)) { return false; } if (r_file_is_directory (file)) { #if R2__WINDOWS__ LPTSTR file_ = r_sys_conv_utf8_to_win (file); bool ret = RemoveDirectory (file_); free (file_); return !ret; #else return !rmdir (file); #endif } else { #if R2__WINDOWS__ LPTSTR file_ = r_sys_conv_utf8_to_win (file); bool ret = DeleteFile (file_); free (file_); return !ret; #else return !unlink (file); #endif } } R_API char *r_file_readlink(const char *path) { r_return_val_if_fail (!R_STR_ISEMPTY (path), false); if (!r_sandbox_enable (0)) { #if R2__UNIX__ int ret; char pathbuf[4096] = {0}; strncpy (pathbuf, path, sizeof (pathbuf) - 1); repeat: ret = readlink (path, pathbuf, sizeof (pathbuf)-1); if (ret != -1) { pathbuf[ret] = 0; path = pathbuf; goto repeat; } return strdup (pathbuf); #endif } return NULL; } R_API int r_file_mmap_write(const char *file, ut64 addr, const ut8 *buf, int len) { #if R2__WINDOWS__ HANDLE fh = INVALID_HANDLE_VALUE; DWORD written = 0; LPTSTR file_ = NULL; int ret = -1; if (r_sandbox_enable (0)) { return -1; } file_ = r_sys_conv_utf8_to_win (file); fh = CreateFile (file_, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (fh == INVALID_HANDLE_VALUE) { r_sys_perror ("r_file_mmap_write/CreateFile"); goto err_r_file_mmap_write; } SetFilePointer (fh, addr, NULL, FILE_BEGIN); if (!WriteFile (fh, buf, (DWORD)len, &written, NULL)) { r_sys_perror ("r_file_mmap_write/WriteFile"); goto err_r_file_mmap_write; } ret = len; err_r_file_mmap_write: free (file_); if (fh != INVALID_HANDLE_VALUE) { CloseHandle (fh); } return ret; #elif __wasi__ || EMSCRIPTEN return -1; #elif R2__UNIX__ int fd = r_sandbox_open (file, RDWR_FLAGS, 0644); const int pagesize = getpagesize (); int mmlen = len + pagesize; int rest = addr % pagesize; ut8 *mmap_buf; if (fd == -1) { return -1; } if ((st64)addr < 0) { return -1; } mmap_buf = mmap (NULL, mmlen * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)addr - rest); if (((int)(size_t)mmap_buf) == -1) { return -1; } memcpy (mmap_buf + rest, buf, len); #if !defined(__serenity__) msync (mmap_buf + rest, len, MS_INVALIDATE); #endif munmap (mmap_buf, mmlen * 2); close (fd); return len; #else return -1; #endif } R_API int r_file_mmap_read(const char *file, ut64 addr, ut8 *buf, int len) { #if R2__WINDOWS__ HANDLE fm = NULL, fh = INVALID_HANDLE_VALUE; LPTSTR file_ = NULL; int ret = -1; if (r_sandbox_enable (0)) { return -1; } file_ = r_sys_conv_utf8_to_win (file); fh = CreateFile (file_, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); if (fh == INVALID_HANDLE_VALUE) { r_sys_perror ("r_file_mmap_read/CreateFile"); goto err_r_file_mmap_read; } fm = CreateFileMapping (fh, NULL, PAGE_READONLY, 0, 0, NULL); if (!fm) { r_sys_perror ("CreateFileMapping"); goto err_r_file_mmap_read; } ut8 *obuf = MapViewOfFile (fm, FILE_MAP_READ, 0, 0, len); if (!obuf) { goto err_r_file_mmap_read; } memcpy (obuf, buf, len); UnmapViewOfFile (obuf); ret = len; err_r_file_mmap_read: if (fh != INVALID_HANDLE_VALUE) { CloseHandle (fh); } if (fm) { CloseHandle (fm); } free (file_); return ret; #elif __wasi__ || EMSCRIPTEN return 0; #elif R2__UNIX__ int fd = r_sandbox_open (file, O_RDONLY, 0644); const int pagesize = 4096; int mmlen = len+pagesize; int rest = addr%pagesize; ut8 *mmap_buf; if (fd == -1) { return -1; } mmap_buf = mmap (NULL, mmlen*2, PROT_READ, MAP_SHARED, fd, (off_t)addr-rest); if (((int)(size_t)mmap_buf) == -1) { return -1; } memcpy (buf, mmap_buf+rest, len); munmap (mmap_buf, mmlen*2); close (fd); return len; #else return 0; #endif } #if __wasi__ || EMSCRIPTEN static RMmap *r_file_mmap_unix(RMmap *m, int fd) { return NULL; } #elif R2__UNIX__ static RMmap *r_file_mmap_unix(RMmap *m, int fd) { ut8 empty = m->len == 0; m->buf = mmap (NULL, (empty?BS:m->len) , m->rw?PROT_READ|PROT_WRITE:PROT_READ, MAP_SHARED, fd, (off_t)m->base); if (m->buf == MAP_FAILED) { m->buf = NULL; } return m; } #elif R2__WINDOWS__ static RMmap *r_file_mmap_windows(RMmap *m, const char *file) { LPTSTR file_ = r_sys_conv_utf8_to_win (file); bool success = false; m->fh = CreateFile (file_, GENERIC_READ | (m->rw ? GENERIC_WRITE : 0), FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (m->fh == INVALID_HANDLE_VALUE) { r_sys_perror ("CreateFile"); goto err_r_file_mmap_windows; } m->fm = CreateFileMapping (m->fh, NULL, PAGE_READONLY, 0, 0, NULL); //m->rw?PAGE_READWRITE:PAGE_READONLY, 0, 0, NULL); if (!m->fm) { r_sys_perror ("CreateFileMapping"); goto err_r_file_mmap_windows; } m->buf = MapViewOfFile (m->fm, // m->rw?(FILE_MAP_READ|FILE_MAP_WRITE):FILE_MAP_READ, FILE_MAP_COPY, UT32_HI (m->base), UT32_LO (m->base), 0); success = true; err_r_file_mmap_windows: if (!success) { if (m->fh != INVALID_HANDLE_VALUE) { CloseHandle (m->fh); } R_FREE (m); } free (file_); return m; } #else static RMmap *file_mmap_other(RMmap *m) { ut8 empty = m->len == 0; m->buf = malloc ((empty? BS: m->len)); if (!empty && m->buf) { lseek (m->fd, (off_t)0, SEEK_SET); read (m->fd, m->buf, m->len); } else { R_FREE (m); } return m; } #endif R_API RMmap *r_file_mmap_arch(RMmap *mmap, const char *filename, int fd) { #if R2__WINDOWS__ (void)fd; return r_file_mmap_windows (mmap, filename); #elif R2__UNIX__ (void)filename; return r_file_mmap_unix (mmap, fd); #else (void)filename; (void)fd; return file_mmap_other (mmap); #endif } // TODO: add rwx support? R_API RMmap *r_file_mmap(const char *file, bool rw, ut64 base) { RMmap *m = NULL; int fd = -1; if (!rw && !r_file_exists (file)) { return m; } fd = r_sandbox_open (file, rw? RDWR_FLAGS: O_RDONLY, 0644); if (fd == -1 && !rw) { R_LOG_ERROR ("r_file_mmap: file (%s) does not exist", file); //m->buf = malloc (m->len); return m; } m = R_NEW (RMmap); if (!m) { if (fd != -1) { close (fd); } return NULL; } m->base = base; m->rw = rw; m->fd = fd; m->len = fd != -1? lseek (fd, (off_t)0, SEEK_END) : 0; m->filename = strdup (file); if (m->fd == -1) { return m; } if (m->len == (off_t)-1) { close (fd); R_FREE (m); return NULL; } #if R2__UNIX__ return r_file_mmap_unix (m, fd); #elif R2__WINDOWS__ close (fd); m->fd = -1; return r_file_mmap_windows (m, file); #else return file_mmap_other (m); #endif } R_API void r_file_mmap_free(RMmap *m) { if (!m) { return; } #if R2__WINDOWS__ if (m->fm != INVALID_HANDLE_VALUE) { CloseHandle (m->fm); } if (m->fh != INVALID_HANDLE_VALUE) { CloseHandle (m->fh); } if (m->buf) { UnmapViewOfFile (m->buf); } #endif if (m->fd == -1) { free (m); return; } free (m->filename); #if R2__UNIX__ && !__wasi__ munmap (m->buf, m->len); #endif close (m->fd); free (m); } R_API char *r_file_temp(const char *prefix) { if (!prefix) { prefix = ""; } char *path = r_file_tmpdir (); char *res = r_str_newf ("%s/%s.%"PFMT64x, path, prefix, r_time_now ()); free (path); return res; } R_API char *r_file_temp_ex(R_NULLABLE const char *prefix, R_NULLABLE const char *ex) { prefix = R_STR_ISEMPTY (prefix)? "r2": prefix; ex = R_STR_ISEMPTY (ex)? "": ex; char *path = r_file_tmpdir (); char *res = NULL; if (path) { ut64 t = r_time_now (); res = r_str_newf ("%s/%s.%" PFMT64x "%s", path, prefix, t, ex); free (path); } return res; } static inline char *file_fmt_split(const char *fmt) { if (R_STR_ISEMPTY (fmt)) { return r_file_temp_ex (NULL, NULL); } char *name = NULL; char *dup = strdup (fmt); if (dup) { RList *splt = r_str_split_list (dup, "*", 2); if (splt && r_list_length (splt)) { char *pref = r_list_pop_head (splt); char *ex = r_list_pop_head (splt); name = r_file_temp_ex (pref, ex); } r_list_free (splt); free (dup); } return name; } R_API int r_file_mkstemp(R_NULLABLE const char *prefix, char **oname) { int h = -1; if (!prefix) { prefix = "r2"; } #if R2__WINDOWS__ LPTSTR name = NULL; char *path = r_file_tmpdir (); if (!path) { return -1; } LPTSTR path_ = r_sys_conv_utf8_to_win (path); free (path); LPTSTR prefix_ = r_sys_conv_utf8_to_win (prefix); name = (LPTSTR)malloc (sizeof (TCHAR) * (MAX_PATH + 1)); if (!name) { goto err_r_file_mkstemp; } if (GetTempFileName (path_, prefix_, 0, name)) { char *name_ = r_sys_conv_win_to_utf8 (name); h = r_sandbox_open (name_, RDWR_FLAGS | O_EXCL | O_BINARY, 0644); if (oname) { if (h != -1) { *oname = name_; } else { *oname = NULL; free (name_); } } else { free (name_); } } err_r_file_mkstemp: free (name); free (path_); free (prefix_); #elif __wasi__ // nothing to do for wasm, drops to return -1 #else char *name = file_fmt_split (prefix); if (name) { int perm = RDWR_FLAGS | O_CREAT | O_EXCL | O_BINARY; h = r_sandbox_open (name, perm, 0644); if (h != -1) { *oname = name; } else { free (name); } } #endif return h; } R_API char *r_file_tmpdir(void) { #if R2__WINDOWS__ LPTSTR tmpdir; char *path = NULL; DWORD len = 0; tmpdir = (LPTSTR)calloc (1, sizeof (TCHAR) * (MAX_PATH + 1)); if (!tmpdir) { return NULL; } if ((len = GetTempPath (MAX_PATH + 1, tmpdir)) == 0) { path = r_sys_getenv ("TEMP"); if (!path) { path = strdup ("C:\\WINDOWS\\Temp\\"); } } else { tmpdir[len] = 0; DWORD (WINAPI *glpn)(LPCTSTR, LPCTSTR, DWORD) = r_lib_dl_sym (GetModuleHandle (TEXT ("kernel32.dll")), W32_TCALL("GetLongPathName")); if (glpn) { // Windows XP sometimes returns short path name glpn (tmpdir, tmpdir, MAX_PATH + 1); } path = r_sys_conv_win_to_utf8 (tmpdir); } free (tmpdir); // Windows 7, stat() function fail if tmpdir ends with '\\' if (path) { size_t path_len = strlen (path); if (path_len > 0 && path[path_len - 1] == '\\') { path[path_len - 1] = '\0'; } } #else char *path = r_sys_getenv ("TMPDIR"); if (path && !*path) { R_FREE (path); } if (!path) { #if __ANDROID__ if (r_file_is_directory (TERMUX_PREFIX "/tmp")) { path = strdup (TERMUX_PREFIX "/tmp"); } else { path = strdup ("/data/local/tmp"); } #else path = strdup ("/tmp"); #endif } #endif if (!r_file_is_directory (path)) { free (path); return NULL; //R_LOG_ERROR ("Cannot find dir.tmp '%s'", path); } return path; } R_API bool r_file_copy(const char *src, const char *dst) { r_return_val_if_fail (R_STR_ISNOTEMPTY (src) && R_STR_ISNOTEMPTY (dst), false); if (!strcmp (src, dst)) { R_LOG_ERROR ("Cannot copy file '%s' to itself", src); return false; } /* TODO: implement in C */ /* TODO: Use NO_CACHE for iOS dyldcache copying */ #if HAVE_COPYFILE_H return copyfile (src, dst, 0, COPYFILE_DATA | COPYFILE_XATTR) != -1; #elif R2__WINDOWS__ PTCHAR s = r_sys_conv_utf8_to_win (src); PTCHAR d = r_sys_conv_utf8_to_win (dst); if (!s || !d) { free (s); free (d); return false; } bool ret = CopyFile (s, d, 0); if (!ret) { r_sys_perror ("r_file_copy"); } free (s); free (d); return ret; #else char *src2 = r_str_replace (strdup (src), "'", "\\'", 1); char *dst2 = r_str_replace (strdup (dst), "'", "\\'", 1); int rc = r_sys_cmdf ("cp -f '%s' '%s'", src2, dst2); free (src2); free (dst2); return rc == 0; #endif } static bool dir_recursive(RList *dst, const char *dir) { char *name, *path = NULL; RListIter *iter; bool ret = true; RList *files = r_sys_dir (dir); if (!files) { return false; } r_list_foreach (files, iter, name) { if (!strcmp (name, "..") || !strcmp (name, ".")) { continue; } path = r_str_newf ("%s" R_SYS_DIR "%s", dir, name); if (!path) { ret = false; break; } if (!r_list_append (dst, strdup (path))) { ret = false; break; } if (r_file_is_directory (path)) { if (!dir_recursive (dst, path)) { ret = false; break; } } R_FREE (path); } free (path); r_list_free (files); return ret; } R_API RList *r_file_lsrf(const char *dir) { RList *ret = r_list_newf (free); if (!ret) { return NULL; } if (!dir_recursive (ret, dir)) { r_list_free (ret); return NULL; } return ret; } R_API bool r_file_rm_rf(const char *dir) { RList *files = r_file_lsrf (dir); if (r_file_exists (dir)) { return r_file_rm (dir); } if (!files) { return false; } r_list_sort (files, (RListComparator) strcmp); RListIter *iter; char *f; r_list_foreach_prev (files, iter, f) { r_file_rm (f); } return r_file_rm (dir); } static void recursive_glob(const char *path, const char *glob, RList* list, int depth) { if (depth < 1) { return; } char* file; RListIter *iter; RList *files = r_sys_dir (path); r_list_foreach (files, iter, file) { if (!strcmp (file, ".") || !strcmp (file, "..")) { continue; } char *filename = r_file_new (path, file, NULL); if (r_file_is_directory (filename)) { recursive_glob (filename, glob, list, depth - 1); free (filename); } else if (r_str_glob (file, glob)) { r_list_append (list, filename); } else { free (filename); } } r_list_free (files); } R_API RList* r_file_glob(const char *_globbed_path, int maxdepth) { char *globbed_path = strdup (_globbed_path); RList *files = r_list_newf (free); char *glob = strchr (globbed_path, '*'); if (!glob) { r_list_append (files, strdup (globbed_path)); } else { *glob = '\0'; char *last_slash = (char *)r_str_last (globbed_path, R_SYS_DIR); *glob = '*'; char *path, *glob_ptr; if (last_slash) { glob_ptr = last_slash + 1; if (globbed_path[0] == '~') { char *rpath = r_str_newlen (globbed_path + 2, last_slash - globbed_path - 1); path = r_file_home (r_str_get (rpath)); free (rpath); } else { path = r_str_newlen (globbed_path, last_slash - globbed_path + 1); } } else { glob_ptr = globbed_path; path = r_str_newf (".%s", R_SYS_DIR); } if (!path) { r_list_free (files); free (globbed_path); return NULL; } if (*(glob + 1) == '*') { // "**" recursive_glob (path, glob_ptr, files, maxdepth); } else { // "*" recursive_glob (path, glob_ptr, files, 1); } free (path); } free (globbed_path); return files; } #if R2__UNIX__ static bool is_executable_header(const char *file) { bool ret = false; int osz = 0; char *data = r_file_slurp_range (file, 0, 1024, &osz); if (data && osz > 4) { // 0xfeedface 0xcefaedfe) // 32bit // 0xfeedfacf 0xcffaedfe) // 64bit if (!memcmp (data, "\xca\xfe\xba\xbe", 4)) { ret = true; } else if (!memcmp (data, "#!/", 3)) { ret = true; } else if (!memcmp (data, "\x7f" "ELF", 4)) { ret = true; } } free (data); return ret; } #endif R_API bool r_file_is_executable(const char *file) { bool ret = false; #if R2__UNIX__ struct stat buf = {0}; if (stat (file, &buf) != 0) { return false; } if (buf.st_mode & 0111) { return is_executable_header (file); } #elif R2__WINDOWS__ const char *ext = r_file_extension (file); if (ext) { return !strcmp (ext, "exe") || !strcmp (ext, "com") || !strcmp (ext, "bat"); } #endif return ret; } R_API const char *r_file_extension(const char *str) { const char *dot = r_str_lchr (str, '.'); if (dot) { return dot + 1; } return NULL; } // returns true if both files exist and f2 is modified after f1 (aka f2 > newer-than > f1) R_API bool r_file_is_newer(const char *f1, const char *f2) { struct stat a1, a2; if (stat (f1, &a1) == -1) { return false; } if (stat (f2, &a2) == -1) { return false; } long a = a1.st_mtime; long b = a2.st_mtime; return a > b; }