diff --git a/include/minix/vfsif.h b/include/minix/vfsif.h index 686beb20e..d79465de2 100644 --- a/include/minix/vfsif.h +++ b/include/minix/vfsif.h @@ -124,6 +124,10 @@ /* For REQ_GETDENTS */ #define RES_GDE_POS_CHANGE m2_l1 +#define RES_GDE_CUM_IO m2_i1 + +/* For REQ_RDLINK */ +#define RES_RDL_LENGTH m6_l1 /* Request numbers */ #define REQ_GETNODE (VFS_BASE + 1) /* Should be removed */ @@ -145,7 +149,7 @@ #define REQ_UNLINK_O (VFS_BASE + 17) /* Replaced with REQ_UNLINK_S */ #define REQ_RMDIR_O (VFS_BASE + 18) /* Replaced with REQ_RMDIR_S */ #define REQ_UTIME (VFS_BASE + 19) -#define REQ_RDLINK_S (VFS_BASE + 20) +#define REQ_RDLINK_SO (VFS_BASE + 20) /* Replaced with REQ_RDLINK_S */ #define REQ_FSTATFS (VFS_BASE + 21) #define REQ_BREAD_S (VFS_BASE + 22) #define REQ_BWRITE_S (VFS_BASE + 23) @@ -165,7 +169,7 @@ #define REQ_NEW_DRIVER (VFS_BASE + 37) #define REQ_BREAD_O (VFS_BASE + 38) /* Replaced with REQ_BREAD_S */ #define REQ_BWRITE_O (VFS_BASE + 39) /* Replaced with REQ_BWRITE_S */ -#define REQ_GETDENTS (VFS_BASE + 40) +#define REQ_GETDENTS_O (VFS_BASE + 40) /* Replaced with REQ_GETDENTS */ #define REQ_FLUSH (VFS_BASE + 41) #define REQ_READ_S (VFS_BASE + 42) #define REQ_WRITE_S (VFS_BASE + 43) @@ -178,13 +182,15 @@ #define REQ_MOUNTPOINT_S (VFS_BASE + 50) #define REQ_READSUPER_S (VFS_BASE + 51) #define REQ_NEWNODE (VFS_BASE + 52) +#define REQ_RDLINK_S (VFS_BASE + 53) +#define REQ_GETDENTS (VFS_BASE + 54) -#define NREQS 53 +#define NREQS 55 -#define EENTERMOUNT 301 -#define ELEAVEMOUNT 302 -#define ESYMLINK 303 +#define EENTERMOUNT -301 +#define ELEAVEMOUNT -302 +#define ESYMLINK -303 /* REQ_L_FLAGS */ #define PATH_RET_SYMLINK 1 /* Return a symlink object (i.e. diff --git a/servers/mfs/link.c b/servers/mfs/link.c index 4dbfa8bce..c9edb16fe 100644 --- a/servers/mfs/link.c +++ b/servers/mfs/link.c @@ -397,7 +397,8 @@ PUBLIC int fs_rdlink_s() (vir_bytes) bp->b_data, (vir_bytes) copylen, D); put_block(bp, DIRECTORY_BLOCK); - if (r == OK) r = copylen; + if (r == OK) + fs_m_out.RES_RDL_LENGTH = copylen; } put_inode(rip); @@ -405,6 +406,25 @@ PUBLIC int fs_rdlink_s() } +/*===========================================================================* + * fs_rdlink_so * + *===========================================================================*/ +PUBLIC int fs_rdlink_so() +{ +/* Legacy support: wrapper around new rdlink, returning the resulting number of + * bytes in the m_type field of the reply message instead. + */ + int r; + + r = fs_rdlink_s(); + + if (r == OK) + r = fs_m_out.RES_RDL_LENGTH; + + return(r); +} + + /*===========================================================================* * remove_dir_o * *===========================================================================*/ diff --git a/servers/mfs/proto.h b/servers/mfs/proto.h index bd2747047..3aaa98005 100644 --- a/servers/mfs/proto.h +++ b/servers/mfs/proto.h @@ -27,6 +27,7 @@ int fs_trunc(void); int fs_sync(void); int fs_stime(void); +int fs_getdents_o(void); int fs_getdents(void); int fs_flush(void); @@ -68,6 +69,7 @@ _PROTOTYPE( void wipe_inode, (struct inode *rip) ); int fs_link_o(void); int fs_link_s(void); int fs_rdlink_o(void); +int fs_rdlink_so(void); int fs_rdlink_s(void); int fs_rename_o(void); int fs_rename_s(void); diff --git a/servers/mfs/read.c b/servers/mfs/read.c index 063461dbb..cf948f289 100644 --- a/servers/mfs/read.c +++ b/servers/mfs/read.c @@ -1007,17 +1007,36 @@ printf("MFS(%d) get_inode by fs_getdents() failed\n", SELF_E); userbuf_off += tmpbuf_off; } - r= ENOSYS; - fs_m_out.RES_GDE_POS_CHANGE= 0; /* No change in case of an error */ if (done && userbuf_off == 0) r= EINVAL; /* The user's buffer is too small */ else { - r= userbuf_off; + fs_m_out.RES_GDE_CUM_IO= userbuf_off; if (new_pos >= pos) fs_m_out.RES_GDE_POS_CHANGE= new_pos-pos; + else + fs_m_out.RES_GDE_POS_CHANGE= 0; + r= OK; } put_inode(rip); /* release the inode */ return(r); } + +/*===========================================================================* + * fs_getdents_o * + *===========================================================================*/ +PUBLIC int fs_getdents_o(void) +{ +/* Legacy support: wrapper around new getdents, returning the resulting number + * of bytes in the m_type field of the reply message instead. + */ + int r; + + r = fs_getdents(); + + if (r == OK) + r = fs_m_out.RES_GDE_CUM_IO; + + return(r); +} diff --git a/servers/mfs/table.c b/servers/mfs/table.c index 0b2c0e00e..75ca006d7 100644 --- a/servers/mfs/table.c +++ b/servers/mfs/table.c @@ -34,7 +34,7 @@ PUBLIC _PROTOTYPE (int (*fs_call_vec[]), (void) ) = { fs_unlink_o, /* 17 */ /* unlink() */ fs_unlink_o, /* 18 */ /* rmdir() */ fs_utime, /* 19 */ - fs_rdlink_s, /* 20 */ + fs_rdlink_so, /* 20 */ fs_fstatfs, /* 21 */ fs_breadwrite_s, /* 22 */ fs_breadwrite_s, /* 23 */ @@ -54,7 +54,7 @@ PUBLIC _PROTOTYPE (int (*fs_call_vec[]), (void) ) = { fs_new_driver, /* 37 */ fs_breadwrite_o, /* 38 */ fs_breadwrite_o, /* 39 */ - fs_getdents, /* 40 */ + fs_getdents_o, /* 40 */ fs_flush, /* 41 */ fs_readwrite_s, /* 42 */ fs_readwrite_s, /* 43 */ @@ -67,5 +67,7 @@ PUBLIC _PROTOTYPE (int (*fs_call_vec[]), (void) ) = { fs_mountpoint_s, /* 50 */ fs_readsuper_s, /* 51 */ fs_newnode, /* 52 */ + fs_rdlink_s, /* 53 */ + fs_getdents, /* 54 */ }; diff --git a/servers/vfs/Makefile b/servers/vfs/Makefile index a3a922a65..18d983ce6 100644 --- a/servers/vfs/Makefile +++ b/servers/vfs/Makefile @@ -17,7 +17,7 @@ OBJ = main.o open.o read.o write.o pipe.o dmap.o \ path.o device.o mount.o link.o exec.o \ filedes.o stadir.o protect.o time.o \ lock.o misc.o utility.o select.o timers.o table.o \ - vnode.o vmnt.o request.o mmap.o + vnode.o vmnt.o request.o mmap.o fscall.o # build local binary install all build: $(SERVER) diff --git a/servers/vfs/fscall.c b/servers/vfs/fscall.c new file mode 100644 index 000000000..e3c3462e0 --- /dev/null +++ b/servers/vfs/fscall.c @@ -0,0 +1,154 @@ +/* This file handles nested counter-request calls to VFS sent by file system + * (FS) servers in response to VFS requests. + * + * The entry points into this file are + * nested_fs_call perform a nested call from a file system server + */ + +#include "fs.h" +#include "fproc.h" +#include +#include +#include +#include + +/* maximum nested call stack depth */ +#define MAX_DEPTH 1 + +/* global variables stack */ +PRIVATE struct { + struct fproc *g_fp; /* pointer to caller process */ + message g_m_in; /* request message */ + message g_m_out; /* reply message */ + int g_who_e; /* endpoint of caller process */ + int g_who_p; /* slot number of caller process */ + int g_call_nr; /* call number */ + int g_super_user; /* is the caller root? */ + short g_cum_path_processed; /* how many path chars processed? */ + char g_user_fullpath[PATH_MAX+1]; /* path to look up */ +} globals[MAX_DEPTH]; + +PRIVATE int depth = 0; /* current globals stack level */ + +#if ENABLE_SYSCALL_STATS +EXTERN unsigned long calls_stats[NCALLS]; +#endif + +FORWARD _PROTOTYPE( int push_globals, (void) ); +FORWARD _PROTOTYPE( void pop_globals, (void) ); +FORWARD _PROTOTYPE( void set_globals, (message *m) ); + +/*===========================================================================* + * push_globals * + *===========================================================================*/ +PRIVATE int push_globals() +{ +/* Save the global variables of the current call onto the globals stack. + */ + + if (depth == MAX_DEPTH) + return EPERM; + + globals[depth].g_fp = fp; + globals[depth].g_m_in = m_in; + globals[depth].g_m_out = m_out; + globals[depth].g_who_e = who_e; + globals[depth].g_who_p = who_p; + globals[depth].g_call_nr = call_nr; + globals[depth].g_super_user = super_user; + globals[depth].g_cum_path_processed = cum_path_processed; + + /* XXX is it safe to strcpy this? */ + assert(sizeof(globals[0].g_user_fullpath) == sizeof(user_fullpath)); + memcpy(globals[depth].g_user_fullpath, user_fullpath, sizeof(user_fullpath)); + + /* err_code is not used across blocking calls */ + + depth++; + + return OK; +} + +/*===========================================================================* + * pop_globals * + *===========================================================================*/ +PRIVATE void pop_globals() +{ +/* Restore the global variables of a call from the globals stack. + */ + + if (depth == 0) + panic("VFS", "Popping from empty globals stack!", NO_NUM); + + depth--; + + fp = globals[depth].g_fp; + m_in = globals[depth].g_m_in; + m_out = globals[depth].g_m_out; + who_e = globals[depth].g_who_e; + who_p = globals[depth].g_who_p; + call_nr = globals[depth].g_call_nr; + super_user = globals[depth].g_super_user; + cum_path_processed = globals[depth].g_cum_path_processed; + + memcpy(user_fullpath, globals[depth].g_user_fullpath, sizeof(user_fullpath)); +} + +/*===========================================================================* + * set_globals * + *===========================================================================*/ +PRIVATE void set_globals(m) +message *m; /* request message */ +{ +/* Initialize global variables based on a request message. + */ + + m_in = *m; + who_e = m_in.m_source; + who_p = _ENDPOINT_P(who_e); + call_nr = m_in.m_type; + fp = &fproc[who_p]; + super_user = (fp->fp_effuid == SU_UID ? TRUE : FALSE); + /* the rest need not be initialized */ +} + +/*===========================================================================* + * nested_fs_call * + *===========================================================================*/ +PUBLIC void nested_fs_call(m) +message *m; /* request/reply message pointer */ +{ +/* Handle a nested call from a file system server. + */ + int r; + + /* Save global variables of the current call */ + if ((r = push_globals()) != OK) { + printf("VFS: error saving global variables in call %d from FS %d\n", + m->m_type, m->m_source); + } else { + /* Initialize global variables for the nested call */ + set_globals(m); + + /* Perform the nested call */ + if (call_nr < 0 || call_nr >= NCALLS) { + printf("VFS: invalid nested call %d from FS %d\n", call_nr, + who_e); + + r = ENOSYS; + } else { +#if ENABLE_SYSCALL_STATS + calls_stats[call_nr]++; +#endif + + r = (*call_vec[call_nr])(); + } + + /* Store the result, and restore original global variables */ + *m = m_out; + + pop_globals(); + } + + m->m_type = r; +} diff --git a/servers/vfs/proto.h b/servers/vfs/proto.h index d4d0a19a5..ea583f5a4 100644 --- a/servers/vfs/proto.h +++ b/servers/vfs/proto.h @@ -56,6 +56,9 @@ _PROTOTYPE( struct filp *get_filp, (int fild) ); _PROTOTYPE( struct filp *get_filp2, (struct fproc *rfp, int fild) ); _PROTOTYPE( int inval_filp, (struct filp *) ); +/* fscall.c */ +_PROTOTYPE( void nested_fs_call, (message *m) ); + /* kputc.c */ _PROTOTYPE( void diag_repl, (void) ); diff --git a/servers/vfs/request.c b/servers/vfs/request.c index 0c50df5de..0db338bcf 100644 --- a/servers/vfs/request.c +++ b/servers/vfs/request.c @@ -282,6 +282,25 @@ off_t *pos_change; m.REQ_GDE_POS= pos; r = fs_sendrec(fs_e, &m); + + if (r != ENOSYS && r != EINVAL) { + *pos_change= m.RES_GDE_POS_CHANGE; + + if (r == OK) + r = m.RES_GDE_CUM_IO; + + return r; + } + + /* Legacy support: try the old getdents */ + m.m_type = REQ_GETDENTS_O; + m.REQ_GDE_INODE= inode_nr; + m.REQ_GDE_GRANT= gid; + m.REQ_GDE_SIZE= size; + m.REQ_GDE_POS= pos; + + r = fs_sendrec(fs_e, &m); + *pos_change= m.RES_GDE_POS_CHANGE; return r; } @@ -633,6 +652,23 @@ size_t len; /* Send/rec request */ r= fs_sendrec(fs_e, &m); + if (r != ENOSYS && r != EINVAL) { + cpf_revoke(gid); + + if (r == OK) + r = m.RES_RDL_LENGTH; + + return r; + } + + /* Legacy support: try the old rdlink */ + m.m_type = REQ_RDLINK_SO; + m.REQ_INODE_NR = inode_nr; + m.REQ_GRANT = gid; + m.REQ_SLENGTH = len; + + r= fs_sendrec(fs_e, &m); + cpf_revoke(gid); return r; @@ -1026,10 +1062,18 @@ PRIVATE int fs_sendrec_f(char *file, int line, endpoint_t fs_e, message *reqm) /* Make a copy of the request so that we can load it back in * case of a dead driver */ origm = *reqm; - -#if 0 + + /* In response to the request we sent, some file systems may send back their + * own VFS request, instead of a reply. VFS currently offers limited support + * for this. As long as the FS keeps sending requests, we process them and + * send back a reply. We break out of the loop as soon as the FS sends a + * reply to the original request. + * + * There is no form of locking or whatever on global data structures, so it + * is quite easy to mess things up; hence, 'limited' support. A future async + * VFS will solve this problem for good. + */ for (;;) { -#endif /* Do the actual send, receive */ if (OK != (r=sendrec(fs_e, reqm))) { printf("VFS:fs_sendrec:%s:%d: error sending message. FS_e: %d req_nr: %d err: %d\n", @@ -1038,6 +1082,28 @@ PRIVATE int fs_sendrec_f(char *file, int line, endpoint_t fs_e, message *reqm) return r; } + /* If the type field is 0 (OK) or negative (E*), this is a reply. If it + * contains a positive nonzero value, this is a request. + */ + if (reqm->m_type <= 0) + break; /* Reply */ + + /* Legacy support: hacks for old nonzero nonnegative replies */ + if (origm.m_type == REQ_GETDENTS_O || origm.m_type == REQ_RDLINK_SO) + break; /* Reply */ + + if (reqm->m_type == -EENTERMOUNT || reqm->m_type == -ELEAVEMOUNT || + reqm->m_type == -ESYMLINK) { + + reqm->m_type = -reqm->m_type; + + break; /* Reply */ + } + + /* Request */ + nested_fs_call(reqm); + } + #if 0 if(r == OK) { /* Sendrec was okay */