// ============================================================================ // // = LIBRARY // ULib - c++ library // // = FILENAME // file.cpp // // = AUTHOR // Stefano Casazza // // ============================================================================ #include #include #include #include #include #ifdef USE_LIBMAGIC # include #endif #ifdef HAVE_NUMA # include #endif char* UFile::cwd_save; char* UFile::pfree; long UFile::nr_hugepages; uint32_t UFile::nfree; uint32_t UFile::cwd_save_len; #if defined(U_LINUX) && (defined(MAP_HUGE_1GB) || defined(MAP_HUGE_2MB)) // (since Linux 3.8) uint32_t UFile::rlimit_memfree = U_2M; #else uint32_t UFile::rlimit_memfree = 16U*1024U; #endif uint32_t UFile::rlimit_memalloc = 256U*1024U*1024U; #ifdef DEBUG int UFile::num_file_object; void UFile::inc_num_file_object(UFile* pthis) { U_TRACE(0+256, "UFile::inc_num_file_object(%p)", pthis) ++num_file_object; // U_INTERNAL_DUMP("this = %p", pthis) // U_INTERNAL_DUMP("&st_dev = %p", &(pthis->st_dev)) // U_INTERNAL_DUMP("&st_ctime = %p", &(pthis->st_ctime)) // U_INTERNAL_DUMP("memory._this = %p", pthis->memory._this) U_INTERNAL_ASSERT_EQUALS((void*)pthis, (void*)&(pthis->st_dev)) } void UFile::chk_num_file_object() { if (num_file_object) U_WARNING("UFile::chdir() with num file object = %d", num_file_object); } void UFile::dec_num_file_object(int fd) { --num_file_object; if (fd != -1) U_WARNING("File descriptor %d not closed...", fd); } #endif #ifndef MAP_NORESERVE #define MAP_NORESERVE 0 #endif #ifndef MREMAP_MAYMOVE #define MREMAP_MAYMOVE 1 #endif void UFile::setPathRelativ(const UString* environment) { U_TRACE(0, "UFile::setPathRelativ(%p)", environment) U_INTERNAL_ASSERT(pathname) char c = pathname.first_char(); if (c == '~' || c == '$') { UString x = UStringExt::expandPath(pathname, environment); if (x) pathname = x; } setPathRelativ(); } bool UFile::open(int flags) { U_TRACE(0, "UFile::open(%d)", flags) U_CHECK_MEMORY U_INTERNAL_ASSERT_EQUALS(fd, -1) U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) U_INTERNAL_ASSERT_MAJOR(path_relativ_len, 0) fd = UFile::open(path_relativ, flags, PERM_FILE); if (fd != -1) U_RETURN(true); U_RETURN(false); } bool UFile::creat(int flags, mode_t mode) { U_TRACE(0, "UFile::creat(%d,%d)", flags, mode) U_CHECK_MEMORY U_INTERNAL_ASSERT_EQUALS(fd, -1) U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) fd = UFile::open(path_relativ, O_CREAT | flags, mode); if (fd != -1) U_RETURN(true); U_RETURN(false); } bool UFile::stat() { U_TRACE_NO_PARAM(1, "UFile::stat()") U_CHECK_MEMORY U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) st_ino = 0; if (U_SYSCALL(stat, "%S,%p", U_PATH_CONV(path_relativ), (struct stat*)this) == 0) U_RETURN(true); st_mode = 0; U_RETURN(false); } bool UFile::isRunningInChroot() { U_TRACE_NO_PARAM(1, "UFile::isRunningInChroot()") #ifdef U_LINUX struct stat st; if (U_SYSCALL(stat, "%S,%p", "/proc/1/root", &st) == 0) { dev_t st_dev = st.st_dev; ino_t st_ino = st.st_ino; if (U_SYSCALL(stat, "%S,%p", "/", &st) == 0 && (st_dev != st.st_dev || st_ino != st.st_ino)) { U_RETURN(true); } } #endif U_RETURN(false); } bool UFile::chdir(const char* path, bool flag_save) { U_TRACE(1, "UFile::chdir(%S,%b)", path, flag_save) chk_num_file_object(); U_INTERNAL_ASSERT_POINTER(cwd_save) U_INTERNAL_DUMP("u_cwd(%u) = %S", u_cwd_len, u_cwd) U_INTERNAL_DUMP("cwd_save(%u) = %S", cwd_save_len, cwd_save) if (path) { if (strcmp(path, u_cwd) == 0) U_RETURN(true); # ifndef _MSWINDOWS_ U_INTERNAL_ASSERT(IS_DIR_SEPARATOR(u_cwd[0])) # endif if (flag_save) { cwd_save_len = u_cwd_len; u__strcpy(cwd_save, u_cwd); } } else { U_INTERNAL_ASSERT(flag_save) U_INTERNAL_ASSERT_MAJOR(cwd_save_len, 0) path = cwd_save; } bool result = (U_SYSCALL(chdir, "%S", U_PATH_CONV(path)) != -1); if (result) { if (path == cwd_save) // NB: => chdir(0, true)... { U_INTERNAL_ASSERT(flag_save) u_cwd_len = cwd_save_len; u__strcpy(u_cwd, cwd_save); cwd_save_len = 0; } else if (IS_DIR_SEPARATOR(path[0]) == false) u_getcwd(); else { u_cwd_len = u__strlen(path, __PRETTY_FUNCTION__); U_INTERNAL_ASSERT_MINOR(u_cwd_len, U_PATH_MAX) u__strcpy(u_cwd, path); } } U_INTERNAL_DUMP("u_cwd(%u) = %S", u_cwd_len, u_cwd) U_INTERNAL_DUMP("cwd_save(%u) = %S", cwd_save_len, cwd_save) U_RETURN(result); } uint32_t UFile::setPathFromFile(const UFile& file, char* buffer_path, const char* suffix, uint32_t len) { U_TRACE(1, "UFile::setPathFromFile(%p,%p,%.*S,%u)", &file, buffer_path, len, suffix, len) U_INTERNAL_DUMP("file.path_relativ(%u) = %.*S", file.path_relativ_len, file.path_relativ_len, file.path_relativ) U_MEMCPY(buffer_path, file.path_relativ, file.path_relativ_len); U_MEMCPY(buffer_path + file.path_relativ_len, suffix, len); uint32_t new_path_relativ_len = file.path_relativ_len + len; U_INTERNAL_ASSERT_MINOR(new_path_relativ_len,(int32_t)MAX_FILENAME_LEN) buffer_path[new_path_relativ_len] = '\0'; U_INTERNAL_DUMP("buffer_path(%u) = %S", new_path_relativ_len, buffer_path) U_RETURN(new_path_relativ_len); } void UFile::setPath(const UFile& file, char* buffer_path, const char* suffix, uint32_t len) { U_TRACE(1, "UFile::setPath(%p,%p,%.*S,%u)", &file, buffer_path, len, suffix, len) U_INTERNAL_DUMP("u_cwd(%u) = %S", u_cwd_len, u_cwd) U_INTERNAL_DUMP("cwd_save(%u) = %S", cwd_save_len, cwd_save) reset(); if (IS_DIR_SEPARATOR(file.path_relativ[0]) == false && cwd_save_len) (void) U_SYSCALL(chdir, "%S", U_PATH_CONV(cwd_save)); // for IR... path_relativ_len = file.path_relativ_len + len; if (buffer_path == U_NULLPTR) { pathname.setBuffer(path_relativ_len); pathname.size_adjust(path_relativ_len); buffer_path = pathname.data(); } path_relativ = buffer_path; (void) setPathFromFile(file, buffer_path, suffix, len); U_INTERNAL_DUMP("path_relativ = %.*S", path_relativ_len, path_relativ) } UString UFile::getName() const { U_TRACE_NO_PARAM(0, "UFile::getName()") U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) U_INTERNAL_ASSERT_MAJOR(path_relativ_len, 0) uint32_t pos; UString result; const char* base = U_NULLPTR; ptrdiff_t name_len = path_relativ_len; for (const char* ptr = path_relativ, *end = path_relativ + path_relativ_len; ptr < end; ++ptr) if (*ptr == '/') base = ptr + 1; if (base) name_len -= (base - path_relativ); pos = pathname.size() - name_len; U_INTERNAL_DUMP("name = %.*S", name_len, pathname.c_pointer(pos)) U_ASSERT(UStringExt::endsWith(pathname, pathname.c_pointer(pos), name_len)) result = pathname.substr(pos); U_RETURN_STRING(result); } bool UFile::isNameDosMatch(const char* mask, uint32_t mask_len) const { U_TRACE(0, "UFile::isNameDosMatch(%.*S,%u)", mask_len, mask, mask_len) UString basename = getName(); if (UServices::dosMatchWithOR(U_STRING_TO_PARAM(basename), mask, mask_len, 0)) U_RETURN(true); U_RETURN(false); } UString UFile::getDirName() const { U_TRACE_NO_PARAM(0, "UFile::getDirName()") U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) U_INTERNAL_ASSERT_MAJOR(path_relativ_len, 0) UString result = UStringExt::dirname(path_relativ, path_relativ_len); U_RETURN_STRING(result); } off_t UFile::size(bool bstat) { U_TRACE(0, "UFile::size(%b)", bstat) U_INTERNAL_ASSERT_EQUALS(st_size, 0) if (bstat == false) readSize(); else { fstat(); # ifdef _MSWINDOWS_ st_ino = u_get_inode(fd); # endif U_INTERNAL_DUMP("st_ino = %llu", st_ino) if (S_ISDIR(st_mode)) U_RETURN(0); } U_RETURN(st_size); } // MEMORY MAPPED I/O char* UFile::shm_open(const char* name, uint32_t length) { U_TRACE(1, "UFile::shm_open(%S,%u)", name, length) U_INTERNAL_ASSERT_POINTER(name) U_INTERNAL_ASSERT_MAJOR(length, 0) // create/open POSIX shared memory object int _fd; // open file in read-write mode and create it if its not there, create the shared object with permissions for only the user to read and write #ifdef HAVE_SHM_OPEN _fd = U_SYSCALL(shm_open, "%S,%d,%d", name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); #else char shm_buffer_path[MAX_FILENAME_LEN]; (void) u__snprintf(shm_buffer_path, sizeof(shm_buffer_path), U_CONSTANT_TO_PARAM("/tmp%s"), name); _fd = U_SYSCALL(open, "%S,%d,%d", shm_buffer_path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); #endif if (LIKELY(_fd > 0)) { (void) U_SYSCALL(ftruncate, "%d,%u", _fd, length); // set the size of the shared memory object char* _ptr = (char*) U_SYSCALL(mmap, "%p,%u,%d,%d,%d,%I", U_NULLPTR, length, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); (void) U_SYSCALL(close, "%d", _fd); return _ptr; } return U_NULLPTR; } // unlink POSIX shared memory object void UFile::shm_unlink(const char* name) { U_TRACE(1, "UFile::shm_unlink(%S)", name) U_INTERNAL_ASSERT_POINTER(name) #ifdef HAVE_SHM_OPEN (void) U_SYSCALL(shm_unlink, "%S", name); #endif } // On linux platforms maps (but not reserves) 256+ Megabytes of virtual address space char* UFile::mmap_anon_huge(uint32_t* plength, int flags) { U_TRACE(1, "UFile::mmap_anon_huge(%p,%d,%u)", plength, flags) char* ptr; #ifdef U_LINUX if (nr_hugepages) { U_INTERNAL_DUMP("nr_hugepages = %ld rlimit_memfree = %u", nr_hugepages, rlimit_memfree) U_INTERNAL_ASSERT_EQUALS(rlimit_memfree, U_2M) # ifdef MAP_HUGE_1GB /* (since Linux 3.8) */ if (*plength >= U_1G) { uint32_t length = (*plength + U_1G_MASK) & ~U_1G_MASK; // NB: munmap() length of MAP_HUGETLB memory must be hugepage aligned... U_INTERNAL_ASSERT_EQUALS(length & U_1G_MASK, 0) U_DEBUG("We are going to allocate (%u GB - %u bytes) MAP_HUGE_1GB - nfree = %u flags = %B", length / U_1G, length, nfree, flags | U_MAP_ANON_HUGE | MAP_HUGE_1GB) ptr = (char*) U_SYSCALL(mmap, "%p,%u,%d,%d,%d,%I", U_MAP_ANON_HUGE_ADDR, length, PROT_READ | PROT_WRITE, flags | U_MAP_ANON_HUGE | MAP_HUGE_1GB, -1, 0); if (ptr != (char*)MAP_FAILED) { *plength = length; # ifdef HAVE_NUMA if ((flags & MAP_SHARED) == 0 && U_SYSCALL(mbind, "%p,%lu,%d,%p,%lu,%u", ptr, length, MPOL_PREFERRED, U_NULLPTR, 0UL, MPOL_MF_MOVE) == -1) { WARNING("unable to mbind memory; performance may suffer"); } # endif return ptr; } } # endif # ifdef MAP_HUGE_2MB /* (since Linux 3.8) */ uint32_t length = (*plength + U_2M_MASK) & ~U_2M_MASK; // NB: munmap() length of MAP_HUGETLB memory must be hugepage aligned... U_INTERNAL_ASSERT_EQUALS(length & U_2M_MASK, 0) U_DEBUG("We are going to allocate (%u MB - %u bytes) MAP_HUGE_2MB - nfree = %u flags = %B", length / (1024U*1024U), length, nfree, flags | U_MAP_ANON_HUGE | MAP_HUGE_2MB) ptr = (char*) U_SYSCALL(mmap, "%p,%u,%d,%d,%d,%I", U_MAP_ANON_HUGE_ADDR, length, PROT_READ | PROT_WRITE, flags | U_MAP_ANON_HUGE | MAP_HUGE_2MB, -1, 0); if (ptr != (char*)MAP_FAILED) { *plength = length; # ifdef HAVE_NUMA if ((flags & MAP_SHARED) == 0 && U_SYSCALL(mbind, "%p,%lu,%d,%p,%lu,%u", ptr, length, MPOL_PREFERRED, U_NULLPTR, 0UL, MPOL_MF_MOVE) == -1) { WARNING("unable to mbind memory; performance may suffer"); } # endif return ptr; } if (*plength < U_1G) { unsigned long vsz, rss; u_get_memusage(&vsz, &rss); U_WARNING("Cannot allocate %u bytes (%u MB) of memory MAP_HUGE_2MB - " "address space usage: %.2f MBytes - " "rss usage: %.2f MBytes", *plength, *plength / (1024U*1024U), (double)vsz / (1024.0 * 1024.0), (double)rss / (1024.0 * 1024.0)); nr_hugepages = 0; } # endif } #endif *plength = (*plength + U_PAGEMASK) & ~U_PAGEMASK; U_INTERNAL_ASSERT_EQUALS(*plength & U_PAGEMASK, 0) U_DEBUG("We are going to allocate (%u KB - %u bytes) - nfree = %u flags = %B", *plength / 1024U, *plength, nfree, flags) ptr = (char*) U_SYSCALL(mmap, "%p,%u,%d,%d,%d,%I", U_NULLPTR, *plength, PROT_READ | PROT_WRITE, flags, -1, 0); #ifdef HAVE_NUMA if ((flags & MAP_SHARED) == 0 && U_SYSCALL(mbind, "%p,%lu,%d,%p,%lu,%u", ptr, *plength, MPOL_PREFERRED, U_NULLPTR, 0UL, MPOL_MF_MOVE) == -1) { WARNING("unable to mbind memory; performance may suffer"); } #endif return ptr; } char* UFile::mmap(uint32_t* plength, int _fd, int prot, int flags, uint32_t offset) { U_TRACE(1, "UFile::mmap(%p,%d,%d,%d,%u)", plength, _fd, prot, flags, offset) U_INTERNAL_ASSERT_POINTER(plength) #ifdef U_LINUX # ifndef HAVE_ARCH64 U_INTERNAL_ASSERT_RANGE(1U, *plength, 3U * 1024U * 1024U * 1024U) // limit of linux system on 32bit # endif if (_fd != -1) #endif return (char*) U_SYSCALL(mmap, "%p,%u,%d,%d,%d,%I", U_NULLPTR, *plength, prot, flags, _fd, offset); U_INTERNAL_ASSERT_EQUALS(prot, PROT_READ | PROT_WRITE) if ((flags & MAP_SHARED) != 0) { U_INTERNAL_ASSERT_DIFFERS(flags & MAP_ANONYMOUS, 0) return mmap_anon_huge(plength, flags); } U_INTERNAL_ASSERT_DIFFERS(flags & MAP_PRIVATE, 0) char* _ptr; bool _abort = false; if (*plength >= rlimit_memalloc) // NB: we try to avoid strong swap pressure... { #if defined(U_LINUX) && (!defined(U_SERVER_CAPTIVE_PORTAL) || defined(ENABLE_THREAD)) try_from_file_system: #endif *plength = (*plength + U_PAGEMASK) & ~U_PAGEMASK; U_INTERNAL_ASSERT_EQUALS(*plength & U_PAGEMASK, 0) int fd = mkTemp(); if (fd != -1) { U_DEBUG("We are going to allocate from file system (%u KB - %u bytes)", *plength / 1024, *plength) _ptr = (fallocate(fd, *plength) ? (char*)U_SYSCALL(mmap, "%p,%u,%d,%d,%d,%I", U_NULLPTR, *plength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, fd, 0) : (char*)MAP_FAILED); close(fd); if (_ptr != (char*)MAP_FAILED) return _ptr; } if (_abort) { unsigned long vsz, rss; u_get_memusage(&vsz, &rss); U_ERROR("Cannot allocate %u bytes (%u KB) of memory - " "address space usage: %.2f MBytes - " "rss usage: %.2f MBytes", *plength, *plength / 1024, (double)vsz / (1024.0 * 1024.0), (double)rss / (1024.0 * 1024.0)); } } #if !defined(U_LINUX) || (defined(U_SERVER_CAPTIVE_PORTAL) && !defined(ENABLE_THREAD)) _ptr = (char*) U_SYSCALL(malloc, "%u", *plength); return _ptr; #else U_INTERNAL_DUMP("plength = %u nfree = %u pfree = %p", *plength, nfree, pfree) if (pfree == U_NULLPTR) { # ifdef DEBUG unsigned long vsz, rss; u_get_memusage(&vsz, &rss); U_DEBUG("We are going to allocate %u MB - " "address space usage: %.2f MBytes - " "rss usage: %.2f MBytes", rlimit_memalloc / (1024 * 1024), (double)vsz / (1024.0 * 1024.0), (double)rss / (1024.0 * 1024.0)) # endif nfree = rlimit_memalloc; # ifdef U_MEMALLOC_WITH_HUGE_PAGE pfree = mmap_anon_huge(&nfree, MAP_PRIVATE | U_MAP_ANON); # else pfree = (char*) U_SYSCALL(mmap, "%p,%u,%d,%d,%d,%I", U_NULLPTR, nfree, PROT_READ | PROT_WRITE, MAP_PRIVATE | U_MAP_ANON, -1, 0); # endif if (pfree == (char*)MAP_FAILED) { nfree = 0; pfree = U_NULLPTR; } } if (*plength > nfree) { _ptr = mmap_anon_huge(plength, MAP_PRIVATE | U_MAP_ANON); if (_ptr == (char*)MAP_FAILED) { _abort = true; goto try_from_file_system; } return _ptr; } # ifdef U_MEMALLOC_WITH_HUGE_PAGE if (nr_hugepages) { # ifdef MAP_HUGE_1GB if (*plength >= U_1G) { *plength = (*plength + U_1G_MASK) & ~U_1G_MASK; U_INTERNAL_ASSERT_EQUALS(*plength & U_1G_MASK, 0) } else # endif # ifdef MAP_HUGE_2MB { *plength = (*plength + U_2M_MASK) & ~U_2M_MASK; U_INTERNAL_ASSERT_EQUALS(*plength & U_2M_MASK, 0) } # endif } else # endif { *plength = (*plength + U_PAGEMASK) & ~U_PAGEMASK; U_INTERNAL_ASSERT_EQUALS(*plength & U_PAGEMASK, 0) } _ptr = pfree; nfree -= *plength; if (nfree > rlimit_memfree) pfree += *plength; else { pfree = U_NULLPTR; *plength += nfree; } U_INTERNAL_DUMP("plength = %u nfree = %u pfree = %p", *plength, nfree, pfree) return _ptr; #endif } // A file is mapped in multiples of the page size. For a file that is not a multiple of the page size, // the remaining memory is zeroed when mapped, and writes to that region are not written out to the file bool UFile::memmap(int prot, UString* str, off_t start, off_t count) { U_TRACE(0, "UFile::memmap(%d,%p,%I,%I)", prot, str, start, count) U_CHECK_MEMORY U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) U_INTERNAL_ASSERT_DIFFERS(fd, -1) U_INTERNAL_ASSERT_MAJOR(st_size, 0) #ifdef _MSWINDOWS_ U_INTERNAL_ASSERT(count <= st_size) // NB: don't allow mappings beyond EOF since Windows can't handle that POSIX like... #endif uint32_t resto = 0; if (count == 0) count = st_size; if (start) { resto = start & U_PAGEMASK; U_INTERNAL_DUMP("resto = %u", resto) U_INTERNAL_ASSERT_EQUALS(resto, start % PAGESIZE) start -= resto; count += resto; } U_INTERNAL_ASSERT_EQUALS(start % PAGESIZE, 0) // offset should be a multiple of the page size as returned by getpagesize(2) int flags = MAP_SHARED; #if defined(U_LINUX) && defined(MAP_POPULATE) // (since Linux 2.5.46) if (prot == PROT_READ) flags |= MAP_POPULATE; #endif if (map != (char*)MAP_FAILED) { munmap(map, map_size); map_size = 0; } map = (char*) U_SYSCALL(mmap, "%p,%I,%d,%d,%d,%I", U_NULLPTR, count, prot, flags, fd, start); if (map != (char*)MAP_FAILED) { map_size = count; if (str == U_NULLPTR) U_RETURN(true); count -= resto; U_INTERNAL_DUMP("count = %I U_STRING_MAX_SIZE = %u", count, U_STRING_MAX_SIZE) if ((uint64_t)count < U_STRING_MAX_SIZE) { str->mmap(map+resto, count); map = (char*)MAP_FAILED; // transfer the ownership to string U_RETURN(true); } U_WARNING("Sorry, I can't manage string size bigger than 4G...") // limit of UString } U_RETURN(false); } void UFile::msync(char* ptr, char* page, int flags) { U_TRACE(1, "UFile::msync(%p,%p,%d)", ptr, page, flags) U_INTERNAL_ASSERT(ptr >= page) uint32_t resto = (long)page & U_PAGEMASK; U_INTERNAL_DUMP("resto = %u", resto) U_INTERNAL_ASSERT_EQUALS(resto, (long)page % PAGESIZE) char* addr = page - resto; uint32_t length = ptr - addr; U_INTERNAL_ASSERT_EQUALS((long)addr & U_PAGEMASK, 0) // addr should be a multiple of the page size as returned by getpagesize(2) (void) U_SYSCALL(msync, "%p,%u,%d", addr, length, flags); } UString UFile::_getContent(bool bsize, bool brdonly, bool bmap) { U_TRACE(0, "UFile::_getContent(%b,%b,%b)", bsize, brdonly, bmap) U_INTERNAL_DUMP("fd = %d map = %p map_size = %I st_size = %I", fd, map, map_size, st_size) U_INTERNAL_ASSERT_DIFFERS(fd, -1) #if defined(DEBUG) && !defined(U_LINUX) && !defined(O_TMPFILE) U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) #endif # ifdef U_COVERITY_FALSE_POSITIVE if (fd <= 0) return UString::getStringNull(); # endif if (bsize) readSize(); if (st_size) { if (bmap || st_size >= (off_t)(4L * PAGESIZE)) { int prot = PROT_READ; if (brdonly == false) prot |= PROT_WRITE; UString fileContent; (void) memmap(prot, &fileContent, 0, st_size); U_RETURN_STRING(fileContent); } UString fileContent(st_size); char* ptr = fileContent.data(); ssize_t value = U_SYSCALL(pread, "%d,%p,%u,%u", fd, ptr, st_size, 0); if (value < 0L) value = 0L; ptr[value] = '\0'; // NB: in this way we can use the UString method data()... fileContent.size_adjust(value); U_RETURN_STRING(fileContent); } return UString::getStringNull(); } UString UFile::getContent(bool brdonly, bool bstat, bool bmap) { U_TRACE(0, "UFile::getContent(%b,%b,%b)", brdonly, bstat, bmap) if (isOpen() == false && open(brdonly ? O_RDONLY : O_RDWR) == false) { return UString::getStringNull(); } UString fileContent; if (st_size || size(bstat)) { fileContent = _getContent(false, brdonly, bmap); } UFile::close(); U_RETURN_STRING(fileContent); } UString UFile::contentOf(const UString& pathname, const UString& pinclude) { U_TRACE(0, "UFile::contentOf(%V,%V)", pathname.rep, pinclude.rep) UFile file(pathname); if (file.open()) return file.getContent(); if (pinclude) { UString x(U_CAPACITY); (void) x.append(pinclude); x.push_back('/'); (void) x.append(UStringExt::basename(pathname)); file.setPath(x); if (file.open()) return file.getContent(); } return UString::getStringNull(); } void UFile::printf(const char* format, uint32_t fmt_size, ...) { U_TRACE(0, "UFile::printf(%.*S,%u)", fmt_size, format, fmt_size) char buffer[8196]; uint32_t bytes_to_write; va_list argp; va_start(argp, fmt_size); bytes_to_write = u__vsnprintf(buffer, U_CONSTANT_SIZE(buffer), format, fmt_size, argp); va_end(argp); buffer[bytes_to_write++] = '\n'; ssize_t bytes_written = U_SYSCALL(write, "%d,%p,%u", fd, buffer, bytes_to_write); if (bytes_written != (ssize_t)bytes_to_write) { U_WARNING("write data of size %u is %s", bytes_to_write, (bytes_written == -1 ? "failed" : "partial")); } } bool UFile::creatForWrite(int flags, bool bmkdirs) { U_TRACE(1, "UFile::creatForWrite(%d,%b)", flags, bmkdirs) U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) bool esito = isOpen(); if (esito == false) { esito = creat(flags, PERM_FILE); if (esito == false && bmkdirs) { // Make any missing parent directories for each directory argument char* ptr = (char*) strrchr(path_relativ, '/'); U_INTERNAL_DUMP("ptr = %S", ptr) if (ptr) { char buffer[U_PATH_MAX]; uint32_t len = ptr - path_relativ; U_MEMCPY(buffer, path_relativ, len); buffer[len] = '\0'; if (mkdirs(buffer)) esito = creat(flags, PERM_FILE); } } } U_RETURN(esito); } bool UFile::write(const char* data, uint32_t sz, int flags, bool bmkdirs) { U_TRACE(0, "UFile::write(%.*S,%u,%d,%b)", sz, data, sz, flags, bmkdirs) bool esito = false; if (creatForWrite(flags, bmkdirs) && sz) { if (sz <= PAGESIZE) esito = UFile::write(fd, data, sz); else { uint32_t offset = ((flags & O_APPEND) != 0 ? size() : 0); esito = fallocate(offset + sz); if (esito == false) { readSize(); U_WARNING("No more space on disk for requested size %u - acquired only %u bytes", offset + sz, st_size); sz = (st_size > offset ? st_size - offset : 0); } if (sz && memmap(PROT_READ | PROT_WRITE, U_NULLPTR, offset, st_size)) { U_MEMCPY(map + offset, data, sz); munmap(); } } } U_RETURN(esito); } bool UFile::write(const struct iovec* iov, int n, int flags, bool bmkdirs) { U_TRACE(0, "UFile::write(%p,%d,%d,%b)", iov, n, flags, bmkdirs) if (creatForWrite(flags, bmkdirs) && writev(iov, n)) { U_RETURN(true); } U_RETURN(false); } bool UFile::writeTo(const UString& path, const char* data, uint32_t sz, int flags, bool bmkdirs) { U_TRACE(0, "UFile::writeTo(%V,%.*S,%u,%d,%b)", path.rep, sz, data, sz, flags, bmkdirs) UFile tmp(path); bool result = tmp.write(data, sz, flags, bmkdirs); if (tmp.isOpen()) tmp.close(); U_RETURN(result); } bool UFile::writeTo(const UString& path, const struct iovec* iov, int n, int flags, bool bmkdirs) { U_TRACE(0, "UFile::writeTo(%V,%p,%d,%d,%b)", path.rep, iov, n, flags, bmkdirs) UFile tmp(path); bool result = tmp.write(iov, n, flags, bmkdirs); if (tmp.isOpen()) tmp.close(); U_RETURN(result); } bool UFile::writeToTmp(const UString& data, int flags, const char* format, uint32_t fmt_size, ...) { U_TRACE(0+256, "UFile::writeToTmp(%V,%d,%.*S,%u)", data.rep, flags, fmt_size, format, fmt_size) U_INTERNAL_ASSERT(data) UString path((unsigned char*)U_CONSTANT_TO_PARAM("/tmp/"), 200U); va_list argp; va_start(argp, fmt_size); path.vsnprintf_add(format, fmt_size, argp); va_end(argp); return writeTo(path, data, flags, false); } bool UFile::writeToTmp(const char* data, uint32_t sz, int flags, const char* format, uint32_t fmt_size, ...) { U_TRACE(0+256, "UFile::writeToTmp(%.*S,%u,%d,%.*S,%u)", sz, data, sz, flags, fmt_size, format, fmt_size) bool result = false; if (sz) { UString path((unsigned char*)U_CONSTANT_TO_PARAM("/tmp/"), 200U); va_list argp; va_start(argp, fmt_size); path.vsnprintf_add(format, fmt_size, argp); va_end(argp); result = writeTo(path, data, sz, flags, false); } U_RETURN(result); } bool UFile::writeToTmp(const struct iovec* iov, int n, int flags, const char* format, uint32_t fmt_size, ...) { U_TRACE(0+256, "UFile::writeToTmp(%p,%d,%d,%.*S,%u)", iov, n, flags, fmt_size, format, fmt_size) bool result = false; if (n) { UString path((unsigned char*)U_CONSTANT_TO_PARAM("/tmp/"), 200U); va_list argp; va_start(argp, fmt_size); path.vsnprintf_add(format, fmt_size, argp); va_end(argp); result = writeTo(path, iov, n, flags, false); } U_RETURN(result); } UString UFile::contentToWrite(const UString& _pathname, uint32_t sz) { U_TRACE(0, "UFile::contentToWrite(%V,%u)", _pathname.rep, sz) setPath(_pathname); if (creat(O_RDWR) && fallocate(sz)) { UString fileContent; (void) memmap(PROT_READ | PROT_WRITE, &fileContent, 0, sz); U_RETURN_STRING(fileContent); } return UString::getStringNull(); } bool UFile::lock(int fd, short l_type, uint32_t start, uint32_t len) { U_TRACE(1, "UFile::lock(%d,%d,%u,%u)", fd, l_type, start, len) U_INTERNAL_ASSERT_DIFFERS(fd, -1) /** * Advisory file segment locking data type - information passed to system by user */ #if defined(__OSX__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__UNIKERNEL__) /** * struct flock { * off_t l_start; // starting offset * off_t l_len; // len = 0 means until end of file * pid_t l_pid; // lock owner * short l_type; // lock type: read/write, etc. * short l_whence; // type of l_start * }; */ struct flock flock = { start, len, u_pid, SEEK_SET, l_type }; #else /** * struct flock { * short l_type; // Type of lock: F_RDLCK, F_WRLCK, F_UNLCK * short l_whence; // How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END * off_t l_start; // Starting offset for lock * off_t l_len; // Number of bytes to lock * pid_t l_pid; // PID of process blocking our lock (F_GETLK only) * }; */ struct flock flock = { l_type, SEEK_SET, start, len, u_pid }; #endif /** * F_SETLK: Acquire a lock (when l_type is F_RDLCK or F_WRLCK) or release a lock (when l_type is F_UNLCK) on the * bytes specified by the l_whence, l_start, and l_len fields of lock. If a conflicting lock is held by another * process, this call returns -1 and sets errno to EACCES or EAGAIN. * * F_SETLKW: As for F_SETLK, but if a conflicting lock is held on the file, then wait for that lock to be released. * If a signal is caught while waiting, then the call is interrupted and (after the signal handler has returned) * returns immediately (with return value -1 and errno set to EINTR). * --------------------------------------------------------------------------------------------------------------------- * #ifndef F_GETLKP * #define F_GETLKP F_GETLK * #define F_SETLKP F_SETLK * #define F_SETLKPW F_SETLKW * #endif * * F_GETLKP - test whether a lock is able to be applied * F_SETLKP - attempt to set a file-private lock * F_SETLKPW - attempt to set a file-private lock and block until able to do so * --------------------------------------------------------------------------------------------------------------------- */ if (U_SYSCALL(fcntl, "%d,%d,%p", fd, F_SETLK, &flock) != -1) U_RETURN(true); // F_SETLKW U_RETURN(false); } bool UFile::ftruncate(off_t n) { U_TRACE(1, "UFile::ftruncate(%I)", n) U_CHECK_MEMORY U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) U_INTERNAL_ASSERT_DIFFERS(fd, -1) #if defined(__CYGWIN__) || defined(_MSWINDOWS_) U_INTERNAL_ASSERT_EQUALS(map, (char*)MAP_FAILED) #endif #ifdef U_COVERITY_FALSE_POSITIVE if (fd <= 0) U_RETURN(false); #endif if (map != (char*)MAP_FAILED && map_size < n) { off_t _map_size = n * 2; char* _map = UFile::mremap(map, map_size, _map_size, MREMAP_MAYMOVE); if (_map == (char*)MAP_FAILED) U_RETURN(false); map = _map; map_size = _map_size; } if (U_SYSCALL(ftruncate, "%d,%I", fd, n) == 0) { st_size = n; U_RETURN(true); } U_RETURN(false); } bool UFile::fallocate(int fd, uint32_t n) { U_TRACE(1, "UFile::fallocate(%d,%u)", fd, n) U_INTERNAL_ASSERT_DIFFERS(fd, -1) #ifdef U_COVERITY_FALSE_POSITIVE if (fd <= 0) U_RETURN(false); #endif #ifdef FALLOCATE_IS_SUPPORTED if (U_SYSCALL(fallocate, "%d,%d,%u,%u", fd, 0, 0, n) == 0) U_RETURN(true); U_INTERNAL_DUMP("errno = %d", errno) if (errno != EOPNOTSUPP) U_RETURN(false); #endif if (U_SYSCALL(ftruncate, "%d,%u", fd, n) == 0) U_RETURN(true); U_RETURN(false); } UString UFile::getSysContent(const char* name) { U_TRACE(0, "UFile::getSysContent(%S)", name) UString fileContent(U_CAPACITY); int fd = open(name, O_RDONLY, PERM_FILE); if (fd != -1) { UServices::readEOF(fd, fileContent); U_ASSERT_EQUALS(UServices::read(fd, fileContent), false) close(fd); } U_RETURN_STRING(fileContent); } long UFile::getSysParam(const char* name) { U_TRACE(0, "UFile::getSysParam(%S)", name) long value = -1; int fd = open(name, O_RDONLY, PERM_FILE); if (fd != -1) { char buffer[32]; ssize_t bytes_read = U_SYSCALL(read, "%d,%p,%u", fd, buffer, U_CONSTANT_SIZE(buffer)); if (bytes_read > 0) { U_INTERNAL_ASSERT_MINOR((uint32_t)bytes_read, sizeof(buffer)) buffer[bytes_read] = '\0'; value = strtol(buffer, U_NULLPTR, 10); } close(fd); } U_RETURN(value); } long UFile::setSysParam(const char* name, long value, bool force) { U_TRACE(0, "UFile::setSysParam(%S,%ld,%b)", name, value, force) long old_value = -1; int fd = open(name, O_RDWR, PERM_FILE); if (fd != -1) { char buffer[32]; ssize_t bytes_read = U_SYSCALL(read, "%d,%p,%u", fd, buffer, U_CONSTANT_SIZE(buffer)); if (bytes_read > 0) { U_INTERNAL_ASSERT_MINOR((uint32_t)bytes_read, sizeof(buffer)) buffer[bytes_read] = '\0'; old_value = strtol(buffer, U_NULLPTR, 10); if (force || old_value < value) { char* ptr = buffer; # if SIZEOF_LONG == 4 if (pwrite(fd, buffer, u_num2str32s(value, ptr) - ptr, 0) > 0) old_value = value; # elif !defined(U_COVERITY_FALSE_POSITIVE) // (INTEGER_OVERFLOW) if (pwrite(fd, buffer, u_num2str64s(value, ptr) - ptr, 0) > 0) old_value = value; # endif } } close(fd); } U_RETURN(old_value); } bool UFile::pread(void* buf, uint32_t count, uint32_t offset) { U_TRACE(0, "UFile::pread(%p,%u,%u)", buf, count, offset) U_CHECK_MEMORY U_INTERNAL_ASSERT_DIFFERS(fd, -1) #if defined(DEBUG) && !defined(U_LINUX) && !defined(O_TMPFILE) U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) #endif #ifdef U_COVERITY_FALSE_POSITIVE if (fd <= 0) U_RETURN(false); #endif if (pwrite(fd, buf, count, offset)) U_RETURN(true); U_RETURN(false); } bool UFile::pwrite(const void* _buf, uint32_t count, uint32_t offset) { U_TRACE(0, "UFile::pwrite(%p,%u,%u)", _buf, count, offset) U_CHECK_MEMORY U_INTERNAL_ASSERT_DIFFERS(fd, -1) #if defined(DEBUG) && !defined(U_LINUX) && !defined(O_TMPFILE) U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) #endif #ifdef U_COVERITY_FALSE_POSITIVE if (fd <= 0) U_RETURN(false); #endif if (pwrite(fd, _buf, count, offset)) U_RETURN(true); U_RETURN(false); } int UFile::setBlocking(int _fd, int flags, bool block) { U_TRACE(1, "UFile::setBlocking(%d,%d,%b)", _fd, flags, block) U_INTERNAL_ASSERT_DIFFERS(_fd, -1) /** * ------------------------------------------------------ * #define O_RDONLY 00 * #define O_WRONLY 01 * #define O_RDWR 02 * #define O_ACCMODE 0003 * #define O_CREAT 0100 // not fcntl * #define O_EXCL 0200 // not fcntl * #define O_NOCTTY 0400 // not fcntl * #define O_TRUNC 01000 // not fcntl * #define O_APPEND 02000 * #define O_NONBLOCK 04000 * #define O_SYNC 010000 * #define O_ASYNC 020000 * #define O_DIRECT 040000 // Direct disk access * #define O_DIRECTORY 0200000 // Must be a directory * #define O_NOFOLLOW 0400000 // Do not follow links * #define O_NOATIME 01000000 // Do not set atime * #define O_CLOEXEC 02000000 // Set close_on_exec * ------------------------------------------------------ * #define O_FSYNC O_SYNC * #define O_NDELAY O_NONBLOCK * ------------------------------------------------------ */ bool blocking = isBlocking(_fd, flags); // actual state is blocking...? // ---------------------------------------------------------------------------------------- // flags & ~O_NONBLOCK: read() and write() block ( normal case) // flags | O_NONBLOCK: read() and write() NOT block (special case) // ---------------------------------------------------------------------------------------- if (block != blocking) { flags = (blocking ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK)); U_INTERNAL_DUMP("flags = %B", flags) (void) U_SYSCALL(fcntl, "%d,%d,%d", _fd, F_SETFL, flags); } U_RETURN(flags); } // resolv simbolic link and/or reference to "./" and "../" UString UFile::getRealPath(const char* path, bool brelativ) { U_TRACE(1, "UFile::getRealPath(%S,%b)", path, brelativ) UString buffer(U_PATH_MAX); char* result = U_SYSCALL(realpath, "%S,%p", path, buffer.data()); if (result) { buffer.size_adjust(); if (brelativ == false) U_RETURN_STRING(buffer); uint32_t len = buffer.size(); void* ptr = u_getPathRelativ(buffer.data(), &len); UString x((void*)ptr, len); U_RETURN_STRING(x); } return UString::getStringNull(); } // create a unique temporary file int UFile::mkTemp() { U_TRACE(1, "UFile::mkTemp()") /** * O_TMPFILE is a new open(2)/openat(2) flag that makes easier the creation of secure temporary files. Files opened with the O_TMPFILE * flag are created but they are not visible in the filesystem. And as soon as they are closed, they get deleted - just as a file you * would have opened and unlinked * * http://kernelnewbies.org/Linux_3.11#head-8be09d59438b31c2a724547838f234cb33c40357 */ #if defined(U_LINUX) && defined(O_TMPFILE) int fd = U_SYSCALL(open, "%S,%d,%d", u_tmpdir, O_TMPFILE | O_RDWR, PERM_FILE); #else char _pathname[U_PATH_MAX+1]; // The last six characters of template must be XXXXXX and these are replaced with a string that makes the filename unique (void) u__snprintf(_pathname, U_PATH_MAX, U_CONSTANT_TO_PARAM("%s/tmpXXXXXX"), u_tmpdir); mode_t old_mode = U_SYSCALL(umask, "%d", 077); // Create file with restrictive permissions errno = 0; // mkstemp may not set it on error int fd = U_SYSCALL(mkstemp, "%S", U_PATH_CONV(_pathname)); (void) U_SYSCALL(unlink, "%S", U_PATH_CONV(_pathname)); (void) U_SYSCALL(umask, "%d", old_mode); #endif #ifdef DEBUG if (fd != -1) { U_ASSERT( lock(fd) && unlock(fd)) } #endif U_RETURN(fd); } // mkdtemp - create a unique temporary directory bool UFile::mkdtemp(UString& _template) { U_TRACE(1, "UFile::mkdtemp(%V)", _template.rep) errno = 0; // mkdtemp may not set it on error char* modified = U_SYSCALL(mkdtemp, "%S", U_PATH_CONV((char*)_template.c_str())); if (modified) { // NB: c_str() in replace use a new string... if (modified != _template.data()) (void) _template.assign(modified); U_RETURN(true); } U_RETURN(false); } // Make any missing parent directories for each directory argument bool UFile::mkdirs(const char* path, mode_t mode) { U_TRACE(1, "UFile::mkdirs(%S,%d)", path, mode) if (_mkdir(path, mode)) U_RETURN(true); U_INTERNAL_DUMP("errno = %d", errno) if (errno == ENOENT) { char* ptr = (char*) strrchr(path, '/'); U_INTERNAL_DUMP("ptr = %S", ptr) if (ptr) { char buffer[U_PATH_MAX]; uint32_t len = ptr - path; U_MEMCPY(buffer, path, len); buffer[len] = '\0'; bool result = mkdirs(buffer, mode) && _mkdir( path, mode); U_RETURN(result); } } U_RETURN(false); } bool UFile::rmdir(const UString& path, bool remove_all) { U_TRACE(1, "UFile::rmdir(%V,%b)", path.rep, remove_all) const char* ptr = path.data(); U_INTERNAL_ASSERT(path.isNullTerminated()) if (U_SYSCALL(rmdir, "%S", U_PATH_CONV(ptr)) == -1) { if (remove_all && # ifdef _MSWINDOWS_ (errno == ENOTEMPTY || errno == EACCES)) # else (errno == ENOTEMPTY)) # endif { bool result; UString file; UDirWalk dirwalk(path); UVector vec(256); for (uint32_t i = 0, n = dirwalk.walk(vec); i < n; ++i) { file = vec[i]; U_ASSERT_DIFFERS(file, path) U_INTERNAL_ASSERT(file.isNullTerminated()) if (UFile::_unlink(file.data()) == false && # ifdef _MSWINDOWS_ (errno == EISDIR || errno == EPERM || errno == EACCES)) # else (errno == EISDIR || errno == EPERM)) # endif { if (UFile::rmdir(file, true) == false) U_RETURN(false); } } result = UFile::rmdir(path, false); U_RETURN(result); } U_RETURN(false); } U_RETURN(true); } // If path includes more than one pathname component, remove it, then strip the last component // and remove the resulting directory, etc., until all components have been removed. // The pathname component must be empty... bool UFile::rmdirs(const UString& path, bool remove_all) { U_TRACE(1, "UFile::rmdirs(%V,%b)", path.rep, remove_all) bool result = rmdir(path, remove_all); if (result) { UString newpath = UStringExt::dirname(path); if (newpath != *UString::str_point) result = rmdirs(newpath.copy()); } U_RETURN(result); } bool UFile::_rename(const char* newpath) { U_TRACE(0, "UFile::_rename(%S)", newpath) U_CHECK_MEMORY U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_ASSERT_POINTER(path_relativ) bool result = UFile::_rename(path_relativ, newpath); if (result) { path_relativ = (char*) newpath; path_relativ_len = u__strlen(newpath, __PRETTY_FUNCTION__); U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_RETURN(true); } U_RETURN(false); } void UFile::substitute(UFile& file) { U_TRACE(1, "UFile::substitute(%p)", &file) U_INTERNAL_ASSERT_POINTER(cwd_save) U_INTERNAL_DUMP("u_cwd(%u) = %S", u_cwd_len, u_cwd) U_INTERNAL_DUMP("cwd_save(%u) = %S", cwd_save_len, cwd_save) U_INTERNAL_DUMP("path_relativ(%u) = %.*S", path_relativ_len, path_relativ_len, path_relativ) U_INTERNAL_DUMP("file.path_relativ = %.*S", file.path_relativ_len, file.path_relativ) U_INTERNAL_ASSERT_EQUALS(strcmp(path_relativ, file.path_relativ), 0) if (cwd_save_len) (void) U_SYSCALL(chdir, "%S", U_PATH_CONV(u_cwd)); if (fd != -1) { UFile::fsync(); UFile::close(); } if (map != (char*)MAP_FAILED) munmap(); fd = file.fd; map = file.map; st_size = file.st_size; map_size = file.map_size; file.reset(); U_INTERNAL_DUMP("fd = %d map = %p map_size = %I st_size = %I", fd, map, map_size, st_size) if (fd != -1) UFile::fsync(); } // MIME TYPE const char* UFile::getMimeType(const char* suffix, int* pmime_index) { U_TRACE(0, "UFile::getMimeType(%S,%p)", suffix, pmime_index) const char* content_type; if (suffix) content_type = u_get_mimetype(suffix, pmime_index); else { suffix = u_getsuffix(path_relativ, path_relativ_len); U_INTERNAL_DUMP("suffix = %.*S", suffix ? (path_relativ_len-(suffix-path_relativ)-1) : 0, suffix+1) content_type = (suffix ? u_get_mimetype(suffix+1, pmime_index) : U_NULLPTR); } #ifdef DEBUG if (pmime_index) U_INTERNAL_DUMP("mime_index(%d) = %C", *pmime_index, *pmime_index) #endif #ifdef USE_LIBMAGIC if (pmime_index && map != (char*)MAP_FAILED && u_is_js ( *pmime_index) == false && u_is_css( *pmime_index) == false && u__isdigit(*pmime_index) == false) // NB: check for dynamic page... { const char* ctype = UMagic::getType(map, map_size).data(); if (ctype) content_type = ctype; } #endif if (content_type == U_NULLPTR) { # ifdef USE_LIBMAGIC if (map != (char*)MAP_FAILED) content_type = UMagic::getType(map, map_size).data(); if (content_type == U_NULLPTR) # endif content_type = "application/octet-stream"; } U_INTERNAL_ASSERT_POINTER(content_type) U_RETURN(content_type); } // DEBUG #if defined(U_STDCPP_ENABLE) && defined(DEBUG) const char* UFile::dump(bool _reset) const { *UObjectIO::os << "fd " << fd << '\n' << "map " << (void*)map << '\n' << "st_size " << st_size << '\n' << "path_relativ " << '"'; if (path_relativ) { *UObjectIO::os << path_relativ; } *UObjectIO::os << "\"\n" << "path_relativ_len " << path_relativ_len << '\n' << "pathname (UString " << (void*)&pathname << ')'; if (_reset) { UObjectIO::output(); return UObjectIO::buffer_output; } return U_NULLPTR; } #endif