Minix1.5/amoeba/examples/client3.c
/* This file shows how one could build a remote file server for MINIX.
* In this file there are several library routines for the basic system
* calls. Unlike the "real" ones, these call a remote file server instead
* of the local kernel. On client machines, one would replace the library
* routines with these routines, and then recompile programs. In this way,
* clients will then call the remote file server. It should be obvious that
* this file is just an example, and that a productio version would have to
* be much more complete.
*
* The file server3.c contains the start of a stateless file server. Because
* MINIX is not stateless, the conversion must be done in this library. For
* example, when an open() is done, the library records the name, but no
* operation is performed on the file server.
*
* An alternative approach to making a remote file systems is to replace FS,
* the local file server, with one that makes the calls to the remote file
* server itself. This approach is less efficient, because a call then
* consists of a local FS call plus a remote one, but it is more transparent
* because no programs need to recompiled.
*/
#include <amoeba.h>
#include <errno.h>
#include <minix/callnr.h>
#include "header.h"
#define MAX_FD 20
#define LOCAL 100
#define HEAPSIZE 512 /* space for file names */
#define WRITING 2
#define ER -1
#define FS 1
#define NIL_PTR (char*) 0
/* The local array is indexed by file descriptor. Those entries containing
* LOCAL are local (e.g., stdin), those containing REMOTE are remote, and
* those containing 0 are unassigned.
*/
char where[MAX_FD] = {LOCAL, LOCAL, LOCAL};
long pos[MAX_FD]; /* current offset */
char *server_name = "filsrv";
char *file_name[MAX_FD];
char heap[HEAPSIZE];
char *heap_ptr = heap;
header hdr1, hdr2;
char buffer[BUF_SIZE+NAME_SIZE];
extern int errno;
/*============================= Remote Library ==============================*/
int open(name, how)
char *name;
int how;
{
/* Open is entirely local. */
int i, len;
if (how < 0 || how > 2) { errno = EINVAL; return(ER);}
/* Find a free file descriptor. */
for (i = 0; i < MAX_FD; i++) {
if (where[i] == 0) {
len = strlen(name);
file_name[i] = heap_ptr;
bcopy(name, heap_ptr, len);
heap_ptr += len;
*heap_ptr++ = 0;
where[i] = how+1;
return(i);
}
}
errno = EMFILE;
return(ER);
}
int creat(name, mode)
char *name;
int mode;
{
/* Create a file. */
int i, len, n;
/* Find a free file descriptor. */
for (i = 0; i < MAX_FD; i++) {
if (where[i] == 0) {
len = strlen(name);
file_name[i] = heap_ptr;
bcopy(name, heap_ptr, len);
heap_ptr += len;
*heap_ptr++ = 0;
where[i] = WRITING;
strncpy(&hdr1.h_port, server_name, PORTSIZE);
hdr1.h_command = CREAT;
hdr1.h_size = mode;
n = trans(&hdr1, file_name[i], len+1, &hdr2, buffer, 0);
if (n < 0) {errno = EIO; return(ER);}
return(hdr2.h_status);
}
}
errno = EMFILE;
return(ER);
}
/* int close(fd)
int fd;
{
if (where[fd] == LOCAL) return(Xclose(fd));
if (where[fd] == 0) {errno = EBADF; return(ER);}
where[fd] = 0;
return(OK);
}
*/
int read(fd, buf, bytes)
int fd, bytes;
char buf[];
{
/* Primitive read() routine for reads up to 1K. */
int n;
if (where[fd] == LOCAL) return (Xread(fd, buf, bytes));
if ((where[fd]&1) == 0) {errno = EBADF; return(ER);}
if (bytes > BUF_SIZE) return(EINVAL); /* in a real version, fix this */
strncpy(&hdr1.h_port, server_name, PORTSIZE);
hdr1.h_command = READ;
hdr1.h_size = bytes;
hdr1.h_offset = pos[fd];
n = trans(&hdr1, file_name[fd], strlen(file_name[fd])+1, &hdr2, buf, bytes);
if (n < 0) {errno = EIO; return(ER);}
if (hdr2.h_extra != 0) errno = hdr2.h_extra;
pos[fd] += hdr2.h_status; /* advance file position */
return(hdr2.h_status);
}
int write(fd, buf, bytes)
int fd, bytes;
char buf[];
{
/* Primitive write() routine for writes up to 1K. This is a very simple
* routine. Because the server is stateless, for a write we must send both
* the data and the file name. In this example, the first 1K of the buffer
* is reserved for the data, with the file name starting at position 1024.
*/
int n, len;
if (where[fd] == LOCAL) return (Xwrite(fd, buf, bytes));
if ((where[fd]&02) == 0) {errno = EBADF; return(ER);}
if (bytes > BUF_SIZE) return(EINVAL); /* in a real version, fix this */
strncpy(&hdr1.h_port, server_name, PORTSIZE);
len = strlen(file_name[fd]);
hdr1.h_command = WRITE;
hdr1.h_size = bytes;
hdr1.h_offset = pos[fd];
bcopy(buf, buffer, bytes); /* copy data to message */
bcopy(file_name[fd], &buffer[BUF_SIZE], len+1);
n = trans(&hdr1, buffer, BUF_SIZE+len+1, &hdr2, buf, 0);
if(n < 0) {errno = EIO; return(ER);}
if (hdr2.h_extra != 0) errno = hdr2.h_extra;
pos[fd] += hdr2.h_status;
return(hdr2.h_status);
}
/* Below are the real calls, which are sometimes needed. */
int Xread(fd, buffer, nbytes)
int fd;
char *buffer;
int nbytes;
{
int n;
n = callm1(FS, READ, fd, nbytes, 0, buffer, NIL_PTR, NIL_PTR);
return(n);
}
int Xwrite(fd, buffer, nbytes)
char *buffer;
int nbytes;
{
return callm1(FS, WRITE, fd, nbytes, 0, buffer, NIL_PTR, NIL_PTR);
}
int Xclose(fd)
int fd;
{
return callm1(FS, CLOSE, fd, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR);
}
/* ========================= test program =============================*/
main(argc, argv)
int argc;
char *argv[];
{
int fd1, n;
char b[1024];
if (argc != 2) {
printf("Usage: client3 file\n");
exit(1);
}
fd1 = open(argv[1], 0);
if (fd1 < 0) {
printf("Open of %s failed\n", argv[1]);
exit(1);
}
do {
if ((n=read(fd1, b, 1024) < 0)) {
printf("Cannot read %s\n", argv[1]);
exit(1);
}
if (write(1, b, n) < 0) {
printf("Cannot write stdout\n");
exit(1);
}
} while (n > 0);
}