upgrade to linux 2.6.10-1.12_FC2
[linux-2.6.git] / fs / coda / upcall.c
1 /*
2  * Mostly platform independent upcall operations to Venus:
3  *  -- upcalls
4  *  -- upcall routines
5  *
6  * Linux 2.0 version
7  * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>, 
8  * Michael Callahan <callahan@maths.ox.ac.uk> 
9  * 
10  * Redone for Linux 2.1
11  * Copyright (C) 1997 Carnegie Mellon University
12  *
13  * Carnegie Mellon University encourages users of this code to contribute
14  * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
15  */
16
17 #include <asm/system.h>
18 #include <asm/signal.h>
19 #include <linux/signal.h>
20
21 #include <linux/types.h>
22 #include <linux/kernel.h>
23 #include <linux/mm.h>
24 #include <linux/time.h>
25 #include <linux/fs.h>
26 #include <linux/file.h>
27 #include <linux/stat.h>
28 #include <linux/errno.h>
29 #include <linux/string.h>
30 #include <asm/uaccess.h>
31 #include <linux/vmalloc.h>
32 #include <linux/vfs.h>
33
34 #include <linux/coda.h>
35 #include <linux/coda_linux.h>
36 #include <linux/coda_psdev.h>
37 #include <linux/coda_fs_i.h>
38 #include <linux/coda_cache.h>
39 #include <linux/coda_proc.h> 
40
41 #define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL)
42 #define upc_free(r) kfree(r)
43
44 static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, 
45                        union inputArgs *buffer);
46
47 static void *alloc_upcall(int opcode, int size)
48 {
49         union inputArgs *inp;
50
51         CODA_ALLOC(inp, union inputArgs *, size);
52         if (!inp)
53                 return ERR_PTR(-ENOMEM);
54
55         inp->ih.opcode = opcode;
56         inp->ih.pid = current->pid;
57         inp->ih.pgid = process_group(current);
58 #ifdef CONFIG_CODA_FS_OLD_API
59         memset(&inp->ih.cred, 0, sizeof(struct coda_cred));
60         inp->ih.cred.cr_fsuid = current->fsuid;
61 #else
62         inp->ih.uid = current->fsuid;
63 #endif
64         return (void*)inp;
65 }
66
67 #define UPARG(op)\
68 do {\
69         inp = (union inputArgs *)alloc_upcall(op, insize); \
70         if (IS_ERR(inp)) { return PTR_ERR(inp); }\
71         outp = (union outputArgs *)(inp); \
72         outsize = insize; \
73 } while (0)
74
75 #define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
76 #define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
77 #define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
78
79
80 /* the upcalls */
81 int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
82 {
83         union inputArgs *inp;
84         union outputArgs *outp;
85         int insize, outsize, error;
86
87         insize = SIZE(root);
88         UPARG(CODA_ROOT);
89
90         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
91         
92         if (error) {
93                 printk("coda_get_rootfid: error %d\n", error);
94         } else {
95                 *fidp = outp->coda_root.VFid;
96         }
97
98         CODA_FREE(inp, insize);
99         return error;
100 }
101
102 int venus_getattr(struct super_block *sb, struct CodaFid *fid, 
103                      struct coda_vattr *attr) 
104 {
105         union inputArgs *inp;
106         union outputArgs *outp;
107         int insize, outsize, error;
108
109         insize = SIZE(getattr); 
110         UPARG(CODA_GETATTR);
111         inp->coda_getattr.VFid = *fid;
112
113         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
114         
115         *attr = outp->coda_getattr.attr;
116
117         CODA_FREE(inp, insize);
118         return error;
119 }
120
121 int venus_setattr(struct super_block *sb, struct CodaFid *fid, 
122                   struct coda_vattr *vattr)
123 {
124         union inputArgs *inp;
125         union outputArgs *outp;
126         int insize, outsize, error;
127         
128         insize = SIZE(setattr);
129         UPARG(CODA_SETATTR);
130
131         inp->coda_setattr.VFid = *fid;
132         inp->coda_setattr.attr = *vattr;
133
134         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
135
136         CODA_FREE(inp, insize);
137         return error;
138 }
139
140 int venus_lookup(struct super_block *sb, struct CodaFid *fid, 
141                     const char *name, int length, int * type, 
142                     struct CodaFid *resfid)
143 {
144         union inputArgs *inp;
145         union outputArgs *outp;
146         int insize, outsize, error;
147         int offset;
148
149         offset = INSIZE(lookup);
150         insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
151         UPARG(CODA_LOOKUP);
152
153         inp->coda_lookup.VFid = *fid;
154         inp->coda_lookup.name = offset;
155         inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
156         /* send Venus a null terminated string */
157         memcpy((char *)(inp) + offset, name, length);
158         *((char *)inp + offset + length) = '\0';
159
160         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
161
162         *resfid = outp->coda_lookup.VFid;
163         *type = outp->coda_lookup.vtype;
164
165         CODA_FREE(inp, insize);
166         return error;
167 }
168
169 int venus_store(struct super_block *sb, struct CodaFid *fid, int flags,
170                 vuid_t uid)
171 {
172         union inputArgs *inp;
173         union outputArgs *outp;
174         int insize, outsize, error;
175 #ifdef CONFIG_CODA_FS_OLD_API
176         struct coda_cred cred = { 0, };
177         cred.cr_fsuid = uid;
178 #endif
179         
180         insize = SIZE(store);
181         UPARG(CODA_STORE);
182         
183 #ifdef CONFIG_CODA_FS_OLD_API
184         memcpy(&(inp->ih.cred), &cred, sizeof(cred));
185 #else
186         inp->ih.uid = uid;
187 #endif
188         
189         inp->coda_store.VFid = *fid;
190         inp->coda_store.flags = flags;
191
192         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
193
194         CODA_FREE(inp, insize);
195         return error;
196 }
197
198 int venus_release(struct super_block *sb, struct CodaFid *fid, int flags)
199 {
200         union inputArgs *inp;
201         union outputArgs *outp;
202         int insize, outsize, error;
203         
204         insize = SIZE(release);
205         UPARG(CODA_RELEASE);
206         
207         inp->coda_release.VFid = *fid;
208         inp->coda_release.flags = flags;
209
210         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
211
212         CODA_FREE(inp, insize);
213         return error;
214 }
215
216 int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
217                 vuid_t uid)
218 {
219         union inputArgs *inp;
220         union outputArgs *outp;
221         int insize, outsize, error;
222 #ifdef CONFIG_CODA_FS_OLD_API
223         struct coda_cred cred = { 0, };
224         cred.cr_fsuid = uid;
225 #endif
226         
227         insize = SIZE(release);
228         UPARG(CODA_CLOSE);
229         
230 #ifdef CONFIG_CODA_FS_OLD_API
231         memcpy(&(inp->ih.cred), &cred, sizeof(cred));
232 #else
233         inp->ih.uid = uid;
234 #endif
235         
236         inp->coda_close.VFid = *fid;
237         inp->coda_close.flags = flags;
238
239         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
240
241         CODA_FREE(inp, insize);
242         return error;
243 }
244
245 int venus_open(struct super_block *sb, struct CodaFid *fid,
246                   int flags, struct file **fh)
247 {
248         union inputArgs *inp;
249         union outputArgs *outp;
250         int insize, outsize, error;
251        
252         insize = SIZE(open_by_fd);
253         UPARG(CODA_OPEN_BY_FD);
254
255         inp->coda_open.VFid = *fid;
256         inp->coda_open.flags = flags;
257
258         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
259
260         *fh = outp->coda_open_by_fd.fh;
261
262         CODA_FREE(inp, insize);
263         return error;
264 }       
265
266 int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, 
267                    const char *name, int length, 
268                    struct CodaFid *newfid, struct coda_vattr *attrs)
269 {
270         union inputArgs *inp;
271         union outputArgs *outp;
272         int insize, outsize, error;
273         int offset;
274
275         offset = INSIZE(mkdir);
276         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
277         UPARG(CODA_MKDIR);
278
279         inp->coda_mkdir.VFid = *dirfid;
280         inp->coda_mkdir.attr = *attrs;
281         inp->coda_mkdir.name = offset;
282         /* Venus must get null terminated string */
283         memcpy((char *)(inp) + offset, name, length);
284         *((char *)inp + offset + length) = '\0';
285         
286         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
287
288         *attrs = outp->coda_mkdir.attr;
289         *newfid = outp->coda_mkdir.VFid;
290
291         CODA_FREE(inp, insize);
292         return error;        
293 }
294
295
296 int venus_rename(struct super_block *sb, struct CodaFid *old_fid, 
297                  struct CodaFid *new_fid, size_t old_length, 
298                  size_t new_length, const char *old_name, 
299                  const char *new_name)
300 {
301         union inputArgs *inp;
302         union outputArgs *outp;
303         int insize, outsize, error; 
304         int offset, s;
305         
306         offset = INSIZE(rename);
307         insize = max_t(unsigned int, offset + new_length + old_length + 8,
308                      OUTSIZE(rename)); 
309         UPARG(CODA_RENAME);
310
311         inp->coda_rename.sourceFid = *old_fid;
312         inp->coda_rename.destFid =  *new_fid;
313         inp->coda_rename.srcname = offset;
314
315         /* Venus must receive an null terminated string */
316         s = ( old_length & ~0x3) +4; /* round up to word boundary */
317         memcpy((char *)(inp) + offset, old_name, old_length);
318         *((char *)inp + offset + old_length) = '\0';
319
320         /* another null terminated string for Venus */
321         offset += s;
322         inp->coda_rename.destname = offset;
323         s = ( new_length & ~0x3) +4; /* round up to word boundary */
324         memcpy((char *)(inp) + offset, new_name, new_length);
325         *((char *)inp + offset + new_length) = '\0';
326
327         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
328
329         CODA_FREE(inp, insize);
330         return error;
331 }
332
333 int venus_create(struct super_block *sb, struct CodaFid *dirfid, 
334                  const char *name, int length, int excl, int mode, dev_t rdev,
335                  struct CodaFid *newfid, struct coda_vattr *attrs) 
336 {
337         union inputArgs *inp;
338         union outputArgs *outp;
339         int insize, outsize, error;
340         int offset;
341
342         offset = INSIZE(create);
343         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
344         UPARG(CODA_CREATE);
345
346         inp->coda_create.VFid = *dirfid;
347         inp->coda_create.attr.va_mode = mode;
348         inp->coda_create.attr.va_rdev = huge_encode_dev(rdev);
349         inp->coda_create.excl = excl;
350         inp->coda_create.mode = mode;
351         inp->coda_create.name = offset;
352
353         /* Venus must get null terminated string */
354         memcpy((char *)(inp) + offset, name, length);
355         *((char *)inp + offset + length) = '\0';
356                 
357         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
358
359         *attrs = outp->coda_create.attr;
360         *newfid = outp->coda_create.VFid;
361
362         CODA_FREE(inp, insize);
363         return error;        
364 }
365
366 int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, 
367                     const char *name, int length)
368 {
369         union inputArgs *inp;
370         union outputArgs *outp;
371         int insize, outsize, error;
372         int offset;
373
374         offset = INSIZE(rmdir);
375         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
376         UPARG(CODA_RMDIR);
377
378         inp->coda_rmdir.VFid = *dirfid;
379         inp->coda_rmdir.name = offset;
380         memcpy((char *)(inp) + offset, name, length);
381         *((char *)inp + offset + length) = '\0';
382         
383         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
384
385         CODA_FREE(inp, insize);
386         return error;
387 }
388
389 int venus_remove(struct super_block *sb, struct CodaFid *dirfid, 
390                     const char *name, int length)
391 {
392         union inputArgs *inp;
393         union outputArgs *outp;
394         int error=0, insize, outsize, offset;
395
396         offset = INSIZE(remove);
397         insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
398         UPARG(CODA_REMOVE);
399
400         inp->coda_remove.VFid = *dirfid;
401         inp->coda_remove.name = offset;
402         memcpy((char *)(inp) + offset, name, length);
403         *((char *)inp + offset + length) = '\0';
404         
405         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
406
407         CODA_FREE(inp, insize);
408         return error;
409 }
410
411 int venus_readlink(struct super_block *sb, struct CodaFid *fid, 
412                       char *buffer, int *length)
413
414         union inputArgs *inp;
415         union outputArgs *outp;
416         int insize, outsize, error;
417         int retlen;
418         char *result;
419         
420         insize = max_t(unsigned int,
421                      INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
422         UPARG(CODA_READLINK);
423
424         inp->coda_readlink.VFid = *fid;
425     
426         error =  coda_upcall(coda_sbp(sb), insize, &outsize, inp);
427         
428         if (! error) {
429                 retlen = outp->coda_readlink.count;
430                 if ( retlen > *length )
431                         retlen = *length;
432                 *length = retlen;
433                 result =  (char *)outp + (long)outp->coda_readlink.data;
434                 memcpy(buffer, result, retlen);
435                 *(buffer + retlen) = '\0';
436         }
437         
438         CODA_FREE(inp, insize);
439         return error;
440 }
441
442
443
444 int venus_link(struct super_block *sb, struct CodaFid *fid, 
445                   struct CodaFid *dirfid, const char *name, int len )
446 {
447         union inputArgs *inp;
448         union outputArgs *outp;
449         int insize, outsize, error;
450         int offset;
451
452         offset = INSIZE(link);
453         insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));
454         UPARG(CODA_LINK);
455
456         inp->coda_link.sourceFid = *fid;
457         inp->coda_link.destFid = *dirfid;
458         inp->coda_link.tname = offset;
459
460         /* make sure strings are null terminated */
461         memcpy((char *)(inp) + offset, name, len);
462         *((char *)inp + offset + len) = '\0';
463         
464         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
465
466         CODA_FREE(inp, insize);
467         return error;
468 }
469
470 int venus_symlink(struct super_block *sb, struct CodaFid *fid,
471                      const char *name, int len,
472                      const char *symname, int symlen)
473 {
474         union inputArgs *inp;
475         union outputArgs *outp;
476         int insize, outsize, error;
477         int offset, s;
478
479         offset = INSIZE(symlink);
480         insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
481         UPARG(CODA_SYMLINK);
482         
483         /*        inp->coda_symlink.attr = *tva; XXXXXX */ 
484         inp->coda_symlink.VFid = *fid;
485
486         /* Round up to word boundary and null terminate */
487         inp->coda_symlink.srcname = offset;
488         s = ( symlen  & ~0x3 ) + 4; 
489         memcpy((char *)(inp) + offset, symname, symlen);
490         *((char *)inp + offset + symlen) = '\0';
491         
492         /* Round up to word boundary and null terminate */
493         offset += s;
494         inp->coda_symlink.tname = offset;
495         s = (len & ~0x3) + 4;
496         memcpy((char *)(inp) + offset, name, len);
497         *((char *)inp + offset + len) = '\0';
498
499         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
500
501         CODA_FREE(inp, insize);
502         return error;
503 }
504
505 int venus_fsync(struct super_block *sb, struct CodaFid *fid)
506 {
507         union inputArgs *inp;
508         union outputArgs *outp; 
509         int insize, outsize, error;
510         
511         insize=SIZE(fsync);
512         UPARG(CODA_FSYNC);
513
514         inp->coda_fsync.VFid = *fid;
515         error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs), 
516                             &outsize, inp);
517
518         CODA_FREE(inp, insize);
519         return error;
520 }
521
522 int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
523 {
524         union inputArgs *inp;
525         union outputArgs *outp; 
526         int insize, outsize, error;
527
528         insize = SIZE(access);
529         UPARG(CODA_ACCESS);
530
531         inp->coda_access.VFid = *fid;
532         inp->coda_access.flags = mask;
533
534         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
535
536         CODA_FREE(inp, insize);
537         return error;
538 }
539
540
541 int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
542                  unsigned int cmd, struct PioctlData *data)
543 {
544         union inputArgs *inp;
545         union outputArgs *outp;  
546         int insize, outsize, error;
547         int iocsize;
548
549         insize = VC_MAXMSGSIZE;
550         UPARG(CODA_IOCTL);
551
552         /* build packet for Venus */
553         if (data->vi.in_size > VC_MAXDATASIZE) {
554                 error = -EINVAL;
555                 goto exit;
556         }
557
558         if (data->vi.out_size > VC_MAXDATASIZE) {
559                 error = -EINVAL;
560                 goto exit;
561         }
562
563         inp->coda_ioctl.VFid = *fid;
564     
565         /* the cmd field was mutated by increasing its size field to
566          * reflect the path and follow args. We need to subtract that
567          * out before sending the command to Venus.  */
568         inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));   
569         iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
570         inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<     16;     
571     
572         /* in->coda_ioctl.rwflag = flag; */
573         inp->coda_ioctl.len = data->vi.in_size;
574         inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
575      
576         /* get the data out of user space */
577         if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data,
578                             data->vi.in, data->vi.in_size) ) {
579                 error = -EINVAL;
580                 goto exit;
581         }
582
583         error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size,
584                             &outsize, inp);
585         
586         if (error) {
587                 printk("coda_pioctl: Venus returns: %d for %s\n", 
588                        error, coda_f2s(fid));
589                 goto exit; 
590         }
591
592         if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
593                 error = -EINVAL;
594                 goto exit;
595         }
596         
597         /* Copy out the OUT buffer. */
598         if (outp->coda_ioctl.len > data->vi.out_size) {
599                 error = -EINVAL;
600                 goto exit;
601         }
602
603         /* Copy out the OUT buffer. */
604         if (copy_to_user(data->vi.out, 
605                          (char *)outp + (long)outp->coda_ioctl.data, 
606                          outp->coda_ioctl.len)) {
607                 error = -EFAULT;
608                 goto exit;
609         }
610
611  exit:
612         CODA_FREE(inp, insize);
613         return error;
614 }
615
616 int venus_statfs(struct super_block *sb, struct kstatfs *sfs) 
617
618         union inputArgs *inp;
619         union outputArgs *outp;
620         int insize, outsize, error;
621         
622         insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
623         UPARG(CODA_STATFS);
624
625         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
626         
627         if (!error) {
628                 sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
629                 sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;
630                 sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
631                 sfs->f_files  = outp->coda_statfs.stat.f_files;
632                 sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;
633         } else {
634                 printk("coda_statfs: Venus returns: %d\n", error);
635         }
636
637         CODA_FREE(inp, insize);
638         return error;
639 }
640
641 /*
642  * coda_upcall and coda_downcall routines.
643  * 
644  */
645
646 static inline void coda_waitfor_upcall(struct upc_req *vmp,
647                                        struct venus_comm *vcommp)
648 {
649         DECLARE_WAITQUEUE(wait, current);
650
651         vmp->uc_posttime = jiffies;
652
653         add_wait_queue(&vmp->uc_sleep, &wait);
654         for (;;) {
655                 if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE ) 
656                         set_current_state(TASK_INTERRUPTIBLE);
657                 else
658                         set_current_state(TASK_UNINTERRUPTIBLE);
659
660                 /* venus died */
661                 if ( !vcommp->vc_inuse )
662                         break;
663
664                 /* got a reply */
665                 if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) )
666                         break;
667
668                 if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) {
669                         /* if this process really wants to die, let it go */
670                         if ( sigismember(&(current->pending.signal), SIGKILL) ||
671                              sigismember(&(current->pending.signal), SIGINT) )
672                                 break;
673                         /* signal is present: after timeout always return 
674                            really smart idea, probably useless ... */
675                         if ( jiffies - vmp->uc_posttime > coda_timeout * HZ )
676                                 break; 
677                 }
678                 schedule();
679         }
680         remove_wait_queue(&vmp->uc_sleep, &wait);
681         set_current_state(TASK_RUNNING);
682
683         return;
684 }
685
686
687 /* 
688  * coda_upcall will return an error in the case of 
689  * failed communication with Venus _or_ will peek at Venus
690  * reply and return Venus' error.
691  *
692  * As venus has 2 types of errors, normal errors (positive) and internal
693  * errors (negative), normal errors are negated, while internal errors
694  * are all mapped to -EINTR, while showing a nice warning message. (jh)
695  * 
696  */
697 static int coda_upcall(struct coda_sb_info *sbi, 
698                 int inSize, int *outSize, 
699                 union inputArgs *buffer) 
700 {
701         struct venus_comm *vcommp;
702         union outputArgs *out;
703         struct upc_req *req;
704         int error = 0;
705
706         vcommp = sbi->sbi_vcomm;
707         if ( !vcommp->vc_inuse ) {
708                 printk("No pseudo device in upcall comms at %p\n", vcommp);
709                 return -ENXIO;
710         }
711
712         /* Format the request message. */
713         req = upc_alloc();
714         if (!req) {
715                 printk("Failed to allocate upc_req structure\n");
716                 return -ENOMEM;
717         }
718         req->uc_data = (void *)buffer;
719         req->uc_flags = 0;
720         req->uc_inSize = inSize;
721         req->uc_outSize = *outSize ? *outSize : inSize;
722         req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
723         req->uc_unique = ++vcommp->vc_seq;
724         init_waitqueue_head(&req->uc_sleep);
725         
726         /* Fill in the common input args. */
727         ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
728
729         /* Append msg to pending queue and poke Venus. */
730         list_add(&(req->uc_chain), vcommp->vc_pending.prev);
731         
732         wake_up_interruptible(&vcommp->vc_waitq);
733         /* We can be interrupted while we wait for Venus to process
734          * our request.  If the interrupt occurs before Venus has read
735          * the request, we dequeue and return. If it occurs after the
736          * read but before the reply, we dequeue, send a signal
737          * message, and return. If it occurs after the reply we ignore
738          * it. In no case do we want to restart the syscall.  If it
739          * was interrupted by a venus shutdown (psdev_close), return
740          * ENODEV.  */
741
742         /* Go to sleep.  Wake up on signals only after the timeout. */
743         coda_waitfor_upcall(req, vcommp);
744
745         if (vcommp->vc_inuse) {      /* i.e. Venus is still alive */
746             /* Op went through, interrupt or not... */
747             if (req->uc_flags & REQ_WRITE) {
748                 out = (union outputArgs *)req->uc_data;
749                 /* here we map positive Venus errors to kernel errors */
750                 error = -out->oh.result;
751                 *outSize = req->uc_outSize;
752                 goto exit;
753             }
754             if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) { 
755                 /* Interrupted before venus read it. */
756                 list_del(&(req->uc_chain));
757                 /* perhaps the best way to convince the app to
758                    give up? */
759                 error = -EINTR;
760                 goto exit;
761             } 
762             if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) {
763                     /* interrupted after Venus did its read, send signal */
764                     union inputArgs *sig_inputArgs;
765                     struct upc_req *sig_req;
766                     
767                     list_del(&(req->uc_chain));
768                     error = -ENOMEM;
769                     sig_req = upc_alloc();
770                     if (!sig_req) goto exit;
771
772                     CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
773                     if (!sig_req->uc_data) {
774                         upc_free(sig_req);
775                         goto exit;
776                     }
777                     
778                     error = -EINTR;
779                     sig_inputArgs = (union inputArgs *)sig_req->uc_data;
780                     sig_inputArgs->ih.opcode = CODA_SIGNAL;
781                     sig_inputArgs->ih.unique = req->uc_unique;
782                     
783                     sig_req->uc_flags = REQ_ASYNC;
784                     sig_req->uc_opcode = sig_inputArgs->ih.opcode;
785                     sig_req->uc_unique = sig_inputArgs->ih.unique;
786                     sig_req->uc_inSize = sizeof(struct coda_in_hdr);
787                     sig_req->uc_outSize = sizeof(struct coda_in_hdr);
788                     
789                     /* insert at head of queue! */
790                     list_add(&(sig_req->uc_chain), &vcommp->vc_pending);
791                     wake_up_interruptible(&vcommp->vc_waitq);
792             } else {
793                     printk("Coda: Strange interruption..\n");
794                     error = -EINTR;
795             }
796         } else {        /* If venus died i.e. !VC_OPEN(vcommp) */
797                 printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n",
798                        req->uc_opcode, req->uc_unique, req->uc_flags);
799                 error = -ENODEV;
800         }
801
802  exit:
803         upc_free(req);
804         return error;
805 }
806
807 /*  
808     The statements below are part of the Coda opportunistic
809     programming -- taken from the Mach/BSD kernel code for Coda. 
810     You don't get correct semantics by stating what needs to be
811     done without guaranteeing the invariants needed for it to happen.
812     When will be have time to find out what exactly is going on?  (pjb)
813 */
814
815
816 /* 
817  * There are 7 cases where cache invalidations occur.  The semantics
818  *  of each is listed here:
819  *
820  * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
821  * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
822  *                  This call is a result of token expiration.
823  *
824  * The next arise as the result of callbacks on a file or directory.
825  * CODA_ZAPFILE   -- flush the cached attributes for a file.
826
827  * CODA_ZAPDIR    -- flush the attributes for the dir and
828  *                  force a new lookup for all the children
829                     of this dir.
830
831  *
832  * The next is a result of Venus detecting an inconsistent file.
833  * CODA_PURGEFID  -- flush the attribute for the file
834  *                  purge it and its children from the dcache
835  *
836  * The last  allows Venus to replace local fids with global ones
837  * during reintegration.
838  *
839  * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
840
841 int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
842 {
843         /* Handle invalidation requests. */
844           if ( !sb || !sb->s_root || !sb->s_root->d_inode)
845                   return 0; 
846
847           switch (opcode) {
848
849           case CODA_FLUSH : {
850                    coda_cache_clear_all(sb);
851                    shrink_dcache_sb(sb);
852                    coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
853                    return(0);
854           }
855
856           case CODA_PURGEUSER : {
857                    coda_cache_clear_all(sb);
858                    return(0);
859           }
860
861           case CODA_ZAPDIR : {
862                   struct inode *inode;
863                   struct CodaFid *fid = &out->coda_zapdir.CodaFid;
864
865                   inode = coda_fid_to_inode(fid, sb);
866                   if (inode) {
867                           coda_flag_inode_children(inode, C_PURGE);
868                           coda_flag_inode(inode, C_VATTR);
869                           iput(inode);
870                   }
871                   
872                   return(0);
873           }
874
875           case CODA_ZAPFILE : {
876                   struct inode *inode;
877                   struct CodaFid *fid = &out->coda_zapfile.CodaFid;
878                   inode = coda_fid_to_inode(fid, sb);
879                   if ( inode ) {
880                           coda_flag_inode(inode, C_VATTR);
881                           iput(inode);
882                   }
883                   return 0;
884           }
885
886           case CODA_PURGEFID : {
887                   struct inode *inode;
888                   struct CodaFid *fid = &out->coda_purgefid.CodaFid;
889                   inode = coda_fid_to_inode(fid, sb);
890                   if ( inode ) { 
891                         coda_flag_inode_children(inode, C_PURGE);
892
893                         /* catch the dentries later if some are still busy */
894                         coda_flag_inode(inode, C_PURGE);
895                         d_prune_aliases(inode);
896
897                         iput(inode);
898                   }
899                   return 0;
900           }
901
902           case CODA_REPLACE : {
903                   struct inode *inode;
904                   struct CodaFid *oldfid = &out->coda_replace.OldFid;
905                   struct CodaFid *newfid = &out->coda_replace.NewFid;
906                   inode = coda_fid_to_inode(oldfid, sb);
907                   if ( inode ) { 
908                           coda_replace_fid(inode, oldfid, newfid);
909                           iput(inode);
910                   }
911                   return 0;
912           }
913           }
914           return 0;
915 }
916