mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-10-05 19:18:01 +08:00
1748 lines
45 KiB
C++
1748 lines
45 KiB
C++
// ============================================================================
|
|
//
|
|
// = LIBRARY
|
|
// ULib - c++ library
|
|
//
|
|
// = FILENAME
|
|
// file.cpp
|
|
//
|
|
// = AUTHOR
|
|
// Stefano Casazza
|
|
//
|
|
// ============================================================================
|
|
|
|
#include <ulib/file.h>
|
|
#include <ulib/tokenizer.h>
|
|
#include <ulib/utility/services.h>
|
|
#include <ulib/utility/dir_walk.h>
|
|
#include <ulib/utility/string_ext.h>
|
|
|
|
#ifdef USE_LIBMAGIC
|
|
# include <ulib/magic/magic.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NUMA
|
|
# include <numaif.h>
|
|
#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<UString> 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
|