mirror of https://github.com/vitalif/tdpkg
301 lines
7.2 KiB
C
301 lines
7.2 KiB
C
/*
|
|
Copyright © 2010 Luca Bruno
|
|
|
|
This file is part of tdpkg.
|
|
|
|
tdpkg is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
tdpkg is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with tdpkg. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <dlfcn.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "cache.h"
|
|
|
|
typedef int (*open_t)(const char *path, int oflag, ...);
|
|
static int _tdpkg_open (const char *path, int oflag, int mode);
|
|
|
|
/* real functions */
|
|
static open_t realopen;
|
|
static open_t realopen64;
|
|
static int (*real__fxstat)(int ver, int fd, struct stat* buf);
|
|
static int (*real__fxstat64)(int ver, int fd, struct stat64* buf);
|
|
static ssize_t (*realread)(int fildes, void *buf, size_t nbyte);
|
|
static int (*realclose)(int fd);
|
|
static int (*realrename)(const char *old, const char *new);
|
|
static int (*realunlink)(const char* pathname);
|
|
|
|
/* handle open() of dpkg/src/filesdb.c */
|
|
#define FAKE_FD 4321
|
|
static struct OpenState
|
|
{
|
|
int fd;
|
|
char* contents;
|
|
size_t len;
|
|
size_t read;
|
|
char *fn;
|
|
} open_state;
|
|
|
|
static int cache_initialized;
|
|
|
|
/* called once library is preloaded */
|
|
void _init (void)
|
|
{
|
|
static int initialized = 0;
|
|
if (!initialized)
|
|
initialized = 1;
|
|
else
|
|
return;
|
|
|
|
/* use absolute path for current library */
|
|
char *ld_preload = getenv ("LD_PRELOAD");
|
|
if (ld_preload)
|
|
{
|
|
char *abspath = realpath (ld_preload, NULL);
|
|
if (abspath)
|
|
{
|
|
setenv ("LD_PRELOAD", abspath, 1);
|
|
free (abspath);
|
|
}
|
|
}
|
|
|
|
memset (&open_state, '\0', sizeof (open_state));
|
|
open_state.fd = -1;
|
|
|
|
realopen = dlsym (RTLD_NEXT, "open");
|
|
realopen64 = dlsym (RTLD_NEXT, "open64");
|
|
real__fxstat = dlsym (RTLD_NEXT, "__fxstat");
|
|
real__fxstat64 = dlsym (RTLD_NEXT, "__fxstat64");
|
|
realread = dlsym (RTLD_NEXT, "read");
|
|
realclose = dlsym (RTLD_NEXT, "close");
|
|
realrename = dlsym (RTLD_NEXT, "rename");
|
|
realunlink = dlsym (RTLD_NEXT, "unlink");
|
|
|
|
if (!tdpkg_cache_initialize ())
|
|
cache_initialized = 1;
|
|
else
|
|
fprintf (stderr, "tdpkg: cache initialization failed, no wrapping\n");
|
|
}
|
|
|
|
static int
|
|
is_list_file (const char* path)
|
|
{
|
|
return strstr (path, "/var/lib/dpkg/info/") && strstr (path, ".list");
|
|
}
|
|
|
|
int
|
|
rename (const char *old, const char *new)
|
|
{
|
|
int result = realrename (old, new);
|
|
if (!result && is_list_file (new))
|
|
{
|
|
if (tdpkg_cache_write_filename (new))
|
|
{
|
|
fprintf (stderr, "tdpkg: can't update cache for file %s, no wrapping\n", new);
|
|
tdpkg_cache_finalize ();
|
|
cache_initialized = 0;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int
|
|
unlink (const char* pathname)
|
|
{
|
|
int result = realunlink (pathname);
|
|
if (!result && is_list_file (pathname))
|
|
{
|
|
if (tdpkg_cache_delete_filename (pathname))
|
|
{
|
|
fprintf (stderr, "tdpkg: can't delete %s from cache, no wrapping\n", pathname);
|
|
tdpkg_cache_finalize ();
|
|
cache_initialized = 0;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
_tdpkg_open (const char *path, int oflag, int mode)
|
|
{
|
|
if (!cache_initialized)
|
|
return realopen (path, oflag, mode);
|
|
|
|
if (!is_list_file (path) || (oflag & O_RDONLY) != O_RDONLY)
|
|
return realopen (path, oflag, mode);
|
|
|
|
// sometimes dpkg calls FIGETBSZ first time, open_state.fn fixes it
|
|
if (open_state.fd >= 0 && open_state.fn != path)
|
|
{
|
|
#ifdef TDPKG_INFO
|
|
fprintf (stderr, "tdpkg: nested open(%s, %d, %d) detected, no wrapping\n", path, oflag, mode);
|
|
#endif
|
|
return realopen (path, oflag, mode);
|
|
}
|
|
|
|
open_state.fn = (char*)path;
|
|
open_state.contents = tdpkg_cache_read_filename (path);
|
|
if (!open_state.contents)
|
|
{
|
|
#ifdef TDPKG_INFO
|
|
fprintf (stderr, "tdpkg: file %s not up-to-date in cache, rebuild cache\n", path);
|
|
#endif
|
|
if (tdpkg_cache_rebuild ())
|
|
{
|
|
fprintf (stderr, "tdpkg: can't rebuild cache, no wrapping\n");
|
|
tdpkg_cache_finalize ();
|
|
cache_initialized = 0;
|
|
return realopen (path, oflag, mode);
|
|
}
|
|
|
|
open_state.contents = tdpkg_cache_read_filename (path);
|
|
if (!open_state.contents)
|
|
{
|
|
fprintf (stderr, "tdpkg: path %s not being indexed, no wrapping\n", path);
|
|
return realopen (path, oflag, mode);
|
|
}
|
|
}
|
|
|
|
open_state.fd = FAKE_FD;
|
|
open_state.len = strlen (open_state.contents);
|
|
return open_state.fd;
|
|
}
|
|
|
|
int
|
|
open (const char *path, int oflag, ...)
|
|
{
|
|
va_list ap;
|
|
va_start (ap, oflag);
|
|
int mode = va_arg (ap, int);
|
|
va_end (ap);
|
|
return _tdpkg_open (path, oflag, mode);
|
|
}
|
|
|
|
int
|
|
open64 (const char *path, int oflag, ...)
|
|
{
|
|
va_list ap;
|
|
va_start (ap, oflag);
|
|
int mode = va_arg (ap, int);
|
|
va_end (ap);
|
|
return _tdpkg_open (path, oflag, mode);
|
|
}
|
|
|
|
int
|
|
__fxstat (int ver, int fd, struct stat* buf)
|
|
{
|
|
if (!cache_initialized)
|
|
return real__fxstat (ver, fd, buf);
|
|
|
|
if (open_state.fd < 0)
|
|
return real__fxstat (ver, fd, buf);
|
|
|
|
if (open_state.fd != fd)
|
|
{
|
|
#ifdef TDPKG_INFO
|
|
fprintf (stderr, "tdpkg: nested __fxstat(%d) detected, no wrapping\n", fd);
|
|
#endif
|
|
return real__fxstat (ver, fd, buf);
|
|
}
|
|
|
|
buf->st_size = open_state.len;
|
|
buf->st_mode = S_IFREG;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
__fxstat64 (int ver, int fd, struct stat64* buf)
|
|
{
|
|
if (!cache_initialized)
|
|
return real__fxstat64 (ver, fd, buf);
|
|
|
|
if (open_state.fd < 0)
|
|
return real__fxstat64 (ver, fd, buf);
|
|
|
|
if (open_state.fd != fd)
|
|
{
|
|
#ifdef TDPKG_INFO
|
|
fprintf (stderr, "tdpkg: nested __fxstat64(%d) detected, no wrapping\n", fd);
|
|
#endif
|
|
return real__fxstat64 (ver, fd, buf);
|
|
}
|
|
|
|
buf->st_size = open_state.len;
|
|
buf->st_mode = S_IFREG;
|
|
return 0;
|
|
}
|
|
|
|
ssize_t
|
|
read (int fildes, void *buf, size_t nbyte)
|
|
{
|
|
if (!cache_initialized)
|
|
return realread (fildes, buf, nbyte);
|
|
|
|
if (open_state.fd < 0)
|
|
return realread (fildes, buf, nbyte);
|
|
|
|
if (open_state.fd != fildes)
|
|
{
|
|
#ifdef TDPKG_INFO
|
|
fprintf (stderr, "tdpkg: nested read(%d) detected, no wrapping\n", fildes);
|
|
#endif
|
|
return realread (fildes, buf, nbyte);
|
|
}
|
|
|
|
if (open_state.read > open_state.len)
|
|
{
|
|
#ifdef TDPKG_INFO
|
|
fprintf (stderr, "tdpkg: useless read(%d) detected, returning 0\n", open_state.fd);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
size_t nowread = (open_state.len-open_state.read) > nbyte ? nbyte : (open_state.len-open_state.read);
|
|
memcpy (buf, open_state.contents+open_state.read, nowread);
|
|
open_state.read += nowread;
|
|
return nowread;
|
|
}
|
|
|
|
int
|
|
close (int fd)
|
|
{
|
|
if (!cache_initialized)
|
|
return realclose (fd);
|
|
|
|
if (open_state.fd < 0)
|
|
return realclose (fd);
|
|
|
|
if (open_state.fd != fd)
|
|
{
|
|
#ifdef TDPKG_INFO
|
|
fprintf (stderr, "tdpkg: close() on unknown fd %d, no wrapping\n", fd);
|
|
#endif
|
|
return realclose (fd);
|
|
}
|
|
|
|
open_state.fd = -1;
|
|
if (open_state.contents)
|
|
free (open_state.contents);
|
|
open_state.contents = NULL;
|
|
open_state.len = 0;
|
|
open_state.read = 0;
|
|
return 0;
|
|
}
|