785 lines
16 KiB
C
785 lines
16 KiB
C
/*
|
|
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
|
|
* The President and Fellows of Harvard College.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* fstest - filesystem test code
|
|
*
|
|
* Writes a file (in small chunks) and then reads it back again
|
|
* (also in small chunks) and complains if what it reads back is
|
|
* not the same.
|
|
*
|
|
* The length of SLOGAN is intentionally a prime number and
|
|
* specifically *not* a power of two.
|
|
*/
|
|
|
|
#include <types.h>
|
|
#include <kern/errno.h>
|
|
#include <kern/fcntl.h>
|
|
#include <lib.h>
|
|
#include <uio.h>
|
|
#include <thread.h>
|
|
#include <synch.h>
|
|
#include <vfs.h>
|
|
#include <fs.h>
|
|
#include <vnode.h>
|
|
#include <test.h>
|
|
|
|
#define SLOGAN "HODIE MIHI - CRAS TIBI\n"
|
|
#define FILENAME "fstest.tmp"
|
|
#define NCHUNKS 720
|
|
#define NTHREADS 12
|
|
#define NLONG 32
|
|
#define NCREATE 24
|
|
|
|
static struct semaphore *threadsem = NULL;
|
|
|
|
static
|
|
void
|
|
init_threadsem(void)
|
|
{
|
|
if (threadsem==NULL) {
|
|
threadsem = sem_create("fstestsem", 0);
|
|
if (threadsem == NULL) {
|
|
panic("fstest: sem_create failed\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Vary each line of the test file in a way that's predictable but
|
|
* unlikely to mask bugs in the filesystem.
|
|
*/
|
|
static
|
|
void
|
|
rotate(char *str, int amt)
|
|
{
|
|
int i, ch;
|
|
|
|
amt = (amt+2600)%26;
|
|
KASSERT(amt>=0);
|
|
|
|
for (i=0; str[i]; i++) {
|
|
ch = str[i];
|
|
if (ch>='A' && ch<='Z') {
|
|
ch = ch - 'A';
|
|
ch += amt;
|
|
ch %= 26;
|
|
ch = ch + 'A';
|
|
KASSERT(ch>='A' && ch<='Z');
|
|
}
|
|
str[i] = ch;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
void
|
|
fstest_makename(char *buf, size_t buflen,
|
|
const char *fs, const char *namesuffix)
|
|
{
|
|
snprintf(buf, buflen, "%s:%s%s", fs, FILENAME, namesuffix);
|
|
KASSERT(strlen(buf) < buflen);
|
|
}
|
|
|
|
#define MAKENAME() fstest_makename(name, sizeof(name), fs, namesuffix)
|
|
|
|
static
|
|
int
|
|
fstest_remove(const char *fs, const char *namesuffix)
|
|
{
|
|
char name[32];
|
|
char buf[32];
|
|
int err;
|
|
|
|
MAKENAME();
|
|
|
|
strcpy(buf, name);
|
|
err = vfs_remove(buf);
|
|
if (err) {
|
|
kprintf("Could not remove %s: %s\n", name, strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int
|
|
fstest_write(const char *fs, const char *namesuffix,
|
|
int stridesize, int stridepos)
|
|
{
|
|
struct vnode *vn;
|
|
int err;
|
|
int i;
|
|
size_t shouldbytes=0;
|
|
size_t bytes=0;
|
|
off_t pos=0;
|
|
char name[32];
|
|
char buf[32];
|
|
struct iovec iov;
|
|
struct uio ku;
|
|
int flags;
|
|
|
|
KASSERT(sizeof(buf) > strlen(SLOGAN));
|
|
|
|
MAKENAME();
|
|
|
|
flags = O_WRONLY|O_CREAT;
|
|
if (stridesize == 1) {
|
|
flags |= O_TRUNC;
|
|
}
|
|
|
|
/* vfs_open destroys the string it's passed */
|
|
strcpy(buf, name);
|
|
err = vfs_open(buf, flags, 0664, &vn);
|
|
if (err) {
|
|
kprintf("Could not open %s for write: %s\n",
|
|
name, strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
for (i=0; i<NCHUNKS; i++) {
|
|
if (i % stridesize != stridepos) {
|
|
pos += strlen(SLOGAN);
|
|
continue;
|
|
}
|
|
strcpy(buf, SLOGAN);
|
|
rotate(buf, i);
|
|
uio_kinit(&iov, &ku, buf, strlen(SLOGAN), pos, UIO_WRITE);
|
|
err = VOP_WRITE(vn, &ku);
|
|
if (err) {
|
|
kprintf("%s: Write error: %s\n", name, strerror(err));
|
|
vfs_close(vn);
|
|
vfs_remove(name);
|
|
return -1;
|
|
}
|
|
|
|
if (ku.uio_resid > 0) {
|
|
kprintf("%s: Short write: %lu bytes left over\n",
|
|
name, (unsigned long) ku.uio_resid);
|
|
vfs_close(vn);
|
|
vfs_remove(name);
|
|
return -1;
|
|
}
|
|
|
|
bytes += (ku.uio_offset - pos);
|
|
shouldbytes += strlen(SLOGAN);
|
|
pos = ku.uio_offset;
|
|
}
|
|
|
|
vfs_close(vn);
|
|
|
|
if (bytes != shouldbytes) {
|
|
kprintf("%s: %lu bytes written, should have been %lu!\n",
|
|
name, (unsigned long) bytes,
|
|
(unsigned long) (NCHUNKS*strlen(SLOGAN)));
|
|
vfs_remove(name);
|
|
return -1;
|
|
}
|
|
kprintf("%s: %lu bytes written\n", name, (unsigned long) bytes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int
|
|
fstest_read(const char *fs, const char *namesuffix)
|
|
{
|
|
struct vnode *vn;
|
|
int err;
|
|
int i;
|
|
size_t bytes=0;
|
|
char name[32];
|
|
char buf[32];
|
|
struct iovec iov;
|
|
struct uio ku;
|
|
|
|
MAKENAME();
|
|
|
|
/* vfs_open destroys the string it's passed */
|
|
strcpy(buf, name);
|
|
err = vfs_open(buf, O_RDONLY, 0664, &vn);
|
|
if (err) {
|
|
kprintf("Could not open test file for read: %s\n",
|
|
strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
for (i=0; i<NCHUNKS; i++) {
|
|
uio_kinit(&iov, &ku, buf, strlen(SLOGAN), bytes, UIO_READ);
|
|
err = VOP_READ(vn, &ku);
|
|
if (err) {
|
|
kprintf("%s: Read error: %s\n", name, strerror(err));
|
|
vfs_close(vn);
|
|
return -1;
|
|
}
|
|
|
|
if (ku.uio_resid > 0) {
|
|
kprintf("%s: Short read: %lu bytes left over\n", name,
|
|
(unsigned long) ku.uio_resid);
|
|
vfs_close(vn);
|
|
return -1;
|
|
}
|
|
buf[strlen(SLOGAN)] = 0;
|
|
rotate(buf, -i);
|
|
if (strcmp(buf, SLOGAN)) {
|
|
kprintf("%s: Test failed: line %d mismatched: %s\n",
|
|
name, i+1, buf);
|
|
vfs_close(vn);
|
|
return -1;
|
|
}
|
|
|
|
bytes = ku.uio_offset;
|
|
}
|
|
|
|
vfs_close(vn);
|
|
|
|
if (bytes != NCHUNKS*strlen(SLOGAN)) {
|
|
kprintf("%s: %lu bytes read, should have been %lu!\n",
|
|
name, (unsigned long) bytes,
|
|
(unsigned long) (NCHUNKS*strlen(SLOGAN)));
|
|
return -1;
|
|
}
|
|
kprintf("%s: %lu bytes read\n", name, (unsigned long) bytes);
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
void
|
|
dofstest(const char *filesys)
|
|
{
|
|
kprintf("*** Starting filesystem test on %s:\n", filesys);
|
|
|
|
if (fstest_write(filesys, "", 1, 0)) {
|
|
kprintf("*** Test failed\n");
|
|
return;
|
|
}
|
|
|
|
if (fstest_read(filesys, "")) {
|
|
kprintf("*** Test failed\n");
|
|
return;
|
|
}
|
|
|
|
if (fstest_remove(filesys, "")) {
|
|
kprintf("*** Test failed\n");
|
|
return;
|
|
}
|
|
|
|
kprintf("*** Filesystem test done\n");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
void
|
|
readstress_thread(void *fs, unsigned long num)
|
|
{
|
|
const char *filesys = fs;
|
|
if (fstest_read(filesys, "")) {
|
|
kprintf("*** Thread %lu: failed\n", num);
|
|
}
|
|
V(threadsem);
|
|
}
|
|
|
|
static
|
|
void
|
|
doreadstress(const char *filesys)
|
|
{
|
|
int i, err;
|
|
|
|
init_threadsem();
|
|
|
|
kprintf("*** Starting fs read stress test on %s:\n", filesys);
|
|
|
|
if (fstest_write(filesys, "", 1, 0)) {
|
|
kprintf("*** Test failed\n");
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
err = thread_fork("readstress", NULL,
|
|
readstress_thread, (char *)filesys, i);
|
|
if (err) {
|
|
panic("readstress: thread_fork failed: %s\n",
|
|
strerror(err));
|
|
}
|
|
}
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
P(threadsem);
|
|
}
|
|
|
|
if (fstest_remove(filesys, "")) {
|
|
kprintf("*** Test failed\n");
|
|
return;
|
|
}
|
|
|
|
kprintf("*** fs read stress test done\n");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
void
|
|
writestress_thread(void *fs, unsigned long num)
|
|
{
|
|
const char *filesys = fs;
|
|
char numstr[8];
|
|
snprintf(numstr, sizeof(numstr), "%lu", num);
|
|
|
|
if (fstest_write(filesys, numstr, 1, 0)) {
|
|
kprintf("*** Thread %lu: failed\n", num);
|
|
V(threadsem);
|
|
return;
|
|
}
|
|
|
|
if (fstest_read(filesys, numstr)) {
|
|
kprintf("*** Thread %lu: failed\n", num);
|
|
V(threadsem);
|
|
return;
|
|
}
|
|
|
|
if (fstest_remove(filesys, numstr)) {
|
|
kprintf("*** Thread %lu: failed\n", num);
|
|
}
|
|
|
|
kprintf("*** Thread %lu: done\n", num);
|
|
|
|
V(threadsem);
|
|
}
|
|
|
|
static
|
|
void
|
|
dowritestress(const char *filesys)
|
|
{
|
|
int i, err;
|
|
|
|
init_threadsem();
|
|
|
|
kprintf("*** Starting fs write stress test on %s:\n", filesys);
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
err = thread_fork("writestress", NULL,
|
|
writestress_thread, (char *)filesys, i);
|
|
if (err) {
|
|
panic("thread_fork failed %s\n", strerror(err));
|
|
}
|
|
}
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
P(threadsem);
|
|
}
|
|
|
|
kprintf("*** fs write stress test done\n");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
void
|
|
writestress2_thread(void *fs, unsigned long num)
|
|
{
|
|
const char *filesys = fs;
|
|
|
|
if (fstest_write(filesys, "", NTHREADS, num)) {
|
|
kprintf("*** Thread %lu: failed\n", num);
|
|
V(threadsem);
|
|
return;
|
|
}
|
|
|
|
V(threadsem);
|
|
}
|
|
|
|
static
|
|
void
|
|
dowritestress2(const char *filesys)
|
|
{
|
|
int i, err;
|
|
char name[32];
|
|
struct vnode *vn;
|
|
|
|
init_threadsem();
|
|
|
|
kprintf("*** Starting fs write stress test 2 on %s:\n", filesys);
|
|
|
|
/* Create and truncate test file */
|
|
fstest_makename(name, sizeof(name), filesys, "");
|
|
err = vfs_open(name, O_WRONLY|O_CREAT|O_TRUNC, 0664, &vn);
|
|
if (err) {
|
|
kprintf("Could not create test file: %s\n", strerror(err));
|
|
kprintf("*** Test failed\n");
|
|
return;
|
|
}
|
|
vfs_close(vn);
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
err = thread_fork("writestress2", NULL,
|
|
writestress2_thread, (char *)filesys, i);
|
|
if (err) {
|
|
panic("writestress2: thread_fork failed: %s\n",
|
|
strerror(err));
|
|
}
|
|
}
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
P(threadsem);
|
|
}
|
|
|
|
if (fstest_read(filesys, "")) {
|
|
kprintf("*** Test failed\n");
|
|
return;
|
|
}
|
|
|
|
if (fstest_remove(filesys, "")) {
|
|
kprintf("*** Test failed\n");
|
|
}
|
|
|
|
|
|
kprintf("*** fs write stress test 2 done\n");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
void
|
|
longstress_thread(void *fs, unsigned long num)
|
|
{
|
|
const char *filesys = fs;
|
|
int i;
|
|
char numstr[16];
|
|
|
|
for (i=0; i<NLONG; i++) {
|
|
|
|
snprintf(numstr, sizeof(numstr), "%lu-%d", num, i);
|
|
|
|
if (fstest_write(filesys, numstr, 1, 0)) {
|
|
kprintf("*** Thread %lu: file %d: failed\n", num, i);
|
|
V(threadsem);
|
|
return;
|
|
}
|
|
|
|
if (fstest_read(filesys, numstr)) {
|
|
kprintf("*** Thread %lu: file %d: failed\n", num, i);
|
|
V(threadsem);
|
|
return;
|
|
}
|
|
|
|
if (fstest_remove(filesys, numstr)) {
|
|
kprintf("*** Thread %lu: file %d: failed\n", num, i);
|
|
V(threadsem);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
V(threadsem);
|
|
}
|
|
|
|
static
|
|
void
|
|
dolongstress(const char *filesys)
|
|
{
|
|
int i, err;
|
|
|
|
init_threadsem();
|
|
|
|
kprintf("*** Starting fs long stress test on %s:\n", filesys);
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
err = thread_fork("longstress", NULL,
|
|
longstress_thread, (char *)filesys, i);
|
|
if (err) {
|
|
panic("longstress: thread_fork failed %s\n",
|
|
strerror(err));
|
|
}
|
|
}
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
P(threadsem);
|
|
}
|
|
|
|
kprintf("*** fs long stress test done\n");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
void
|
|
createstress_thread(void *fs, unsigned long num)
|
|
{
|
|
const char *filesys = fs;
|
|
int i, err;
|
|
char namesuffix[16];
|
|
char name[32];
|
|
char buf[32];
|
|
struct vnode *vn;
|
|
struct iovec iov;
|
|
struct uio ku;
|
|
size_t bytes;
|
|
unsigned numwritten = 0, numread = 0, numremoved = 0;
|
|
|
|
for (i=0; i<NCREATE; i++) {
|
|
snprintf(namesuffix, sizeof(namesuffix), "%lu-%d", num, i);
|
|
MAKENAME();
|
|
|
|
/* vfs_open destroys the string it's passed */
|
|
strcpy(buf, name);
|
|
err = vfs_open(buf, O_WRONLY|O_CREAT|O_TRUNC, 0664, &vn);
|
|
if (err) {
|
|
kprintf("Could not open %s for write: %s\n",
|
|
name, strerror(err));
|
|
continue;
|
|
}
|
|
|
|
strcpy(buf, SLOGAN);
|
|
rotate(buf, i);
|
|
|
|
uio_kinit(&iov, &ku, buf, strlen(SLOGAN), 0, UIO_WRITE);
|
|
err = VOP_WRITE(vn, &ku);
|
|
vfs_close(vn);
|
|
if (err) {
|
|
kprintf("%s: Write error: %s\n", name, strerror(err));
|
|
continue;
|
|
}
|
|
if (ku.uio_resid > 0) {
|
|
kprintf("%s: Short write: %lu bytes left over\n",
|
|
name, (unsigned long) ku.uio_resid);
|
|
continue;
|
|
}
|
|
|
|
bytes = ku.uio_offset;
|
|
if (bytes != strlen(SLOGAN)) {
|
|
kprintf("%s: %lu bytes written, expected %lu!\n",
|
|
name, (unsigned long) bytes,
|
|
(unsigned long) strlen(SLOGAN));
|
|
continue;
|
|
}
|
|
numwritten++;
|
|
}
|
|
kprintf("Thread %lu: %u files written\n", num, numwritten);
|
|
|
|
for (i=0; i<NCREATE; i++) {
|
|
snprintf(namesuffix, sizeof(namesuffix), "%lu-%d", num, i);
|
|
MAKENAME();
|
|
|
|
/* vfs_open destroys the string it's passed */
|
|
strcpy(buf, name);
|
|
err = vfs_open(buf, O_RDONLY, 0664, &vn);
|
|
if (err) {
|
|
kprintf("Could not open %s for read: %s\n",
|
|
name, strerror(err));
|
|
continue;
|
|
}
|
|
|
|
uio_kinit(&iov, &ku, buf, strlen(SLOGAN), 0, UIO_READ);
|
|
err = VOP_READ(vn, &ku);
|
|
vfs_close(vn);
|
|
if (err) {
|
|
kprintf("%s: Read error: %s\n", name, strerror(err));
|
|
continue;
|
|
}
|
|
if (ku.uio_resid > 0) {
|
|
kprintf("%s: Short read: %lu bytes left over\n",
|
|
name, (unsigned long) ku.uio_resid);
|
|
continue;
|
|
}
|
|
|
|
buf[strlen(SLOGAN)] = 0;
|
|
rotate(buf, -i);
|
|
|
|
if (strcmp(buf, SLOGAN)) {
|
|
kprintf("%s: Test failed: file mismatched: %s\n",
|
|
name, buf);
|
|
continue;
|
|
}
|
|
|
|
bytes = ku.uio_offset;
|
|
if (bytes != strlen(SLOGAN)) {
|
|
kprintf("%s: %lu bytes read, expected %lu!\n",
|
|
name, (unsigned long) bytes,
|
|
(unsigned long) strlen(SLOGAN));
|
|
continue;
|
|
}
|
|
|
|
numread++;
|
|
}
|
|
kprintf("Thread %lu: %u files read\n", num, numread);
|
|
|
|
for (i=0; i<NCREATE; i++) {
|
|
snprintf(namesuffix, sizeof(namesuffix), "%lu-%d", num, i);
|
|
if (fstest_remove(filesys, namesuffix)) {
|
|
continue;
|
|
}
|
|
numremoved++;
|
|
}
|
|
kprintf("Thread %lu: %u files removed\n", num, numremoved);
|
|
|
|
V(threadsem);
|
|
}
|
|
|
|
static
|
|
void
|
|
docreatestress(const char *filesys)
|
|
{
|
|
int i, err;
|
|
|
|
init_threadsem();
|
|
|
|
kprintf("*** Starting fs create stress test on %s:\n", filesys);
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
err = thread_fork("createstress", NULL,
|
|
createstress_thread, (char *)filesys, i);
|
|
if (err) {
|
|
panic("createstress: thread_fork failed %s\n",
|
|
strerror(err));
|
|
}
|
|
}
|
|
|
|
for (i=0; i<NTHREADS; i++) {
|
|
P(threadsem);
|
|
}
|
|
|
|
kprintf("*** fs create stress test done\n");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
int
|
|
checkfilesystem(int nargs, char **args)
|
|
{
|
|
char *device;
|
|
|
|
if (nargs != 2) {
|
|
kprintf("Usage: fs[12345] filesystem:\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
device = args[1];
|
|
|
|
/* Allow (but do not require) colon after device name */
|
|
if (device[strlen(device)-1]==':') {
|
|
device[strlen(device)-1] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define DEFTEST(testname) \
|
|
int \
|
|
testname(int nargs, char **args) \
|
|
{ \
|
|
int result; \
|
|
result = checkfilesystem(nargs, args); \
|
|
if (result) { \
|
|
return result; \
|
|
} \
|
|
do##testname(args[1]); \
|
|
return 0; \
|
|
}
|
|
|
|
DEFTEST(fstest);
|
|
DEFTEST(readstress);
|
|
DEFTEST(writestress);
|
|
DEFTEST(writestress2);
|
|
DEFTEST(longstress);
|
|
DEFTEST(createstress);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
printfile(int nargs, char **args)
|
|
{
|
|
struct vnode *rv, *wv;
|
|
struct iovec iov;
|
|
struct uio ku;
|
|
off_t rpos=0, wpos=0;
|
|
char buf[128];
|
|
char outfile[16];
|
|
int result;
|
|
int done=0;
|
|
|
|
if (nargs != 2) {
|
|
kprintf("Usage: pf filename\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
/* vfs_open destroys the string it's passed; make a copy */
|
|
strcpy(outfile, "con:");
|
|
|
|
result = vfs_open(args[1], O_RDONLY, 0664, &rv);
|
|
if (result) {
|
|
kprintf("printfile: %s\n", strerror(result));
|
|
return result;
|
|
}
|
|
|
|
result = vfs_open(outfile, O_WRONLY, 0664, &wv);
|
|
if (result) {
|
|
kprintf("printfile: output: %s\n", strerror(result));
|
|
vfs_close(rv);
|
|
return result;
|
|
}
|
|
|
|
while (!done) {
|
|
uio_kinit(&iov, &ku, buf, sizeof(buf), rpos, UIO_READ);
|
|
result = VOP_READ(rv, &ku);
|
|
if (result) {
|
|
kprintf("Read error: %s\n", strerror(result));
|
|
break;
|
|
}
|
|
rpos = ku.uio_offset;
|
|
|
|
if (ku.uio_resid > 0) {
|
|
done = 1;
|
|
}
|
|
|
|
uio_kinit(&iov, &ku, buf, sizeof(buf)-ku.uio_resid, wpos,
|
|
UIO_WRITE);
|
|
result = VOP_WRITE(wv, &ku);
|
|
if (result) {
|
|
kprintf("Write error: %s\n", strerror(result));
|
|
break;
|
|
}
|
|
wpos = ku.uio_offset;
|
|
|
|
if (ku.uio_resid > 0) {
|
|
kprintf("Warning: short write\n");
|
|
}
|
|
}
|
|
|
|
vfs_close(wv);
|
|
vfs_close(rv);
|
|
|
|
return 0;
|
|
}
|