mud/fluffos-2.23-ds03/file.c
2020-09-06 05:43:07 -07:00

1201 lines
29 KiB
C

/*
* file: file.c
* description: handle all file based efuns
*/
#include "std.h"
#include "file.h"
#include "comm.h"
#include "lex.h"
#include "md.h"
#include "port.h"
#include "master.h"
#ifdef PACKAGE_COMPRESS
#include <zlib.h>
#endif
int legal_path (const char *);
static int match_string (char *, char *);
static int copy (const char *from, const char *to);
static int do_move (const char *from, const char *to, int flag);
static int CDECL pstrcmp (CONST void *, CONST void *);
static int CDECL parrcmp (CONST void *, CONST void *);
static void encode_stat (svalue_t *, int, char *, struct stat *);
#define MAX_LINES 50
/*
* These are used by qsort in get_dir().
*/
static int CDECL pstrcmp (CONST void * p1, CONST void * p2)
{
svalue_t *x = (svalue_t *)p1;
svalue_t *y = (svalue_t *)p2;
return strcmp(x->u.string, y->u.string);
}
static int CDECL parrcmp (CONST void * p1, CONST void * p2)
{
svalue_t *x = (svalue_t *)p1;
svalue_t *y = (svalue_t *)p2;
return strcmp(x->u.arr->item[0].u.string, y->u.arr->item[0].u.string);
}
static void encode_stat (svalue_t * vp, int flags, char * str, struct stat * st)
{
if (flags == -1) {
array_t *v = allocate_empty_array(3);
v->item[0].type = T_STRING;
v->item[0].subtype = STRING_MALLOC;
v->item[0].u.string = string_copy(str, "encode_stat");
v->item[1].type = T_NUMBER;
v->item[1].u.number =
((st->st_mode & S_IFDIR) ? -2 : st->st_size);
v->item[2].type = T_NUMBER;
v->item[2].u.number = st->st_mtime;
vp->type = T_ARRAY;
vp->u.arr = v;
} else {
vp->type = T_STRING;
vp->subtype = STRING_MALLOC;
vp->u.string = string_copy(str, "encode_stat");
}
}
/*
* List files in directory. This function do same as standard list_files did,
* but instead writing files right away to user this returns an array
* containing those files. Actually most of code is copied from list_files()
* function.
* Differences with list_files:
*
* - file_list("/w"); returns ({ "w" })
*
* - file_list("/w/"); and file_list("/w/."); return contents of directory
* "/w"
*
* - file_list("/");, file_list("."); and file_list("/."); return contents
* of directory "/"
*
* With second argument equal to non-zero, instead of returning an array
* of strings, the function will return an array of arrays about files.
* The information in each array is supplied in the order:
* name of file,
* size of file,
* last update of file.
*/
/* WIN32 should be fixed to do this correctly (i.e. no ifdefs for it) */
#define MAX_FNAME_SIZE 255
#define MAX_PATH_LEN 1024
array_t *get_dir (const char * path, int flags)
{
array_t *v;
int i, count = 0;
#ifndef WIN32
DIR *dirp;
#endif
int namelen, do_match = 0;
#ifndef WIN32
#ifdef USE_STRUCT_DIRENT
struct dirent *de;
#else
struct direct *de;
#endif
#endif
struct stat st;
char *endtemp;
char temppath[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
char regexppath[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
char *p;
#ifdef WIN32
struct _finddata_t FindBuffer;
long FileHandle, FileCount;
#endif
if (!path)
return 0;
path = check_valid_path(path, current_object, "stat", 0);
if (path == 0)
return 0;
if (strlen(path) < 2) {
temppath[0] = path[0] ? path[0] : '.';
temppath[1] = '\000';
p = temppath;
} else {
strncpy(temppath, path, MAX_FNAME_SIZE + MAX_PATH_LEN + 1);
temppath[MAX_FNAME_SIZE + MAX_PATH_LEN + 1] = '\0';
/*
* If path ends with '/' or "/." remove it
*/
if ((p = strrchr(temppath, '/')) == 0)
p = temppath;
if (p[0] == '/' && ((p[1] == '.' && p[2] == '\0') || p[1] == '\0'))
*p = '\0';
}
if (stat(temppath, &st) < 0) {
if (*p == '\0')
return 0;
if (p != temppath) {
strcpy(regexppath, p + 1);
*p = '\0';
} else {
strcpy(regexppath, p);
strcpy(temppath, ".");
}
do_match = 1;
} else if (*p != '\0' && strcmp(temppath, ".")) {
if (*p == '/' && *(p + 1) != '\0')
p++;
v = allocate_empty_array(1);
encode_stat(&v->item[0], flags, p, &st);
return v;
}
#ifdef WIN32
FileHandle = -1;
FileCount = 1;
/* strcat(temppath, "\\*"); */
strcat(temppath, "/*");
if ((FileHandle = _findfirst(temppath, &FindBuffer)) == -1) return 0;
#else
if ((dirp = opendir(temppath)) == 0)
return 0;
#endif
/*
* Count files
*/
#ifdef WIN32
do {
if (!do_match && (!strcmp(FindBuffer.name, ".") ||
!strcmp(FindBuffer.name, ".."))) {
continue;
}
if (do_match && !match_string(regexppath, FindBuffer.name)) {
continue;
}
count++;
if (count >= max_array_size) {
break;
}
} while (!_findnext(FileHandle, &FindBuffer));
_findclose(FileHandle);
#else
for (de = readdir(dirp); de; de = readdir(dirp)) {
#ifdef USE_STRUCT_DIRENT
namelen = strlen(de->d_name);
#else
namelen = de->d_namlen;
#endif
if (!do_match && (strcmp(de->d_name, ".") == 0 ||
strcmp(de->d_name, "..") == 0))
continue;
if (do_match && !match_string(regexppath, de->d_name))
continue;
count++;
if (count >= max_array_size)
break;
}
#endif
/*
* Make array and put files on it.
*/
v = allocate_empty_array(count);
if (count == 0) {
/* This is the easy case :-) */
#ifndef WIN32
closedir(dirp);
#endif
return v;
}
#ifdef WIN32
FileHandle = -1;
if ((FileHandle = _findfirst(temppath, &FindBuffer)) == -1) return 0;
endtemp = temppath + strlen(temppath) - 2;
*endtemp = 0;
/* strcat(endtemp++, "\\"); */
strcat(endtemp++, "/");
i = 0;
do {
if (!do_match && (!strcmp(FindBuffer.name, ".") ||
!strcmp(FindBuffer.name, ".."))) continue;
if (do_match && !match_string(regexppath, FindBuffer.name)) continue;
if (flags == -1) {
strcpy(endtemp, FindBuffer.name);
stat(temppath, &st);
}
encode_stat(&v->item[i], flags, FindBuffer.name, &st);
i++;
} while (!_findnext(FileHandle, &FindBuffer));
_findclose(FileHandle);
#else /* WIN32 */
rewinddir(dirp);
endtemp = temppath + strlen(temppath);
strcat(endtemp++, "/");
for (i = 0, de = readdir(dirp); i < count; de = readdir(dirp)) {
#ifdef USE_STRUCT_DIRENT
namelen = strlen(de->d_name);
#else
namelen = de->d_namlen;
#endif
if (!do_match && (strcmp(de->d_name, ".") == 0 ||
strcmp(de->d_name, "..") == 0))
continue;
if (do_match && !match_string(regexppath, de->d_name))
continue;
de->d_name[namelen] = '\0';
if (flags == -1) {
/*
* We'll have to .... sigh.... stat() the file to get some add'tl
* info.
*/
strcpy(endtemp, de->d_name);
stat(temppath, &st);/* We assume it works. */
}
encode_stat(&v->item[i], flags, de->d_name, &st);
i++;
}
closedir(dirp);
#endif /* OS2 */
/* Sort the names. */
qsort((void *) v->item, count, sizeof v->item[0],
(flags == -1) ? parrcmp : pstrcmp);
return v;
}
int remove_file (const char * path)
{
path = check_valid_path(path, current_object, "remove_file", 1);
if (path == 0)
return 0;
if (unlink(path) == -1)
return 0;
return 1;
}
/*
* Check that it is an legal path. No '..' are allowed.
*/
int legal_path (const char * path)
{
const char *p;
if (path == NULL)
return 0;
if (path[0] == '/')
return 0;
/*
* disallowing # seems the easiest way to solve a bug involving loading
* files containing that character
*/
if (strchr(path, '#')) {
return 0;
}
p = path;
while (p) { /* Zak, 930530 - do better checking */
if (p[0] == '.') {
if (p[1] == '\0') /* trailing `.' ok */
break;
if (p[1] == '.') /* check for `..' or `../' */
p++;
if (p[1] == '/' || p[1] == '\0')
return 0; /* check for `./', `..', or `../' */
}
p = (char *)strstr(p, "/."); /* search next component */
if (p)
p++; /* step over `/' */
}
#if defined(WIN32)
/*
* I don't know what the proper define should be, just leaving an
* appropriate place for the right stuff to happen here - Wayfarer
*/
/* Could be a drive thingy for os2. */
if (strchr(path, ':'))
return 0;
#endif
return 1;
} /* legal_path() */
/*
* There is an error in a specific file. Ask the MudOS driver to log the
* message somewhere.
*/
void smart_log (const char * error_file, int line, const char * what, int flag)
{
char *buff;
svalue_t *mret;
extern int pragmas;
buff = (char *)
DMALLOC(strlen(error_file) + strlen(what) +
((pragmas & PRAGMA_ERROR_CONTEXT) ? 100 : 40), TAG_TEMPORARY, "smart_log: 1");
if (flag)
sprintf(buff, "/%s line %d: Warning: %s", error_file, line, what);
else
sprintf(buff, "/%s line %d: %s", error_file, line, what);
if (pragmas & PRAGMA_ERROR_CONTEXT) {
char *ls = strrchr(buff, '\n');
unsigned char *tmp;
if (ls) {
tmp = (unsigned char *)ls + 1;
while (*tmp && isspace(*tmp)) tmp++;
if (!*tmp) *ls = 0;
}
strcat(buff, show_error_context());
} else strcat(buff, "\n");
push_malloced_string(add_slash(error_file));
copy_and_push_string(buff);
mret = safe_apply_master_ob(APPLY_LOG_ERROR, 2);
if (!mret || mret == (svalue_t *)-1) {
debug_message("%s", buff);
}
FREE(buff);
} /* smart_log() */
/*
* Append string to file. Return 0 for failure, otherwise 1.
*/
int write_file (const char * file, const char * str, int flags)
{
FILE *f;
#ifdef PACKAGE_COMPRESS
gzFile gf;
#endif
#ifdef WIN32
char fmode[3];
#endif
file = check_valid_path(file, current_object, "write_file", 1);
if (!file)
return 0;
#ifdef PACKAGE_COMPRESS
if(flags & 2){
gf = gzopen(file, (flags & 1)?"w":"a");
if(!gf)
error("Wrong permissions for opening file /%s for %s.\n\"%s\"\n",
file, (flags & 1) ? "overwrite" : "append", port_strerror(errno));
} else {
#endif
#ifdef WIN32
fmode[0] = (flags & 1) ? 'w' : 'a';
fmode[1] = 't';
fmode[2] = '\0';
f = fopen(file, fmode);
#else
f = fopen(file, (flags & 1) ? "w" : "a");
#endif
if (f == 0) {
error("Wrong permissions for opening file /%s for %s.\n\"%s\"\n",
file, (flags & 1) ? "overwrite" : "append", port_strerror(errno));
}
#ifdef PACKAGE_COMPRESS
}
if(flags & 2){
gzwrite(gf, str, strlen(str));
}else
#endif
fwrite(str, strlen(str), 1, f);
#ifdef PACKAGE_COMPRESS
if(flags & 2)
gzclose(gf);
else
#endif
fclose(f);
return 1;
}
char *read_file(const char * file, int start, int len) {
struct stat st;
#ifndef PACKAGE_COMPRESS
FILE *f;
#else
gzFile f;
#endif
int chunk;
char *str, *p, *p2;
if (len < 0)
return 0;
file = check_valid_path(file, current_object, "read_file", 0);
if (!file)
return 0;
/*
* file doesn't exist, or is really a directory
*/
if (stat(file, &st) == -1 || (st.st_mode & S_IFDIR))
return 0;
#ifndef PACKAGE_COMPRESS
f = fopen(file, FOPEN_READ);
#else
f = gzopen(file, "rb");
#endif
if (f == 0)
return 0;
if (start < 1)
start = 1;
if (len == 0)
len = 2*READ_FILE_MAX_SIZE;
str = new_string(2*READ_FILE_MAX_SIZE, "read_file: str");
if (st.st_size== 0) {
/* zero length file */
str[0] = 0;
#ifndef PACKAGE_COMPRESS
fclose(f);
#else
gzclose(f);
#endif
str = extend_string(str, 0); /* fix string length */
return str;
}
do {
#ifndef PACKAGE_COMPRESS
chunk = fread(str, 1, 2*READ_FILE_MAX_SIZE, f);
#else
chunk = gzread(f, str, 2*READ_FILE_MAX_SIZE);
#endif
if (chunk < 1)
goto free_str;
p = str;
while (start > 1) {
/* skip newlines */
p2 = (char *)memchr(p, '\n', 2*READ_FILE_MAX_SIZE+str-p);
if (p2) {
p = p2 + 1;
start--;
} else
break;
}
} while (start > 1);
p2 = str;
while (1) {
char c;
c = *p++;
if (p-str > chunk) {
if (chunk == 2*READ_FILE_MAX_SIZE) {
goto free_str; //file too big
} else
break; //reached the end
}
if (p2-str > READ_FILE_MAX_SIZE)
goto free_str; //file too big
if (c == '\0') {
FREE_MSTR(str);
#ifndef PACKAGE_COMPRESS
fclose(f);
#else
gzclose(f);
#endif
error("Attempted to read '\\0' into string!\n");
}
if (c != '\r' || *p != '\n') {
*p2++ = c;
if (c == '\n' && --len == 0)
break; /* done */
}
}
*p2 = 0;
str = extend_string(str, p2 - str); /* fix string length */
#ifndef PACKAGE_COMPRESS
fclose(f);
#else
gzclose(f);
#endif
return str;
/* Error path: unwind allocated resources */
free_str:
FREE_MSTR(str);
#ifndef PACKAGE_COMPRESS
fclose(f);
#else
gzclose(f);
#endif
return 0;
}
char *read_bytes (const char * file, int start, int len, int * rlen)
{
struct stat st;
FILE *fptr;
char *str;
int size;
if (len < 0)
return 0;
file = check_valid_path(file, current_object,
"read_bytes", 0);
if (!file)
return 0;
fptr = fopen(file, "rb");
if (fptr == NULL)
return 0;
if (fstat(fileno(fptr), &st) == -1)
fatal("Could not stat an open file.\n");
size = st.st_size;
if (start < 0)
start = size + start;
if (len == 0)
len = size;
if (len > MAX_BYTE_TRANSFER) {
fclose(fptr);
error("Transfer exceeded maximum allowed number of bytes.\n");
return 0;
}
if (start >= size) {
fclose(fptr);
return 0;
}
if ((start + len) > size)
len = (size - start);
if ((size = fseek(fptr, start, 0)) < 0) {
fclose(fptr);
return 0;
}
str = new_string(len, "read_bytes: str");
size = fread(str, 1, len, fptr);
fclose(fptr);
if (size <= 0) {
FREE_MSTR(str);
return 0;
}
/*
* The string has to end to '\0'!!!
*/
str[size] = '\0';
*rlen = size;
return str;
}
int write_bytes (const char * file, int start, const char * str, int theLength)
{
struct stat st;
int size;
FILE *fptr;
file = check_valid_path(file, current_object, "write_bytes", 1);
if (!file)
return 0;
if (theLength > MAX_BYTE_TRANSFER)
return 0;
/* Under system V, it isn't possible change existing data in a file
* opened for append, so it can't be opened for append.
* opening for r+ won't create the file if it doesn't exist.
* opening for w or w+ will truncate it if it does exist. So we
* have to check if it exists first.
*/
if (stat(file, &st) == -1) {
fptr = fopen(file, "wb");
} else {
fptr = fopen(file, "r+b");
}
if (fptr == NULL) {
return 0;
}
if (fstat(fileno(fptr), &st) == -1)
fatal("Could not stat an open file.\n");
size = st.st_size;
if (start < 0)
start = size + start;
if (start < 0 || start > size) {
fclose(fptr);
return 0;
}
if ((size = fseek(fptr, start, 0)) < 0) {
fclose(fptr);
return 0;
}
size = fwrite(str, 1, theLength, fptr);
fclose(fptr);
if (size <= 0) {
return 0;
}
return 1;
}
int file_size (const char * file)
{
struct stat st;
long ret;
#ifdef WIN32
char *t;
int needs_free = 0, len;
const char *p;
#endif
file = check_valid_path(file, current_object, "file_size", 0);
if (!file)
return -1;
#ifdef WIN32
len = strlen(file);
p = file + len - 1;
if (*p == '/') {
needs_free = 1;
p = file;
t = new_string(len - 1, "file_size");
memcpy(t, p, len - 1);
t[len-1] = 0;
file = t;
}
#endif
if (stat(file, &st) == -1)
ret = -1;
else if (S_IFDIR & st.st_mode)
ret = -2;
else
ret = st.st_size;
#ifdef WIN32
if (needs_free) FREE_MSTR(file);
#endif
return ret;
}
/*
* Check that a path to a file is valid for read or write.
* This is done by functions in the master object.
* The path is always treated as an absolute path, and is returned without
* a leading '/'.
* If the path was '/', then '.' is returned.
* Otherwise, the returned path is temporarily allocated by apply(), which
* means it will be deallocated at next apply().
*/
const char *check_valid_path (const char * path, object_t * call_object, const char * const call_fun, int writeflg)
{
svalue_t *v;
if(!master_ob && !call_object){
//early startup, ignore security
extern svalue_t apply_ret_value;
free_svalue(&apply_ret_value, "check_valid_path");
apply_ret_value.type = T_STRING;
apply_ret_value.subtype = STRING_MALLOC;
path = apply_ret_value.u.string = string_copy(path, "check_valid_path");
return path;
}
if (call_object == 0 || call_object->flags & O_DESTRUCTED)
return 0;
#ifdef WIN32
{
char *p;
for(p=path; *p; p++) if (*p == '\\') *p='/';
}
#endif
copy_and_push_string(path);
push_object(call_object);
push_constant_string(call_fun);
if (writeflg)
v = apply_master_ob(APPLY_VALID_WRITE, 3);
else
v = apply_master_ob(APPLY_VALID_READ, 3);
if (v == (svalue_t *)-1)
v = 0;
if (v && v->type == T_NUMBER && v->u.number == 0) return 0;
if (v && v->type == T_STRING) {
path = v->u.string;
} else {
extern svalue_t apply_ret_value;
free_svalue(&apply_ret_value, "check_valid_path");
apply_ret_value.type = T_STRING;
apply_ret_value.subtype = STRING_MALLOC;
path = apply_ret_value.u.string = string_copy(path, "check_valid_path");
}
if (path[0] == '/')
path++;
if (path[0] == '\0')
path = ".";
if (legal_path(path))
return path;
return 0;
}
static int match_string (char * match, char * str)
{
int i;
again:
if (*str == '\0' && *match == '\0')
return 1;
switch (*match) {
case '?':
if (*str == '\0')
return 0;
str++;
match++;
goto again;
case '*':
match++;
if (*match == '\0')
return 1;
for (i = 0; str[i] != '\0'; i++)
if (match_string(match, str + i))
return 1;
return 0;
case '\0':
return 0;
case '\\':
match++;
if (*match == '\0')
return 0;
/* Fall through ! */
default:
if (*match == *str) {
match++;
str++;
goto again;
}
return 0;
}
}
static struct stat to_stats, from_stats;
static int copy (const char * from, const char * to)
{
int ifd;
int ofd;
char buf[1024 * 8];
int len; /* Number of bytes read into `buf'. */
if (!S_ISREG(from_stats.st_mode)) {
return 1;
}
if (unlink(to) && errno != ENOENT) {
return 1;
}
ifd = open(from, OPEN_READ);
if (ifd < 0) {
return errno;
}
ofd = open(to, OPEN_WRITE | O_CREAT | O_TRUNC, 0666);
if (ofd < 0) {
close(ifd);
return 1;
}
#ifdef HAS_FCHMOD
if (fchmod(ofd, from_stats.st_mode & 0777)) {
close(ifd);
close(ofd);
unlink(to);
return 1;
}
#endif
while ((len = read(ifd, buf, sizeof(buf))) > 0) {
int wrote = 0;
char *bp = buf;
do {
wrote = write(ofd, bp, len);
if (wrote < 0) {
close(ifd);
close(ofd);
unlink(to);
return 1;
}
bp += wrote;
len -= wrote;
} while (len > 0);
}
if (len < 0) {
close(ifd);
close(ofd);
unlink(to);
return 1;
}
if (close(ifd) < 0) {
close(ofd);
return 1;
}
if (close(ofd) < 0) {
return 1;
}
#ifdef FCHMOD_MISSING
if (chmod(to, from_stats.st_mode & 0777)) {
return 1;
}
#endif
return 0;
}
/* Move FROM onto TO. Handles cross-filesystem moves.
If TO is a directory, FROM must be also.
Return 0 if successful, 1 if an error occurred. */
#ifdef F_RENAME
static int do_move (const char * from, const char * to, int flag)
{
if (lstat(from, &from_stats) != 0) {
error("/%s: lstat failed\n", from);
return 1;
}
if (lstat(to, &to_stats) == 0) {
#ifdef WIN32
if (!strcmp(from, to))
#else
if (from_stats.st_dev == to_stats.st_dev
&& from_stats.st_ino == to_stats.st_ino)
#endif
{
error("`/%s' and `/%s' are the same file", from, to);
return 1;
}
if (S_ISDIR(to_stats.st_mode)) {
error("/%s: cannot overwrite directory", to);
return 1;
}
#ifdef WIN32
unlink(to);
#endif
} else if (errno != ENOENT) {
error("/%s: unknown error\n", to);
return 1;
}
#ifdef SYSV
if ((flag == F_RENAME) && file_size(from) == -2) {
char cmd_buf[100];
sprintf(cmd_buf, "/usr/lib/mv_dir %s %s", from, to);
return system(cmd_buf);
} else
#endif /* SYSV */
if ((flag == F_RENAME) && (rename(from, to) == 0))
return 0;
#ifdef F_LINK
else if (flag == F_LINK) {
#ifdef WIN32
error("link() not supported.\n");
#else
if (link(from, to) == 0)
return 0;
#endif
}
#endif
if (errno != EXDEV) {
if (flag == F_RENAME)
error("cannot move `/%s' to `/%s'\n", from, to);
else
error("cannot link `/%s' to `/%s'\n", from, to);
return 1;
}
/* rename failed on cross-filesystem link. Copy the file instead. */
if (flag == F_RENAME) {
if (copy(from, to))
return 1;
if (unlink(from)) {
error("cannot remove `/%s'", from);
return 1;
}
}
#ifdef F_LINK
else if (flag == F_LINK) {
if (symlink(from, to) == 0) /* symbolic link */
return 0;
}
#endif
return 0;
}
#endif
void debug_perror (const char * what, const char * file) {
if (file)
debug_message("System Error: %s:%s:%s\n", what, file, port_strerror(errno));
else
debug_message("System Error: %s:%s\n", what, port_strerror(errno));
}
/*
* do_rename is used by the efun rename. It is basically a combination
* of the unix system call rename and the unix command mv.
*/
static svalue_t from_sv = { T_NUMBER };
static svalue_t to_sv = { T_NUMBER };
#ifdef DEBUGMALLOC_EXTENSIONS
void mark_file_sv() {
mark_svalue(&from_sv);
mark_svalue(&to_sv);
}
#endif
#ifdef F_RENAME
int do_rename (const char * fr, const char * t, int flag)
{
const char *from;
const char *to;
char newfrom[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
int flen;
extern svalue_t apply_ret_value;
/*
* important that the same write access checks are done for link() as are
* done for rename(). Otherwise all kinds of security problems would
* arise (e.g. creating links to files in protected directories and then
* modifying the protected file by modifying the linked file). The idea
* is prevent linking to a file unless the person doing the linking has
* permission to move the file.
*/
from = check_valid_path(fr, current_object, "rename", 1);
if (!from)
return 1;
assign_svalue(&from_sv, &apply_ret_value);
to = check_valid_path(t, current_object, "rename", 1);
if (!to)
return 1;
assign_svalue(&to_sv, &apply_ret_value);
if (!strlen(to) && !strcmp(t, "/")) {
to = "./";
}
/* Strip trailing slashes */
flen = strlen(from);
if (flen > 1 && from[flen - 1] == '/') {
const char *p = from + flen - 2;
int n;
while (*p == '/' && (p > from))
p--;
n = p - from + 1;
memcpy(newfrom, from, n);
newfrom[n] = 0;
from = newfrom;
}
if (file_size(to) == -2) {
/* Target is a directory; build full target filename. */
const char *cp;
char newto[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
cp = strrchr(from, '/');
if (cp)
cp++;
else
cp = from;
sprintf(newto, "%s/%s", to, cp);
return do_move(from, newto, flag);
} else
return do_move(from, to, flag);
}
#endif /* F_RENAME */
int copy_file (const char * from, const char * to)
{
char buf[128];
int from_fd, to_fd;
int num_read, num_written;
char *write_ptr;
extern svalue_t apply_ret_value;
from = check_valid_path(from, current_object, "move_file", 0);
assign_svalue(&from_sv, &apply_ret_value);
to = check_valid_path(to, current_object, "move_file", 1);
assign_svalue(&to_sv, &apply_ret_value);
if (from == 0)
return -1;
if (to == 0)
return -2;
if (lstat(from, &from_stats) != 0) {
error("/%s: lstat failed\n", from);
return 1;
}
if (lstat(to, &to_stats) == 0) {
#ifdef WIN32
if (!strcmp(from, to))
#else
if (from_stats.st_dev == to_stats.st_dev
&& from_stats.st_ino == to_stats.st_ino)
#endif
{
error("`/%s' and `/%s' are the same file", from, to);
return 1;
}
} else if (errno != ENOENT) {
error("/%s: unknown error\n", to);
return 1;
}
from_fd = open(from, OPEN_READ);
if (from_fd < 0)
return -1;
if (file_size(to) == -2) {
/* Target is a directory; build full target filename. */
const char *cp;
char newto[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
cp = strrchr(from, '/');
if (cp)
cp++;
else
cp = from;
sprintf(newto, "%s/%s", to, cp);
close(from_fd);
return copy_file(from, newto);
}
to_fd = open(to, OPEN_WRITE | O_CREAT | O_TRUNC, 0666);
if (to_fd < 0) {
close(from_fd);
return -2;
}
while ((num_read = read(from_fd, buf, 128)) != 0) {
if (num_read < 0) {
debug_perror("copy_file: read", from);
close(from_fd);
close(to_fd);
return -3;
}
write_ptr = buf;
while (write_ptr != (buf + num_read)) {
num_written = write(to_fd, write_ptr, num_read);
if (num_written < 0) {
debug_perror("copy_file: write", to);
close(from_fd);
close(to_fd);
return -3;
}
write_ptr += num_written;
}
}
close(from_fd);
close(to_fd);
return 1;
}
void dump_file_descriptors (outbuffer_t * out)
{
int i;
dev_t dev;
struct stat stbuf;
outbuf_add(out, "Fd Device Number Inode Mode Uid Gid Size\n");
outbuf_add(out, "-- ------------- ----- ------ ----- ----- ----------\n");
for (i = 0; i < FD_SETSIZE; i++) {
/* bug in NeXT OS 2.1, st_mode == 0 for sockets */
if (fstat(i, &stbuf) == -1)
continue;
#if !defined(WIN32)
if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
dev = stbuf.st_rdev;
else
#endif
dev = stbuf.st_dev;
outbuf_addv(out, "%2d", i);
outbuf_addv(out, "%13x", dev);
outbuf_addv(out, "%9d", stbuf.st_ino);
outbuf_add(out, " ");
switch (stbuf.st_mode & S_IFMT) {
case S_IFDIR:
outbuf_add(out, "d");
break;
case S_IFCHR:
outbuf_add(out, "c");
break;
#ifdef S_IFBLK
case S_IFBLK:
outbuf_add(out, "b");
break;
#endif
case S_IFREG:
outbuf_add(out, "f");
break;
#ifdef S_IFIFO
case S_IFIFO:
outbuf_add(out, "p");
break;
#endif
#ifdef S_IFLNK
case S_IFLNK:
outbuf_add(out, "l");
break;
#endif
#ifdef S_IFSOCK
case S_IFSOCK:
outbuf_add(out, "s");
break;
#endif
default:
outbuf_add(out, "?");
break;
}
outbuf_addv(out, "%5o", stbuf.st_mode & ~S_IFMT);
outbuf_addv(out, "%7d", stbuf.st_uid);
outbuf_addv(out, "%7d", stbuf.st_gid);
outbuf_addv(out, "%12d", stbuf.st_size);
outbuf_add(out, "\n");
}
}