From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- erts/emulator/drivers/common/ram_file_drv.c | 692 ++++++++++++++++++++++++++++ 1 file changed, 692 insertions(+) create mode 100644 erts/emulator/drivers/common/ram_file_drv.c (limited to 'erts/emulator/drivers/common/ram_file_drv.c') diff --git a/erts/emulator/drivers/common/ram_file_drv.c b/erts/emulator/drivers/common/ram_file_drv.c new file mode 100644 index 0000000000..2e3aeb981e --- /dev/null +++ b/erts/emulator/drivers/common/ram_file_drv.c @@ -0,0 +1,692 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * RAM File operations + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* Operations */ + +/* defined "file" functions */ +#define RAM_FILE_OPEN 1 +#define RAM_FILE_READ 2 +#define RAM_FILE_LSEEK 3 +#define RAM_FILE_WRITE 4 +#define RAM_FILE_FSYNC 9 +#define RAM_FILE_TRUNCATE 14 +#define RAM_FILE_PREAD 17 +#define RAM_FILE_PWRITE 18 + +/* other operations */ +#define RAM_FILE_GET 30 +#define RAM_FILE_SET 31 +#define RAM_FILE_GET_CLOSE 32 /* get_file/close */ +#define RAM_FILE_COMPRESS 33 /* compress file */ +#define RAM_FILE_UNCOMPRESS 34 /* uncompress file */ +#define RAM_FILE_UUENCODE 35 /* uuencode file */ +#define RAM_FILE_UUDECODE 36 /* uudecode file */ +#define RAM_FILE_SIZE 37 /* get file size */ +/* possible new operations include: + DES_ENCRYPT + DES_DECRYPT + CRC-32, CRC-16, CRC-CCITT + IP-CHECKSUM +*/ + +/* + * Open modes for RAM_FILE_OPEN. + */ +#define RAM_FILE_MODE_READ 1 +#define RAM_FILE_MODE_WRITE 2 /* Implies truncating file + * when used alone. */ +#define RAM_FILE_MODE_READ_WRITE 3 + +/* + * Seek modes for RAM_FILE_LSEEK. + */ +#define RAM_FILE_SEEK_SET 0 +#define RAM_FILE_SEEK_CUR 1 +#define RAM_FILE_SEEK_END 2 + +/* Return codes */ + +#define RAM_FILE_RESP_OK 0 +#define RAM_FILE_RESP_ERROR 1 +#define RAM_FILE_RESP_DATA 2 +#define RAM_FILE_RESP_NUMBER 3 +#define RAM_FILE_RESP_INFO 4 + +#include +#include +#include + +#include "sys.h" +#include "erl_driver.h" +#include "zlib.h" +#include "gzio.h" + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#define BFILE_BLOCK 1024 + +typedef unsigned char uchar; + +static ErlDrvData rfile_start(ErlDrvPort, char*); +static int rfile_init(void); +static void rfile_stop(ErlDrvData); +static void rfile_command(ErlDrvData, char*, int); + + +struct erl_drv_entry ram_file_driver_entry = { + rfile_init, + rfile_start, + rfile_stop, + rfile_command, + NULL, + NULL, + "ram_file_drv" +}; + +/* A File is represented as a array of bytes, this array is + reallocated when needed. A possibly better implementation + whould be to have a vector of blocks. This may be implemented + when we have the commandv/driver_outputv +*/ +typedef struct ram_file { + ErlDrvPort port; /* the associcated port */ + int flags; /* flags read/write */ + ErlDrvBinary* bin; /* binary to hold binary file */ + char* buf; /* buffer start (in binary) */ + int size; /* buffer size (allocated) */ + int cur; /* current position in buffer */ + int end; /* end position in buffer */ +} RamFile; + +#ifdef LOADABLE +static int rfile_finish(DriverEntry* drv) +{ + return 0; +} + +DriverEntry* driver_init(void *handle) +{ + ram_file_driver_entry.handle = handle; + ram_file_driver_entry.driver_name = "ram_file_drv"; + ram_file_driver_entry.finish = rfile_finish; + ram_file_driver_entry.init = rfile_init; + ram_file_driver_entry.start = rfile_start; + ram_file_driver_entry.stop = rfile_stop; + ram_file_driver_entry.output = rfile_command; + ram_file_driver_entry.ready_input = NULL; + ram_file_driver_entry.ready_output = NULL; + return &ram_file_driver_entry; +} +#endif + +static int rfile_init(void) +{ + return 0; +} + +static ErlDrvData rfile_start(ErlDrvPort port, char* buf) +{ + RamFile* f; + + if ((f = (RamFile*) driver_alloc(sizeof(RamFile))) == NULL) { + errno = ENOMEM; + return ERL_DRV_ERROR_ERRNO; + } + f->port = port; + f->flags = 0; + f->bin = NULL; + f->buf = NULL; + f->size = f->cur = f->end = 0; + return (ErlDrvData)f; +} + +static void rfile_stop(ErlDrvData e) +{ + RamFile* f = (RamFile*)e; + if (f->bin != NULL) + driver_free_binary(f->bin); + driver_free(f); +} + +/* + * Sends back an error reply to Erlang. + */ + +static int error_reply(RamFile *f, int err) +{ + char response[256]; /* Response buffer. */ + char* s; + char* t; + + /* + * Contents of buffer sent back: + * + * +-----------------------------------------+ + * | RAM_FILE_RESP_ERROR | Posix error id string | + * +-----------------------------------------+ + */ + response[0] = RAM_FILE_RESP_ERROR; + for (s = erl_errno_id(err), t = response+1; *s; s++, t++) + *t = tolower(*s); + driver_output2(f->port, response, t-response, NULL, 0); + return 0; +} + +static int reply(RamFile *f, int ok, int err) +{ + if (!ok) + error_reply(f, err); + else { + char c = RAM_FILE_RESP_OK; + driver_output2(f->port, &c, 1, NULL, 0); + } + return 0; +} + +static int numeric_reply(RamFile *f, int result) +{ + char tmp[5]; + + /* + * Contents of buffer sent back: + * + * +-----------------------------------------------+ + * | RAM_FILE_RESP_NUMBER | 32-bit number (big-endian) | + * +-----------------------------------------------+ + */ + + tmp[0] = RAM_FILE_RESP_NUMBER; + put_int32(result, tmp+1); + driver_output2(f->port, tmp, sizeof(tmp), NULL, 0); + return 0; +} + +/* install bin as the new binary reset all pointer */ + +static void ram_file_set(RamFile *f, ErlDrvBinary *bin, int bsize, int len) +{ + f->size = bsize; + f->buf = bin->orig_bytes; + f->cur = 0; + f->end = len; + f->bin = bin; +} + +static int ram_file_init(RamFile *f, char *buf, int count, int *error) +{ + int bsize; + ErlDrvBinary* bin; + + if (count < 0) { + *error = EINVAL; + return -1; + } + if ((bsize = (count+BFILE_BLOCK+(BFILE_BLOCK>>1)) & ~(BFILE_BLOCK-1)) + < 0) { + bsize = INT_MAX; + } + + if (f->bin == NULL) + bin = driver_alloc_binary(bsize); + else + bin = driver_realloc_binary(f->bin, bsize); + if (bin == NULL) { + *error = ENOMEM; + return -1; + } + sys_memzero(bin->orig_bytes, bsize); + sys_memcpy(bin->orig_bytes, buf, count); + ram_file_set(f, bin, bsize, count); + return count; +} + +static int ram_file_expand(RamFile *f, int size, int *error) +{ + int bsize; + ErlDrvBinary* bin; + + if (size < 0) { + *error = EINVAL; + return -1; + } + if ((bsize = (size+BFILE_BLOCK+(BFILE_BLOCK>>1)) & ~(BFILE_BLOCK-1)) + < 0) { + bsize = INT_MAX; + } + + if (bsize <= f->size) + return f->size; + else { + if ((bin = driver_realloc_binary(f->bin, bsize)) == NULL) { + *error = ENOMEM; + return -1; + } + sys_memzero(bin->orig_bytes+f->size, bsize - f->size); + f->size = bsize; + f->buf = bin->orig_bytes; + f->bin = bin; + return bsize; + } +} + + +static int ram_file_write(RamFile *f, char *buf, int len, + int *location, int *error) +{ + int cur = f->cur; + + if (!(f->flags & RAM_FILE_MODE_WRITE)) { + *error = EBADF; + return -1; + } + if (location) cur = *location; + if (cur < 0 || len < 0 || cur+len < 0) { + *error = EINVAL; + return -1; + } + if (cur+len > f->size && ram_file_expand(f, cur+len, error) < 0) { + return -1; + } + if (len) sys_memcpy(f->buf+cur, buf, len); + cur += len; + if (cur > f->end) f->end = cur; + if (! location) f->cur = cur; + return len; +} + +static int ram_file_read(RamFile *f, int len, ErlDrvBinary **bp, + int *location, int *error) +{ + ErlDrvBinary* bin; + int cur = f->cur; + + if (!(f->flags & RAM_FILE_MODE_READ)) { + *error = EBADF; + return -1; + } + if (location) cur = *location; + if (cur < 0 || len < 0) { + *error = EINVAL; + return -1; + } + if (cur < f->end) { + if (len > f->end-cur) len = f->end - cur; + } else { + len = 0; /* eof */ + } + if ((bin = driver_alloc_binary(len)) == NULL) { + *error = ENOMEM; + return -1; + } + if (len) sys_memcpy(bin->orig_bytes, f->buf+cur, len); + *bp = bin; + if (! location) f->cur = cur + len; + return len; +} + +static int ram_file_seek(RamFile *f, int offset, int whence, int *error) +{ + int pos; + + if (f->flags == 0) { + *error = EBADF; + return -1; + } + switch(whence) { + case RAM_FILE_SEEK_SET: pos = offset; break; + case RAM_FILE_SEEK_CUR: pos = f->cur + offset; break; + case RAM_FILE_SEEK_END: pos = f->end + offset; break; + default: *error = EINVAL; return -1; + } + if (pos < 0) { + *error = EINVAL; + return -1; + } + return f->cur = pos; +} + +#define UUMASK(x) ((x)&0x3F) +#define uu_encode(x) (UUMASK(x)+32) + +/* calculate max number of quadrauple bytes given max line length */ +#define UULINE(n) ( (((n)-1) / 4) * 3) + +#define UNIX_LINE 61 /* 61 character lines => 45 uncoded => 60 coded */ + +#define uu_pack(p, c1, c2, c3) \ + (p)[0] = uu_encode((c1) >> 2), \ + (p)[1] = uu_encode(((c1) << 4) | ((c2) >> 4)), \ + (p)[2] = uu_encode(((c2) << 2) | ((c3) >> 6)), \ + (p)[3] = uu_encode(c3) + +static int ram_file_uuencode(RamFile *f) +{ + int code_len = UULINE(UNIX_LINE); + int len = f->end; + int usize = (len*4+2)/3 + 2*(len/code_len+1) + 2 + 1; + ErlDrvBinary* bin; + uchar* inp; + uchar* outp; + int count = 0; + + if ((bin = driver_alloc_binary(usize)) == NULL) + return error_reply(f, ENOMEM); + outp = (uchar*)bin->orig_bytes; + inp = (uchar*)f->buf; + + while(len > 0) { + int c1, c2, c3; + int n = (len >= code_len) ? code_len : len; + + len -= n; + *outp++ = uu_encode(UUMASK(n)); + count++; + while (n >= 3) { + c1 = inp[0]; + c2 = inp[1]; + c3 = inp[2]; + uu_pack(outp, c1, c2, c3); + inp += 3; n -= 3; + outp += 4; count += 4; + } + if (n == 2) { + c1 = inp[0]; + c2 = inp[1]; + uu_pack(outp, c1, c2, 0); + inp += 2; + outp += 4; count += 4; + } + else if (n == 1) { + c1 = inp[0]; + uu_pack(outp, c1, 0, 0); + inp += 1; + outp += 4; count += 4; + } + *outp++ = '\n'; + count++; + } + *outp++ = ' '; /* this end of file 0 length !!! */ + *outp++ = '\n'; + count += 2; + + driver_free_binary(f->bin); + ram_file_set(f, bin, usize, count); + return numeric_reply(f, count); +} + + +#define uu_decode(x) ((x)-32) + +static int ram_file_uudecode(RamFile *f) +{ + int len = f->end; + int usize = ( (len+3) / 4 ) * 3; + ErlDrvBinary* bin; + uchar* inp; + uchar* outp; + int count = 0; + int n; + + if ((bin = driver_alloc_binary(usize)) == NULL) + return error_reply(f, ENOMEM); + outp = (uchar*)bin->orig_bytes; + inp = (uchar*)f->buf; + + while(len > 0) { + if ((n = uu_decode(*inp++)) < 0) + goto error; + len--; + if ((n == 0) && (*inp == '\n')) + break; + count += n; /* count characters */ + while((n > 0) && (len >= 4)) { + int c1, c2, c3, c4; + c1 = uu_decode(inp[0]); + c2 = uu_decode(inp[1]); + c3 = uu_decode(inp[2]); + c4 = uu_decode(inp[3]); + inp += 4; + len -= 4; + + switch(n) { + case 1: + *outp++ = (c1 << 2) | (c2 >> 4); + n = 0; + break; + case 2: + *outp++ = (c1 << 2) | (c2 >> 4); + *outp++ = (c2 << 4) | (c3 >> 2); + n = 0; + break; + default: + *outp++ = (c1 << 2) | (c2 >> 4); + *outp++ = (c2 << 4) | (c3 >> 2); + *outp++ = (c3 << 6) | c4; + n -= 3; + break; + } + } + if ((n != 0) || (*inp++ != '\n')) + goto error; + len--; + } + driver_free_binary(f->bin); + ram_file_set(f, bin, usize, count); + return numeric_reply(f, count); + + error: + driver_free_binary(bin); + return error_reply(f, EINVAL); +} + + +static int ram_file_compress(RamFile *f) +{ + int size = f->end; + ErlDrvBinary* bin; + + if ((bin = erts_gzdeflate_buffer(f->buf, size)) == NULL) { + return error_reply(f, EINVAL); + } + driver_free_binary(f->bin); + size = bin->orig_size; + ram_file_set(f, bin, size, size); + return numeric_reply(f, size); +} + +/* Tricky since we dont know the expanded size !!! */ +/* First attempt is to double the size of input */ +/* loop until we don't get Z_BUF_ERROR */ + +static int ram_file_uncompress(RamFile *f) +{ + int size = f->end; + ErlDrvBinary* bin; + + if ((bin = erts_gzinflate_buffer(f->buf, size)) == NULL) { + return error_reply(f, EINVAL); + } + driver_free_binary(f->bin); + size = bin->orig_size; + ram_file_set(f, bin, size, size); + return numeric_reply(f, size); +} + + +static void rfile_command(ErlDrvData e, char* buf, int count) +{ + RamFile* f = (RamFile*)e; + int error = 0; + ErlDrvBinary* bin; + char header[5]; /* result code + count */ + int offset; + int origin; /* Origin of seek. */ + int n; + + count--; + switch(*(uchar*)buf++) { + case RAM_FILE_OPEN: /* args is initial data */ + f->flags = get_int32(buf); + if (ram_file_init(f, buf+4, count-4, &error) < 0) + error_reply(f, error); + else + numeric_reply(f, 0); /* 0 is not used */ + break; + + case RAM_FILE_FSYNC: + if (f->flags == 0) + error_reply(f, EBADF); + else + reply(f, 1, 0); + break; + + case RAM_FILE_WRITE: + if (ram_file_write(f, buf, count, NULL, &error) < 0) + error_reply(f, error); + else + numeric_reply(f, count); + break; + + case RAM_FILE_PWRITE: + if ((offset = get_int32(buf)) < 0) + error_reply(f, EINVAL); + else if (ram_file_write(f, buf+4, count-4, &offset, &error) < 0) + error_reply(f, error); + else + numeric_reply(f, count-4); + break; + + case RAM_FILE_LSEEK: + offset = get_int32(buf); + origin = get_int32(buf+4); + if ((offset = ram_file_seek(f, offset, origin, &error)) < 0) + error_reply(f, error); + else + numeric_reply(f, offset); + break; + + case RAM_FILE_PREAD: + if ((offset = get_int32(buf)) < 0) { + error_reply(f, EINVAL); + break; + } + + count = get_int32(buf+4); + if ((n = ram_file_read(f, count, &bin, &offset, &error)) < 0) { + error_reply(f, error); + } else { + header[0] = RAM_FILE_RESP_DATA; + put_int32(n, header+1); + driver_output_binary(f->port, header, sizeof(header), + bin, 0, n); + driver_free_binary(bin); + } + break; + + case RAM_FILE_READ: + count = get_int32(buf); + if ((n = ram_file_read(f, count, &bin, NULL, &error)) < 0) + error_reply(f, error); + else { + header[0] = RAM_FILE_RESP_DATA; + put_int32(n, header+1); + driver_output_binary(f->port, header, sizeof(header), + bin, 0, n); + driver_free_binary(bin); + } + break; + + case RAM_FILE_TRUNCATE: + if (!(f->flags & RAM_FILE_MODE_WRITE)) { + error_reply(f, EACCES); + break; + } + if (f->end > f->cur) + sys_memzero(f->buf + f->cur, f->end - f->cur); + f->end = f->cur; + reply(f, 1, 0); + break; + + case RAM_FILE_GET: /* return a copy of the file */ + n = f->end; /* length */ + if ((bin = driver_alloc_binary(n)) == NULL) { + error_reply(f, ENOMEM); + break; + } + sys_memcpy(bin->orig_bytes, f->buf, n); + + header[0] = RAM_FILE_RESP_DATA; + put_int32(n, header+1); + driver_output_binary(f->port, header, sizeof(header), + bin, 0, n); + driver_free_binary(bin); + break; + + case RAM_FILE_GET_CLOSE: /* return the file and close driver */ + n = f->end; /* length */ + bin = f->bin; + f->bin = NULL; /* NUKE IT */ + header[0] = RAM_FILE_RESP_DATA; + put_int32(n, header+1); + driver_output_binary(f->port, header, sizeof(header), + bin, 0, n); + driver_free_binary(bin); + driver_failure(f->port, 0); + break; + + case RAM_FILE_SIZE: + numeric_reply(f, f->end); + break; + + case RAM_FILE_SET: /* re-init file with new data */ + if ((n = ram_file_init(f, buf, count, &error)) < 0) + error_reply(f, error); + else + numeric_reply(f, n); /* 0 is not used */ + break; + + case RAM_FILE_COMPRESS: /* inline compress the file */ + ram_file_compress(f); + break; + + case RAM_FILE_UNCOMPRESS: /* inline uncompress file */ + ram_file_uncompress(f); + break; + + case RAM_FILE_UUENCODE: /* uuencode file */ + ram_file_uuencode(f); + break; + + case RAM_FILE_UUDECODE: /* uudecode file */ + ram_file_uudecode(f); + break; + } + /* + * Ignore anything else -- let the caller hang. + */ +} -- cgit v1.2.3