windows-eventlog2-20080301
[openafs.git] / src / WINNT / afsd / afsdifs.c
1 /* copyright (c) 2005
2  * the regents of the university of michigan
3  * all rights reserved
4  * 
5  * permission is granted to use, copy, create derivative works and
6  * redistribute this software and such derivative works for any purpose,
7  * so long as the name of the university of michigan is not used in
8  * any advertising or publicity pertaining to the use or distribution
9  * of this software without specific, written prior authorization.  if
10  * the above copyright notice or any other identification of the
11  * university of michigan is included in any copy of any portion of
12  * this software, then the disclaimer below must also be included.
13  * 
14  * this software is provided as is, without representation from the
15  * university of michigan as to its fitness for any purpose, and without
16  * warranty by the university of michigan of any kind, either express 
17  * or implied, including without limitation the implied warranties of
18  * merchantability and fitness for a particular purpose.  the regents
19  * of the university of michigan shall not be liable for any damages,   
20  * including special, indirect, incidental, or consequential damages, 
21  * with respect to any claim arising out or in connection with the use
22  * of the software, even if it has been or is hereafter advised of the
23  * possibility of such damages.
24  */
25
26 #include <osi.h>
27 #include "afsd.h"
28 #include <winioctl.h>
29 #include "..\afsrdr\kif.h"
30 #include "..\afsrdr\ifs_rpc.h"
31
32 #include "afsdifs.h"
33
34
35 /****************************/
36 /* parameters, macros, etc. */
37 /****************************/
38 #define IFSL_SUCCEEDED(st)              (!(st & IFSL_FAIL_BASE))
39 #define MAP_RETURN(code)                if (code) return ifs_MapCmError(code);
40 #define ROOTPATH                        "\\"
41 #define TRANSFER_BUF_SIZE               (RPC_BUF_SIZE + TRANSFER_CHUNK_SIZE)
42 #define SCPL_LOCK                       EnterCriticalSection(&scp_list_lock);
43 #define SCPL_UNLOCK                     LeaveCriticalSection(&scp_list_lock);
44
45
46 /****************************/
47 /* structs                  */
48 /****************************/
49 struct user_map_entry                   /* how we keep users straight.  total of MAX_AFS_USERS of these */
50 {
51     LARGE_INTEGER id;                   /* internal id created by kernel */
52     cm_user_t *creds;                   /* global (thread-specific) var userp is set to this */
53 };
54
55 struct scp_status                               /* one for each unique file in afs */
56 {
57     struct scp_status *next;    /* stored in a global chain in a chain locked by SCPL_[UN]LOCK */
58     cm_scache_t *scp;                   /* file handle used with cm_ fns */
59     ULONG fid;                                  /* internal id generated by FID_HASH_FN from AFS's 128-bit FID */
60 };
61 typedef struct scp_status scp_status_t;
62
63 struct readdir_context                  /* temporary struct, allocated as necessary, for cm_Apply callback */
64 {
65     char *matchString;                  /* for matching against */
66     char *buf, *buf_pos;                /* filling buffer to length, currently at buf_pos */
67     ULONG_PTR length;
68     ULONG count;                        /* number of entries packed so far */
69 };
70 typedef struct readdir_context readdir_context_t;
71
72
73 /****************************/
74 /* global vars              */
75 /****************************/
76 /* the table user_map is used to store cm_user structs keyed on the calling
77  * process' 64-bit ID token.  the rpc library sets userp to point to this
78  * entry; userp is specific to each thread, and thus may be used securely
79  * as a global.
80  */
81 __declspec(thread) cm_user_t *userp;
82 struct user_map_entry user_map[MAX_AFS_USERS];
83
84 CRITICAL_SECTION mapLock, scp_list_lock;
85
86 scp_status_t *scp_list_head = NULL;
87
88
89 /****************************/
90 /* error functions          */
91 /****************************/
92 char *IfslErrorToText(unsigned long ifsl)
93 {
94     switch (ifsl)
95     {
96     case IFSL_SUCCESS:
97         return "success";
98     case IFSL_DOES_NOT_EXIST:
99         return "does not exist";
100     case IFSL_NOT_IMPLEMENTED:
101         return "not implemented";
102     case IFSL_END_OF_ENUM:
103         return "end of enum";
104     case IFSL_CANNOT_MAKE:
105         return "cannot make";
106     case IFSL_END_OF_FILE:
107         return "end of file";
108     case IFSL_NO_ACCESS:
109         return "no access";
110     case IFSL_BUFFER_TOO_SMALL:
111         return "buffer too small";
112     case IFSL_SHARING_VIOLATION:
113         return "sharing violation";
114     case IFSL_BAD_INPUT:
115         return "bad input";
116     case IFSL_GENERIC_FAILURE:
117         return "generic failure";
118     case IFSL_OPEN_CREATED:
119         return "open created";
120     case IFSL_OPEN_EXISTS:
121         return "open exists";
122     case IFSL_OPEN_OPENED:
123         return "opened";
124     case IFSL_OPEN_OVERWRITTEN:
125         return "overwritten";
126     case IFSL_OPEN_SUPERSCEDED:
127         return "supersceded";
128     case IFSL_BADFILENAME:
129         return "bad filename";
130     case IFSL_READONLY:
131         return "read only";
132     case IFSL_IS_A_DIR:
133         return "is a dir";
134     case IFSL_PATH_DOES_NOT_EXIST:
135         return "path does not exist";
136     case IFSL_IS_A_FILE:
137         return "is a file";
138     case IFSL_NOT_EMPTY:
139         return "dir not empty";
140     case IFSL_UNSPEC:
141         return "unspecified error";
142     default:
143         return "NOT FOUND";
144     }
145 }       
146
147 unsigned long ifs_MapCmError(unsigned long code)
148 {
149     switch (code)
150     {
151     case CM_ERROR_STOPNOW:
152     case 0:
153         return IFSL_SUCCESS;
154     case CM_ERROR_NOSUCHCELL:
155     case CM_ERROR_NOSUCHVOLUME:
156     case CM_ERROR_NOSUCHFILE:
157         return IFSL_DOES_NOT_EXIST;
158     case CM_ERROR_NOSUCHPATH:
159         return IFSL_PATH_DOES_NOT_EXIST;
160     case CM_ERROR_BADNTFILENAME:
161         return IFSL_BADFILENAME;
162     case CM_ERROR_TIMEDOUT:
163     case CM_ERROR_ALLOFFLINE:
164     case CM_ERROR_CLOCKSKEW:
165     case CM_ERROR_REMOTECONN:
166     case CM_ERROR_ALLBUSY:
167         return IFSL_GENERIC_FAILURE;
168     case CM_ERROR_NOACCESS:
169         return IFSL_NO_ACCESS;
170     case CM_ERROR_RETRY:
171     case CM_ERROR_TOOBIG:
172     case CM_ERROR_BADFD:
173     case CM_ERROR_BADFDOP:
174     case CM_ERROR_CROSSDEVLINK:
175         return IFSL_GENERIC_FAILURE;
176     case CM_ERROR_EXISTS:
177         return IFSL_OPEN_EXISTS;
178     case CM_ERROR_BADOP:
179     case CM_ERROR_INVAL:
180         case CM_ERROR_UNKNOWN:
181     case CM_ERROR_BADSMB:
182         return IFSL_GENERIC_FAILURE;
183     case CM_ERROR_NOTDIR:
184     case CM_ERROR_ISDIR:
185     case CM_ERROR_READONLY:
186         return IFSL_BAD_INPUT;
187     case CM_ERROR_BUFFERTOOSMALL:
188         return IFSL_BUFFER_TOO_SMALL;
189     case CM_ERROR_WOULDBLOCK:
190     case CM_ERROR_BADSHARENAME:
191     case CM_ERROR_NOMORETOKENS:
192     case CM_ERROR_NOTEMPTY:
193     case CM_ERROR_USESTD:
194     case CM_ERROR_ATSYS:
195         return IFSL_GENERIC_FAILURE;
196     case CM_ERROR_NOFILES:
197     case CM_ERROR_BADTID:
198         return IFSL_END_OF_ENUM;
199     case CM_ERROR_PARTIALWRITE:
200     case CM_ERROR_NOIPC:
201     case CM_ERROR_RENAME_IDENTICAL:
202     case CM_ERROR_AMBIGUOUS_FILENAME:
203         return IFSL_GENERIC_FAILURE;
204     case IFSL_SHARING_VIOLATION:
205         return IFSL_SHARING_VIOLATION;
206     case IFSL_NOT_EMPTY:
207         return IFSL_NOT_EMPTY;
208     case CM_ERROR_SPACE:
209     case CM_ERROR_QUOTA:
210         return IFSL_OVERQUOTA;  
211     }
212     return IFSL_GENERIC_FAILURE;
213 }
214
215
216 /****************************/
217 /* support fns              */
218 /****************************/
219 cm_scache_t *ifs_FindScp(ULONG fid)             /* walk list to find scp<->fid mapping */
220 {
221     scp_status_t *curr;
222
223     SCPL_LOCK;
224
225     curr = scp_list_head;
226     while (curr)
227     {
228         if (curr->fid == fid)
229         {
230             SCPL_UNLOCK;
231             return curr->scp;
232         }
233         curr = curr->next;
234     }
235     SCPL_UNLOCK;
236     return NULL;
237 }
238
239 /* must call with scp write-locked.  will always return correct results
240    unless network fails (it loops properly). */
241 ifs_CheckAcl(cm_scache_t *scp, ULONG access, ULONG *granted)
242 {
243     long code;
244     cm_req_t req;
245
246     cm_InitReq(&req);
247
248     /* ripped from cm_scache.c */
249     while (1)
250     {
251         if (cm_HaveAccessRights(scp, userp, access, granted))
252         {
253             return 0;
254         }
255         else
256         {
257             /* we don't know the required access rights */
258             code = cm_GetAccessRights(scp, userp, &req);
259             MAP_RETURN(code);
260             continue;
261         }
262     }
263
264     return 0;
265 }
266
267 /* extract data from scp.  an ifs_ support function to centralize changes. */
268 ifs_CopyInfo(cm_scache_t *scp, ULONG *attribs, LARGE_INTEGER *size,
269               LARGE_INTEGER *creation, LARGE_INTEGER *access,
270               LARGE_INTEGER *change, LARGE_INTEGER *written)
271 {
272     access->QuadPart = 0;                                               /* these mappings are not quite correct.  we can */
273     change->QuadPart = scp->clientModTime;              /* leave them zero, if necessary. */
274     written->QuadPart = scp->clientModTime;
275     creation->QuadPart = scp->serverModTime;
276
277     *attribs = 0;
278     if (scp->fileType == CM_SCACHETYPE_DIRECTORY ||
279         scp->fileType == CM_SCACHETYPE_SYMLINK ||
280         scp->fileType == CM_SCACHETYPE_MOUNTPOINT/* ||
281         scp->fileType == 0*/)
282         *attribs |= FILE_ATTRIBUTE_DIRECTORY;
283
284     /*if (!attribs && scp->fileType == CM_SCACHETYPE_FILE)
285     *attribs |= FILE_ATTRIBUTE_NORMAL;*/
286
287     if (*attribs == FILE_ATTRIBUTE_DIRECTORY)
288         size->QuadPart = 0;
289     else
290         *size = scp->length;
291
292     return 0;
293 }
294
295
296 /* close and zero scp pointer.  zeroing pointer should
297    help eliminate accessing discarded cache entries. */
298 void ifs_InternalClose(cm_scache_t **scpp)
299 {
300     osi_assert(scpp && *scpp);
301
302     lock_ObtainMutex(&((*scpp)->mx));
303     cm_ReleaseSCache(*scpp);
304
305     lock_ReleaseMutex(&((*scpp)->mx));
306     *scpp = NULL;
307 }
308
309 /* normalizes path by removing trailing slashes.  separates last
310  * path component with a null, so that *dirp points to parent path
311  * and *filep points to filename.  modifies string path.
312  */
313 BOOLEAN ifs_FindComponents(char *path, const char **dirp, const char **filep)
314 {
315     char *lastSep;
316     BOOLEAN removed;
317     static char emptyPath[] = "\\";             /* if the path contains only one component, this is the parent. */
318
319     osi_assert(path);
320
321     if (strlen(path))
322         removed = (path[strlen(path)-1] == '\\');
323     else
324         removed = 1;
325
326     lastSep = strrchr(path, '\\');
327     while (lastSep == path + strlen(path) - 1)
328     {
329         *lastSep = '\0';
330         lastSep = strrchr(path, '\\');
331     }
332
333     if (lastSep)
334     {
335         *lastSep = '\0';
336
337         *dirp = path;
338         *filep = lastSep + 1;
339     }
340     else    
341     {
342         lastSep = path + strlen(path);
343
344         *dirp = emptyPath;
345         *filep = path;
346     }
347
348     return removed;
349 }
350
351 /* here to make maintenance easy */
352 unsigned long ifs_ConvertFileName(wchar_t *in, unsigned int inchars, char *out, unsigned int outchars)
353 {
354     unsigned long code;
355
356     code = WideCharToMultiByte(CP_UTF8, 0/*WC_NO_BEST_FIT_CHARS*/, in, inchars, out, outchars-1, NULL, NULL);
357     if (!code)
358         return IFSL_BADFILENAME;
359
360     return 0;
361 }
362
363 /* called by rpc_ library to let us initialize environment.
364  * call with id of zero to clear current thread auth. */
365 ifs_ImpersonateClient(LARGE_INTEGER user_id)
366 {
367     int x, empty;
368
369     if (!user_id.QuadPart)
370     {
371         userp = NULL;
372         return 0;
373     }
374
375     empty = -1;
376     EnterCriticalSection(&mapLock);
377     for (x = 0; x < MAX_AFS_USERS; x++)
378     {
379         if (user_map[x].id.QuadPart == 0)
380             empty = x;
381         if (user_map[x].id.QuadPart == user_id.QuadPart)
382             goto done;
383     }
384     if (empty == -1)
385     {
386         LeaveCriticalSection(&mapLock);
387         return -1;
388     }
389     user_map[empty].id = user_id;
390     user_map[empty].creds = cm_NewUser();
391     x = empty;
392
393   done:
394     userp = user_map[x].creds;
395     LeaveCriticalSection(&mapLock);
396
397     return 0;
398 }
399
400
401 /****************************/
402 /* upcalls                  */
403 /****************************/
404 long uc_namei(WCHAR *name, ULONG *fid)  /* performs name<->fid mapping, and enters it into table */
405 {
406     char *buffer;                               /* we support semi-infinite path lengths */
407     long code;
408     cm_scache_t *scp, *dscp;
409     char *dirp, *filep;
410     cm_req_t req;
411     scp_status_t *st;
412     short len;
413
414     cm_InitReq(&req);
415
416     len = (short)wcslen(name)+20;                       /* characters *should* map 1<->1, but in case */
417     buffer = malloc(len);
418     code = ifs_ConvertFileName(name, -1, buffer, len);
419     if (code)
420     {
421         free(buffer);
422         MAP_RETURN(code);
423     }
424     ifs_FindComponents(buffer, &dirp, &filep);
425
426     code = cm_NameI(cm_data.rootSCachep, dirp, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp);
427     if (code)
428     {
429         free(buffer);
430         MAP_RETURN(code);
431     }
432     if (*filep)
433         code = cm_Lookup(dscp, filep, 0, userp, &req, &scp);
434     else
435         cm_HoldSCache(scp = dscp);
436     cm_ReleaseSCache(dscp);
437
438     if (code)
439     {
440         free(buffer);
441         MAP_RETURN(code);
442     }
443
444     if (ifs_FindScp(FID_HASH_FN(&scp->fid)))
445         {
446         osi_assertx(ifs_FindScp(FID_HASH_FN(&scp->fid)) == scp, "uc_namei: same fid hash for two files");
447         *fid = FID_HASH_FN(&scp->fid);
448         osi_assert(scp->refCount > 1);
449         cm_ReleaseSCache(scp);
450         }
451     else
452         {
453         SCPL_LOCK;
454         st = malloc(sizeof(scp_status_t));
455         st->scp = scp;
456         st->fid = FID_HASH_FN(&scp->fid);
457         st->next = scp_list_head;
458         scp_list_head = st;
459         SCPL_UNLOCK;
460         osi_assert(scp->refCount == 1);
461         *fid = st->fid;
462         }
463
464     free(buffer);
465
466     return 0;
467 }
468
469 /* this should only be called right after open, so we do not need to stat file.
470  * we only check the server's restrictions.  sharing violations are handled in the
471  * kernel. the access mode we grant sticks with the file_object until its death. */
472 long uc_check_access(ULONG fid, ULONG access, ULONG *granted)
473 {
474     ULONG afs_acc, afs_gr;
475     cm_scache_t *scp;
476     ULONG gr;
477     BOOLEAN file, dir;
478
479     gr = 0;
480  
481     scp = ifs_FindScp(fid);
482     if (!scp)
483         return IFSL_BAD_INPUT;
484
485     file = (scp->fileType == CM_SCACHETYPE_FILE);
486     dir = !file;
487
488     /* access definitions from prs_fs.h */
489     afs_acc = 0;
490     if (access & FILE_READ_DATA)
491         afs_acc |= PRSFS_READ;
492     if (file && ((access & FILE_WRITE_DATA) || (access & FILE_APPEND_DATA)))
493         afs_acc |= PRSFS_WRITE;
494     if (access & FILE_WRITE_EA || access & FILE_WRITE_ATTRIBUTES)
495         afs_acc |= PRSFS_WRITE;
496     if (dir && ((access & FILE_ADD_FILE) || (access & FILE_ADD_SUBDIRECTORY)))
497         afs_acc |= PRSFS_INSERT;
498     if (dir && (access & FILE_LIST_DIRECTORY))
499         afs_acc |= PRSFS_LOOKUP;
500     if (access & FILE_READ_EA || access & FILE_READ_ATTRIBUTES)
501         afs_acc |= PRSFS_LOOKUP;
502     if (file && (access & FILE_EXECUTE))
503         afs_acc |= PRSFS_WRITE;
504     if (dir && (access & FILE_TRAVERSE))
505         afs_acc |= PRSFS_READ;
506     if (dir && (access & FILE_DELETE_CHILD))
507         afs_acc |= PRSFS_DELETE;
508     if ((access & DELETE))
509         afs_acc |= PRSFS_DELETE;
510
511     /* check ACL with server */
512     lock_ObtainMutex(&(scp->mx));
513     ifs_CheckAcl(scp, afs_acc, &afs_gr);
514     lock_ReleaseMutex(&(scp->mx));
515
516     *granted = 0;
517     if (afs_gr & PRSFS_READ)
518         *granted |= FILE_READ_DATA | FILE_EXECUTE;
519     if (afs_gr & PRSFS_WRITE)
520         *granted |= FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_EXECUTE;
521     if (afs_gr & PRSFS_INSERT)
522         *granted |= (dir ? FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY : 0) | (file ? FILE_ADD_SUBDIRECTORY : 0);
523     if (afs_gr & PRSFS_LOOKUP)
524         *granted |= (dir ? FILE_LIST_DIRECTORY : 0) | FILE_READ_EA | FILE_READ_ATTRIBUTES;
525     if (afs_gr & PRSFS_DELETE)
526         *granted |= FILE_DELETE_CHILD | DELETE;
527     if (afs_gr & PRSFS_LOCK)
528         *granted |= 0;
529     if (afs_gr & PRSFS_ADMINISTER)
530         *granted |= 0;
531
532     * granted |= SYNCHRONIZE | READ_CONTROL;
533
534     return 0;
535 }
536
537 long uc_create(WCHAR *name, ULONG attribs, LARGE_INTEGER alloc, ULONG access, ULONG *granted, ULONG *fid)
538 {
539     char *buffer;                                       /* we support semi-infinite path lengths */
540     long code;
541     cm_scache_t *scp, *dscp;
542     char *dirp, *filep;
543     unsigned char removed;
544     cm_req_t req;
545     scp_status_t *st;
546     cm_attr_t attr;
547     short len;
548
549     cm_InitReq(&req);
550
551     len = (short)wcslen(name)+20;                       /* characters *should* map 1<->1, but in case */
552     buffer = malloc(len);
553     code = ifs_ConvertFileName(name, -1, buffer, len);
554     if (code)
555     {
556         free(buffer);
557         MAP_RETURN(code);
558     }
559     removed = ifs_FindComponents(buffer, &dirp, &filep);
560
561     /* lookup the parent directory, which must exist */
562     code = cm_NameI(cm_data.rootSCachep, dirp, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp);
563     if (code)
564     {
565         free(buffer);
566         MAP_RETURN(code);
567     }
568
569     osi_assert(filep);
570     if (*filep)
571     {
572         attr.mask = CM_ATTRMASK_LENGTH;
573         attr.length = alloc;
574
575         if (attribs & FILE_ATTRIBUTE_DIRECTORY)
576         {
577             code = cm_MakeDir(dscp, filep, 0, &attr, userp, &req);
578             if (!code)
579                 code = cm_Lookup(dscp, filep, 0, userp, &req, &scp);
580         }
581         else
582             code = cm_Create(dscp, filep, 0, &attr, &scp, userp, &req);
583     }
584     cm_ReleaseSCache(dscp);
585
586     if (code)
587     {
588         free(buffer);
589         MAP_RETURN(code);
590     }
591
592     SCPL_LOCK;
593     st = malloc(sizeof(scp_status_t));
594     st->scp = scp;
595     st->fid = FID_HASH_FN(&scp->fid);
596     st->next = scp_list_head;
597     scp_list_head = st;
598     SCPL_UNLOCK;
599
600     *fid = st->fid;
601     *granted = access;
602
603     free(buffer);
604
605     return 0;
606 }
607
608 /* this does not fill the attribs member completely.  additional flags must
609    be added in the kernel, such as read-only. */
610 long uc_stat(ULONG fid, ULONG *attribs, LARGE_INTEGER *size, LARGE_INTEGER *creation,
611                 LARGE_INTEGER *access, LARGE_INTEGER *change, LARGE_INTEGER *written)
612 {
613     cm_scache_t *scp;
614     cm_req_t req;
615     ULONG code;
616
617     scp = ifs_FindScp(fid);
618     if (!scp)
619         return IFSL_BAD_INPUT;
620
621     /* stat file; don't want callback */
622     cm_InitReq(&req);
623     lock_ObtainMutex(&(scp->mx));
624     cm_HoldUser(userp);
625     code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS);
626     cm_ReleaseUser(userp);
627
628     if (code)
629         lock_ReleaseMutex(&(scp->mx));
630     MAP_RETURN(code);
631
632     code = ifs_CopyInfo(scp, attribs, size, creation, access, change, written);
633     lock_ReleaseMutex(&(scp->mx));
634     MAP_RETURN(code);
635
636     return 0;
637 }
638
639 /* set atime, mtime, etc. */
640 long uc_setinfo(ULONG fid, ULONG attribs, LARGE_INTEGER creation, LARGE_INTEGER access,
641                    LARGE_INTEGER change, LARGE_INTEGER written)
642 {
643     return IFSL_GENERIC_FAILURE;
644 }
645
646 /* FIXFIX: this code may not catch over-quota errors, because the end
647  * of the file is not written to the server by the time this returns. */
648 /* truncate or extend file, in cache and on server */
649 long uc_trunc(ULONG fid, LARGE_INTEGER size)
650 {
651     ULONG code;
652     cm_scache_t *scp;
653     cm_req_t req;
654     osi_hyper_t oldLen;
655
656     scp = ifs_FindScp(fid);
657     if (!scp)
658         return IFSL_BAD_INPUT;
659
660     /* we have already checked permissions in the kernel; but, if we do not
661      * have access as this userp, code will fail in rpc layer.
662      */
663
664     cm_InitReq(&req);
665     lock_ObtainMutex(&(scp->mx));
666
667     code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS);
668
669     if (code)
670         lock_ReleaseMutex(&(scp->mx));
671     MAP_RETURN(code);
672
673     oldLen = scp->length;
674     lock_ReleaseMutex(&(scp->mx));
675
676     code = cm_SetLength(scp, &size, userp, &req);
677     MAP_RETURN(code);
678     /*code = cm_FSync(scp, userp, &req);
679     MAP_RETURN(code);*/
680     /*code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS);
681     MAP_RETURN(code);*/
682
683 #if 0
684     /* attempt to write last byte of file.  fails to bring out quota errors because of delayed writing. */
685     if (oldLen.QuadPart < size.QuadPart)
686     {
687         writePos.QuadPart = size.QuadPart - 1;
688         WriteData(scp, writePos, 1, &"\0\0\0", userp, &written);
689         MAP_RETURN(code);
690         if (written != 1)
691             return IFSL_UNSPEC;
692     }
693 #endif
694
695     return 0;
696 }
697
698 /* read data from a file */
699 long uc_read(ULONG fid, LARGE_INTEGER offset, ULONG length, ULONG *read, char *data)
700 {
701     ULONG code;
702     cm_scache_t *scp;
703
704     *read = 0;
705  
706     scp = ifs_FindScp(fid);
707     if (!scp)
708         return IFSL_BAD_INPUT;
709
710     if (scp->fileType == CM_SCACHETYPE_DIRECTORY)
711         return IFSL_IS_A_DIR;
712
713     code = ReadData(scp, offset, (unsigned long)length, data, userp, read);
714     MAP_RETURN(code);
715
716     return 0;
717 }
718
719 /* FIXFIX: this does not catch all overquota errors, because the file
720  * is not necessarily written to the server when this returns. */
721 /* write data to a file */
722 long uc_write(ULONG fid, LARGE_INTEGER offset, ULONG length, ULONG *written, char *data)
723 {
724     ULONG code;
725     cm_scache_t *scp;
726
727     scp = ifs_FindScp(fid);
728     if (!scp)
729         return IFSL_BAD_INPUT;
730
731     if (offset.QuadPart == -1)
732         offset = scp->length;
733     code = WriteData(scp, offset, (unsigned long)length, data, userp, written);
734     MAP_RETURN(code);
735
736     return 0;
737 }
738
739 long uc_rename(ULONG fid, WCHAR *curr, WCHAR *new_dir, WCHAR *new_name, ULONG *new_fid)
740 {
741     int code;
742     cm_req_t req;
743     char *curdir, *curfile, *newdir, *newfile;
744     cm_scache_t *dscp1, *dscp2, *scp;
745     char b1[MAX_PATH], b2[MAX_PATH], b3[MAX_PATH];
746     wchar_t b3_w[MAX_PATH];
747
748     code = !(scp = ifs_FindScp(fid));
749     if (!code)
750         code = ifs_ConvertFileName(curr, -1, b1, MAX_PATH);
751     if (!code)
752         code = ifs_ConvertFileName(new_name, -1, b2, MAX_PATH);
753     if (!code)
754         code = ifs_ConvertFileName(new_dir, -1, b3, MAX_PATH);
755     if (!code)
756     {
757         ifs_FindComponents(b1, &curdir, &curfile);
758         ifs_FindComponents(b2, &newdir, &newfile);
759         newdir = b3;
760         uc_close(fid);
761         code = cm_NameI(cm_data.rootSCachep, curdir, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp1);
762     }
763     if (!code)
764     {
765         if (!strcmp(curdir, newdir))
766         {
767             dscp2 = dscp1;
768             dscp1->refCount++;
769         }
770         else
771             code = cm_NameI(cm_data.rootSCachep, newdir, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp2);
772         if (!code)
773         {
774             code = cm_Rename(dscp1, curfile, dscp2, newfile, userp, &req);
775             if (!code)
776             {
777                 strcat(b3, "\\");
778                 strcat(b3, b2);
779                 mbstowcs(b3_w, b3, MAX_PATH);
780                 uc_namei(b3_w, new_fid);
781             }
782             else
783             {
784                 code = uc_namei(curr, new_fid);
785             }
786             ifs_InternalClose(&dscp2);
787         }
788         else
789         {
790             code = uc_namei(curr, new_fid);
791         }
792         ifs_InternalClose(&dscp1);
793     }
794
795     MAP_RETURN(code);
796     return 0;
797 }
798
799 uc_flush(ULONG fid)
800 {
801     ULONG code;
802     cm_scache_t *scp;
803     cm_req_t req;
804
805     scp = ifs_FindScp(fid);
806     if (!scp)
807         return IFSL_BAD_INPUT;
808
809     cm_InitReq(&req);
810     code = cm_FSync(scp, userp, &req);
811
812     MAP_RETURN(code);
813     return 0;
814 }
815
816 ifs_ReaddirCallback(cm_scache_t *scp, cm_dirEntry_t *entry, void *param, osi_hyper_t *offset)
817 {
818     readdir_context_t *context;
819     ULONG name_len;
820     readdir_data_t *info;
821     char short_name[14], *endp;
822     ULONG code;
823     cm_req_t req;
824     cm_scache_t *child_scp;
825     cm_fid_t child_fid;
826     int t;
827
828     context = param;
829
830     name_len = (ULONG) strlen(entry->name);
831
832     info = (readdir_data_t *)context->buf_pos;
833     if (context->length - (context->buf_pos - context->buf) < sizeof(readdir_data_t) + name_len * sizeof(WCHAR) + sizeof(LARGE_INTEGER))
834     {
835         if (context->count == 0)
836             return CM_ERROR_BUFFERTOOSMALL;
837         info->cookie = *offset;
838         return CM_ERROR_STOPNOW;
839     }
840
841     if ((context->matchString && context->matchString[0] && (!strcmp(context->matchString, entry->name) || context->matchString[0]=='*')) ||
842          !(context->matchString && context->matchString[0]))
843         ;       
844     else
845         return 0;
846
847     cm_InitReq(&req);
848     cm_HoldUser(userp);
849     child_scp = NULL;
850
851     child_fid.cell = scp->fid.cell;
852     child_fid.volume = scp->fid.volume;
853     child_fid.vnode = ntohl(entry->fid.vnode);
854     child_fid.unique = ntohl(entry->fid.unique);
855     code = cm_GetSCache(&child_fid, &child_scp, userp, &req);
856     if (code || !child_scp)
857     {
858         cm_ReleaseUser(userp);
859         return 0;
860     }
861
862     {
863         lock_ObtainMutex(&child_scp->mx);
864         code = cm_SyncOp(child_scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
865         lock_ReleaseMutex(&child_scp->mx);
866     }
867
868     if (code)   /* perhaps blank fields we do not know, and continue.  bad filents should not prevent readdirs. */
869         ;
870
871     info->cookie = *offset;
872
873     lock_ObtainMutex(&(child_scp->mx));
874     code = ifs_CopyInfo(child_scp, &info->attribs, &info->size, &info->creation, &info->access, &info->change, &info->write);
875 #if 0
876         /* make files we do not have write access to read-only */
877         /* this is a handy feature, but it takes a lot of time and traffic to enumerate */
878     ifs_CheckAcl(child_scp, FILE_WRITE_DATA, &gr);      /* perhaps add flag to not loop, to avoid network traffic if not found*/
879     if (gr & FILE_READ_DATA && !(gr & FILE_WRITE_DATA))
880         info->attribs |= FILE_ATTRIBUTE_READONLY;
881 #endif
882     lock_ReleaseMutex(&(child_scp->mx));
883     ifs_InternalClose(&child_scp);
884     MAP_RETURN(code);
885
886     cm_Gen8Dot3Name(entry, short_name, &endp);
887     *endp = '\0';
888     info->short_name_length = (CCHAR)sizeof(WCHAR)*((t=MultiByteToWideChar(CP_UTF8, 0, short_name, -1, info->short_name, 14))?t-1:0);
889     info->name_length = sizeof(WCHAR)*((t=MultiByteToWideChar(CP_UTF8, 0, entry->name, -1, info->name, 600))?t-1:0);
890
891     context->buf_pos = ((char*)info) + sizeof(readdir_data_t) + info->name_length;
892     context->count++;
893
894     info = (readdir_data_t *)context->buf_pos;
895     info->cookie.QuadPart = -1;
896
897     return 0;
898 }
899
900 long uc_readdir(ULONG fid, LARGE_INTEGER cookie_in, WCHAR *filter, ULONG *count, char *data, ULONG_PTR *len)
901 {
902     ULONG code;
903     char buffer[2048];
904     cm_req_t req;
905     cm_scache_t *scp;
906     readdir_context_t context;
907     LARGE_INTEGER cookie;
908
909     if (cookie_in.QuadPart == -1)
910     {
911         *len = 0;
912         *count = 0;
913         return 0;
914     }
915
916     scp = ifs_FindScp(fid);
917     if (!scp)
918         return IFSL_BAD_INPUT;
919     code = ifs_ConvertFileName(filter, -1, buffer, 2048);
920     if (code)
921         return code;
922
923     cm_InitReq(&req);
924     cm_HoldUser(userp);
925
926     cookie = cookie_in;
927     context.matchString = buffer;
928     context.buf_pos = context.buf = data;
929     context.length = *len;
930     context.count = 0;
931     *count = 0;
932
933     ((LARGE_INTEGER *)context.buf)->QuadPart = -1;
934
935     code = cm_ApplyDir(scp, ifs_ReaddirCallback, &context, &cookie, userp, &req, NULL);
936
937     context.buf_pos += sizeof(LARGE_INTEGER);
938
939     *count = context.count;
940
941     cm_ReleaseUser(userp);
942     *len = context.buf_pos - context.buf;
943
944     code = ifs_MapCmError(code);
945     return code;
946 }
947
948 long uc_close(ULONG fid)
949 {
950     cm_scache_t *scp;
951     cm_req_t req;
952     scp_status_t *prev, *curr;
953
954     scp = ifs_FindScp(fid);
955     if (!scp)
956         return IFSL_BAD_INPUT;
957
958     cm_InitReq(&req);
959     cm_FSync(scp, userp, &req);
960
961     SCPL_LOCK;  /* perhaps this should be earlier */
962
963     lock_ObtainMutex(&(scp->mx));
964     cm_ReleaseSCache(scp);
965     lock_ReleaseMutex(&(scp->mx));
966  
967     prev = NULL, curr = scp_list_head;
968
969     while (curr)
970     {
971         if (curr->fid == fid)
972         {
973             if (prev)
974                 prev->next = curr->next;
975             else
976                 scp_list_head = curr->next;
977             free(curr);
978             break;
979         }
980         prev = curr;
981         curr = curr->next;
982     }
983
984     SCPL_UNLOCK;
985
986     return 0;
987 }
988
989 long uc_unlink(WCHAR *name)
990 {
991     char buffer[2048];
992     long code;
993     cm_scache_t *dscp;
994     char *dirp, *filep;
995     unsigned char removed;
996     cm_req_t req;
997
998     cm_InitReq(&req);
999
1000     code = ifs_ConvertFileName(name, -1, buffer, 2048);
1001     MAP_RETURN(code);
1002     removed = ifs_FindComponents(buffer, &dirp, &filep);
1003
1004     if (!(*filep))
1005         return IFSL_BADFILENAME;
1006
1007     code = cm_NameI(cm_data.rootSCachep, dirp, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp);
1008     MAP_RETURN(code);
1009
1010     code = cm_Unlink(dscp, filep, userp, &req);
1011     if (code)
1012         code = cm_RemoveDir(dscp, filep, userp, &req);
1013
1014     cm_ReleaseSCache(dscp);
1015     MAP_RETURN(code);
1016
1017     return 0;
1018 }
1019
1020
1021 long uc_ioctl_write(ULONG length, char *data, ULONG_PTR *key)
1022 {
1023     smb_ioctl_t *iop;
1024
1025     iop = malloc(sizeof(smb_ioctl_t));
1026     memset(iop, 0, sizeof(smb_ioctl_t));
1027     smb_IoctlPrepareWrite(NULL, iop);
1028
1029     memcpy(iop->inDatap + iop->inCopied, data, length);
1030     iop->inCopied += length;
1031     *key = (ULONG_PTR)iop;
1032
1033     return 0;
1034 }
1035
1036 long uc_ioctl_read(ULONG_PTR key, ULONG *length, char *data)
1037 {
1038     smb_ioctl_t *iop;
1039
1040     iop = (smb_ioctl_t *)key;
1041     osi_assert(iop);
1042
1043     cm_HoldUser(userp);
1044     smb_IoctlPrepareRead(NULL, iop, userp);
1045     cm_ReleaseUser(userp);
1046
1047     *length = iop->outDatap - iop->outAllocp;
1048     memcpy(data, iop->outAllocp, *length);
1049     free(iop);
1050
1051     return 0;
1052 }
1053
1054 int ifs_Init(char **reason)
1055 {
1056     HANDLE kcom;
1057
1058     kcom = CreateFile("\\\\.\\afscom\\upcallhook", GENERIC_READ | GENERIC_WRITE,
1059                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
1060                        0, NULL);
1061     if (kcom == INVALID_HANDLE_VALUE)
1062     {
1063         *reason = "error creating communications file";
1064         return CM_ERROR_REMOTECONN;
1065     }
1066     CloseHandle(kcom);
1067
1068     memset(user_map, 0, MAX_AFS_USERS*sizeof(struct user_map_entry));
1069     InitializeCriticalSection(&mapLock);
1070     InitializeCriticalSection(&scp_list_lock);
1071
1072     return 0;
1073 }
1074
1075 ifs_TransactRpc(char *outbuf, int outlen, char *inbuf, int *inlen)
1076 {
1077     HANDLE hf;
1078     DWORD read = 0;
1079     DWORD inmax;
1080
1081     if (!outbuf || !inbuf)
1082         return IFSL_GENERIC_FAILURE;
1083
1084     hf = CreateFile("\\\\.\\afscom\\downcall", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
1085     if (hf == INVALID_HANDLE_VALUE)
1086         return 0;
1087
1088     inmax = *inlen;
1089     if (!DeviceIoControl(hf, IOCTL_AFSRDR_DOWNCALL, outbuf, outlen, inbuf, inmax, inlen, NULL))
1090     {
1091         CloseHandle(hf);
1092         return IFSL_GENERIC_FAILURE;
1093     }
1094
1095     CloseHandle(hf);
1096     return inlen ? IFSL_SUCCESS : IFSL_GENERIC_FAILURE;
1097 }
1098
1099
1100 DWORD WINAPI ifs_MainLoop(LPVOID param)
1101 {
1102     HANDLE pipe;
1103     DWORD written;
1104     unsigned char *bufIn, *bufOut;
1105     DWORD lenIn;
1106     rpc_t rpc;
1107     BOOL st;
1108
1109     bufIn = VirtualAlloc(NULL, TRANSFER_BUF_SIZE, MEM_COMMIT, PAGE_READWRITE);
1110     bufOut = VirtualAlloc(NULL, TRANSFER_BUF_SIZE, MEM_COMMIT, PAGE_READWRITE);
1111     if (!bufIn || !bufOut)
1112     {
1113         if (bufIn) VirtualFree(bufIn, 0, MEM_RELEASE);
1114         if (bufOut) VirtualFree(bufOut, 0, MEM_RELEASE);
1115         osi_panic("ifs: allocate transfer buffers", __FILE__, __LINE__);
1116     }
1117
1118     pipe = CreateFile("\\\\.\\afscom\\upcallhook", GENERIC_READ | GENERIC_WRITE,
1119                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
1120                        0, NULL);
1121     if (pipe == INVALID_HANDLE_VALUE)
1122     {
1123         VirtualFree(bufIn, 0, MEM_RELEASE);
1124         VirtualFree(bufOut, 0, MEM_RELEASE);
1125         osi_panic("ifs: creating communications handle", __FILE__, __LINE__);
1126     }
1127
1128     while (1)
1129     {
1130         /* just check if the event is already signalled, do not wait */
1131         if (WaitForSingleObject(WaitToTerminate, 0) == WAIT_OBJECT_0)
1132             break;
1133
1134         /* read request... */
1135         st = ReadFile(pipe, bufIn, TRANSFER_BUF_SIZE, &lenIn, NULL);
1136         if (!st) {
1137         if (GetLastError() == ERROR_INVALID_HANDLE)
1138                 break;
1139         else
1140                 continue;
1141         }
1142
1143         ZeroMemory(&rpc, sizeof(rpc));
1144         rpc.in_buf = rpc.in_pos = bufIn;
1145         rpc.out_buf = rpc.out_pos = bufOut;
1146
1147         /* ...process it... */
1148         rpc_parse(&rpc);
1149
1150         /* ...and write it back */
1151         st = WriteFile(pipe, rpc.out_buf, rpc.out_pos - rpc.out_buf, &written, NULL);
1152         if (!st)
1153             if (GetLastError() == ERROR_INVALID_HANDLE)
1154                 break;
1155             else
1156                 continue;
1157     }
1158
1159     return (DWORD)1;
1160 }