0efedbe1f7241debc607a6e100f789baf0ff4184
[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                                         /* defined in multiple places (search source) */
41 #define BUF_FILEHASH(fidp)              ((((fidp)->vnode+((fidp)->unique << 13) + ((fidp)->unique >> (32-13))   \
42                                                                                 +(fidp)->volume+(fidp)->cell)                   \
43                                                                                 /*& 0xffffffff*/))
44 #define ROOTPATH                        "\\"
45 #define TRANSFER_BUF_SIZE               (2*1024*1024)
46 #define SCPL_LOCK                       EnterCriticalSection(&scp_list_lock);
47 #define SCPL_UNLOCK                     LeaveCriticalSection(&scp_list_lock);
48 #define MAX_USERS                       32
49
50 /****************************/
51 /* structs                  */
52 /****************************/
53 struct user_map_entry                   /* how we keep users straight.  total of MAX_USERS of these */
54 {
55     LARGE_INTEGER id;                   /* internal id created by kernel */
56     cm_user_t *creds;                   /* global (thread-specific) var userp is set to this */
57 };
58
59 struct scp_status                       /* one for each unique file in afs */
60 {
61     struct scp_status *next;            /* stored in a global chain in a chain locked by SCPL_[UN]LOCK */
62     cm_scache_t *scp;                   /* file handle used with cm_ fns */
63     ULONG fid;                          /* internal id generated by BUF_FILEHASH from AFS's 128-bit FID */
64 };
65 typedef struct scp_status scp_status_t;
66
67 struct readdir_context                  /* temporary struct, allocated as necessary, for cm_Apply callback */
68 {
69     char *matchString;                  /* for matching against */
70     char *buf, *buf_pos;                /* filling buffer to length, currently at buf_pos */
71     ULONG length;
72     ULONG count;                        /* number of entries packed so far */
73 };
74 typedef struct readdir_context readdir_context_t;
75
76
77 /****************************/
78 /* global vars              */
79 /****************************/
80 __declspec(thread) cm_user_t *userp;
81 struct user_map_entry user_map[MAX_USERS];
82
83 CRITICAL_SECTION mapLock, scp_list_lock;
84
85 extern HANDLE DoTerminate;
86
87 scp_status_t *scp_list_head = NULL;
88
89
90 /****************************/
91 /* error functions          */
92 /****************************/
93 char *IfslErrorToText(unsigned long ifsl)
94 {
95     switch (ifsl)
96     {
97     case IFSL_SUCCESS:
98         return "success";
99     case IFSL_DOES_NOT_EXIST:
100         return "does not exist";
101     case IFSL_NOT_IMPLEMENTED:
102         return "not implemented";
103     case IFSL_END_OF_ENUM:
104         return "end of enum";
105     case IFSL_CANNOT_MAKE:
106         return "cannot make";
107     case IFSL_END_OF_FILE:
108         return "end of file";
109     case IFSL_NO_ACCESS:
110         return "no access";
111     case IFSL_BUFFER_TOO_SMALL:
112         return "buffer too small";
113     case IFSL_SHARING_VIOLATION:
114         return "sharing violation";
115     case IFSL_BAD_INPUT:
116         return "bad input";
117     case IFSL_GENERIC_FAILURE:
118         return "generic failure";
119     case IFSL_OPEN_CREATED:
120         return "open created";
121     case IFSL_OPEN_EXISTS:
122         return "open exists";
123     case IFSL_OPEN_OPENED:
124         return "opened";
125     case IFSL_OPEN_OVERWRITTEN:
126         return "overwritten";
127     case IFSL_OPEN_SUPERSCEDED:
128         return "supersceded";
129     case IFSL_BADFILENAME:
130         return "bad filename";
131     case IFSL_READONLY:
132         return "read only";
133     case IFSL_IS_A_DIR:
134         return "is a dir";
135     case IFSL_PATH_DOES_NOT_EXIST:
136         return "path does not exist";
137     case IFSL_IS_A_FILE:
138         return "is a file";
139     case IFSL_NOT_EMPTY:
140         return "dir not empty";
141     case IFSL_UNSPEC:
142         return "unspecified error";
143     default:
144         return "NOT FOUND";
145     }
146 }       
147
148 unsigned long ifs_MapCmError(unsigned long code)
149 {
150     switch (code)
151     {
152     case CM_ERROR_STOPNOW:
153     case 0:
154         return IFSL_SUCCESS;
155     case CM_ERROR_NOSUCHCELL:
156     case CM_ERROR_NOSUCHVOLUME:
157     case CM_ERROR_NOSUCHFILE:           // x
158         return IFSL_DOES_NOT_EXIST;
159     case CM_ERROR_NOSUCHPATH:           // x
160         return IFSL_PATH_DOES_NOT_EXIST;
161     case CM_ERROR_BADNTFILENAME:
162         return IFSL_BADFILENAME;
163     case CM_ERROR_TIMEDOUT:
164     case CM_ERROR_ALLOFFLINE:
165     case CM_ERROR_CLOCKSKEW:
166     case CM_ERROR_REMOTECONN:
167     case CM_ERROR_ALLBUSY:
168         return IFSL_GENERIC_FAILURE;
169     case CM_ERROR_NOACCESS:
170         return IFSL_NO_ACCESS;
171     case CM_ERROR_RETRY:
172     case CM_ERROR_TOOBIG:
173     case CM_ERROR_BADFD:
174     case CM_ERROR_BADFDOP:
175     case CM_ERROR_CROSSDEVLINK:
176         return IFSL_GENERIC_FAILURE;
177     case CM_ERROR_EXISTS:
178         return IFSL_OPEN_EXISTS;
179     case CM_ERROR_BADOP:
180     case CM_ERROR_INVAL:
181         case CM_ERROR_UNKNOWN:
182     case CM_ERROR_BADSMB:
183         return IFSL_GENERIC_FAILURE;//TODO:? ERR - STATUS_NO_MORE_FILES;
184     case CM_ERROR_NOTDIR:
185     case CM_ERROR_ISDIR:
186     case CM_ERROR_READONLY:
187         return IFSL_BAD_INPUT;
188     case CM_ERROR_BUFFERTOOSMALL:
189         return IFSL_BUFFER_TOO_SMALL;
190     case CM_ERROR_WOULDBLOCK:
191     case CM_ERROR_BADSHARENAME:
192     case CM_ERROR_NOMORETOKENS:
193     case CM_ERROR_NOTEMPTY:
194     case CM_ERROR_USESTD:
195     case CM_ERROR_ATSYS:
196         return IFSL_GENERIC_FAILURE;
197     case CM_ERROR_NOFILES:
198     case CM_ERROR_BADTID:
199         return IFSL_END_OF_ENUM;
200     case CM_ERROR_PARTIALWRITE:
201     case CM_ERROR_NOIPC:
202     case CM_ERROR_RENAME_IDENTICAL:
203     case CM_ERROR_AMBIGUOUS_FILENAME:
204         return IFSL_GENERIC_FAILURE;
205     case IFSL_SHARING_VIOLATION:
206         return IFSL_SHARING_VIOLATION;
207     case IFSL_NOT_EMPTY:
208         return IFSL_NOT_EMPTY;
209     case CM_ERROR_SPACE:
210     case CM_ERROR_QUOTA:
211         return IFSL_OVERQUOTA;  
212     }
213     return IFSL_GENERIC_FAILURE;
214 }
215
216
217 /****************************/
218 /* support fns              */
219 /****************************/
220 cm_scache_t *ifs_FindScp(ULONG fid)                     /* walk list to find scp<->fid mapping */
221 {
222     scp_status_t *curr;
223
224     SCPL_LOCK;
225
226     curr = scp_list_head;
227     while (curr)
228     {
229         if (curr->fid == fid)
230         {
231             SCPL_UNLOCK;
232             return curr->scp;
233         }
234         curr = curr->next;
235     }
236     SCPL_UNLOCK;
237     return NULL;
238 }
239
240 /* must call with scp write-locked.  will always return correct results
241    unless network fails (it loops properly). */
242 ifs_CheckAcl(cm_scache_t *scp, ULONG access, ULONG *granted)
243 {
244     long outRights, code;
245     cm_req_t req;
246
247     cm_InitReq(&req);
248
249     /* ripped from cm_scache.c */
250     while (1)
251     {
252         if (cm_HaveAccessRights(scp, userp, access, granted))
253         {
254             return 0;
255         }
256         else
257         {
258             /* we don't know the required access rights */
259             code = cm_GetAccessRights(scp, userp, &req);
260             MAP_RETURN(code);
261             continue;
262         }
263     }
264
265     return 0;
266 }
267
268 /* extract data from scp.  in ifs_ support function to centralize changes. */
269 ifs_CopyInfo(cm_scache_t *scp, ULONG *attribs, LARGE_INTEGER *size,
270               LARGE_INTEGER *creation, LARGE_INTEGER *access,
271               LARGE_INTEGER *change, LARGE_INTEGER *written)
272 {
273     access->QuadPart = 0;                               /* these mappings are not quite correct.  we have the */
274     change->QuadPart = scp->clientModTime;              /* right to leave them zero, if necessary. */
275     written->QuadPart = scp->clientModTime;
276     creation->QuadPart = scp->serverModTime;
277
278     *attribs = 0;
279     if (scp->fileType == CM_SCACHETYPE_DIRECTORY ||
280         scp->fileType == CM_SCACHETYPE_SYMLINK ||
281         scp->fileType == CM_SCACHETYPE_MOUNTPOINT/* ||
282         scp->fileType == 0*/)
283         *attribs |= FILE_ATTRIBUTE_DIRECTORY;
284
285     /*if (!attribs && scp->fileType == CM_SCACHETYPE_FILE)
286     *attribs |= FILE_ATTRIBUTE_NORMAL;*/
287
288     if (*attribs == FILE_ATTRIBUTE_DIRECTORY)
289         size->QuadPart = 0;
290     else
291         *size = scp->length;
292
293     return 0;
294 }
295
296
297 /* close and zero scp pointer.  zeroing pointer should
298    help eliminate accessing discarded cache entries. */
299 void ifs_InternalClose(cm_scache_t **scp)
300 {
301     osi_assert(scp && *scp);
302     lock_ObtainMutex(&((*scp)->mx));
303     cm_ReleaseSCache(*scp);
304     if ((*scp)->refCount == 0)                  /* we haven't held scache for external use yet */
305         cm_DiscardSCache(*scp);
306     lock_ReleaseMutex(&((*scp)->mx));
307     *scp = NULL;
308 }
309
310 /* normalizes path by removing trailing slashes.  separates last
311    path component with a null, so that *dirp points to parent path
312    and *filep points to filename.  modifies string path. */
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_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 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 = 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     SCPL_LOCK;
445     st = malloc(sizeof(scp_status_t));
446     st->scp = scp;
447     st->fid = BUF_FILEHASH(&scp->fid);
448     st->next = scp_list_head;
449     scp_list_head = st;
450     SCPL_UNLOCK;
451
452     *fid = st->fid;
453     free(buffer);
454
455     return 0;
456 }
457
458 /* this should only be called right after open, so we do not need to stat file.
459  * we only check the server's restrictions.  sharing violations are handled in the
460  * kernel. the access mode we grant sticks with the file_object until its death. */
461 uc_check_access(ULONG fid, ULONG access, ULONG *granted)
462 {
463     ULONG afs_acc, afs_gr;
464     cm_scache_t *scp;
465     ULONG gr;
466     BOOLEAN file, dir;
467
468     gr = 0;
469  
470     scp = ifs_FindScp(fid);
471     if (!scp)
472         return IFSL_BAD_INPUT;
473
474     file = (scp->fileType == CM_SCACHETYPE_FILE);
475     dir = !file;
476
477     /* access definitions from prs_fs.h */
478     afs_acc = 0;
479     if (access & FILE_READ_DATA)
480         afs_acc |= PRSFS_READ;
481     if (file && ((access & FILE_WRITE_DATA) || (access & FILE_APPEND_DATA)))
482         afs_acc |= PRSFS_WRITE;
483     if (access & FILE_WRITE_EA || access & FILE_WRITE_ATTRIBUTES)
484         afs_acc |= PRSFS_WRITE;
485     if (dir && ((access & FILE_ADD_FILE) || (access & FILE_ADD_SUBDIRECTORY)))
486         afs_acc |= PRSFS_INSERT;
487     if (dir && (access & FILE_LIST_DIRECTORY))
488         afs_acc |= PRSFS_LOOKUP;
489     if (access & FILE_READ_EA || access & FILE_READ_ATTRIBUTES)
490         afs_acc |= PRSFS_LOOKUP;
491     if (file && (access & FILE_EXECUTE)) /* look at making this require write access */
492         afs_acc |= PRSFS_WRITE;
493     if (dir && (access & FILE_TRAVERSE))
494         afs_acc |= PRSFS_READ;
495     if (dir && (access & FILE_DELETE_CHILD))
496         afs_acc |= PRSFS_DELETE;
497     if (/*file && */(access & DELETE))
498         afs_acc |= PRSFS_DELETE;
499
500     /* check ACL with server */
501     lock_ObtainMutex(&(scp->mx));
502     ifs_CheckAcl(scp, afs_acc, &afs_gr);
503     lock_ReleaseMutex(&(scp->mx));
504
505     *granted = 0;
506     if (afs_gr & PRSFS_READ)
507         *granted |= FILE_READ_DATA | FILE_EXECUTE;
508     if (afs_gr & PRSFS_WRITE)
509         *granted |= FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_EXECUTE; // last one hack
510     if (afs_gr & PRSFS_INSERT)
511         *granted |= (dir ? FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY : 0) | (file ? FILE_ADD_SUBDIRECTORY : 0);
512     if (afs_gr & PRSFS_LOOKUP)
513         *granted |= (dir ? FILE_LIST_DIRECTORY : 0) | FILE_READ_EA | FILE_READ_ATTRIBUTES;
514     if (afs_gr & PRSFS_DELETE)
515         *granted |= FILE_DELETE_CHILD | DELETE;
516     if (afs_gr & PRSFS_LOCK)
517         *granted |= 0;
518     if (afs_gr & PRSFS_ADMINISTER)
519         *granted |= 0;
520
521     * granted |= SYNCHRONIZE | READ_CONTROL;
522
523     return 0;
524 }
525
526 uc_create(WCHAR *name, ULONG attribs, LARGE_INTEGER alloc, ULONG access, ULONG *granted, ULONG *fid)
527 {
528     char *buffer;                                       /* we support semi-infinite path lengths */
529     long code;
530     cm_scache_t *scp, *dscp;
531     char *dirp, *filep;
532     unsigned char removed;
533     cm_req_t req;
534     scp_status_t *st;
535     cm_attr_t attr;
536     short len;
537
538     cm_InitReq(&req);
539
540     len = wcslen(name)+20;                      /* characters *should* map 1<->1, but in case */
541     buffer = malloc(len);
542     code = ifs_ConvertFileName(name, -1, buffer, len);
543     if (code)
544     {
545         free(buffer);
546         MAP_RETURN(code);
547     }
548     removed = ifs_FindComponents(buffer, &dirp, &filep);
549
550     /* lookup the parent directory, which must exist */
551     code = cm_NameI(cm_data.rootSCachep, dirp, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp);
552     if (code)
553     {
554         free(buffer);
555         MAP_RETURN(code);
556     }
557
558     osi_assert(filep);
559     if (*filep)
560     {
561         attr.mask = CM_ATTRMASK_LENGTH;
562         attr.length = alloc;
563
564         if (attribs & FILE_ATTRIBUTE_DIRECTORY)
565         {
566             code = cm_MakeDir(dscp, filep, 0, &attr, userp, &req);
567             if (!code)
568                 code = cm_Lookup(dscp, filep, 0, userp, &req, &scp);
569         }
570         else
571         {
572             /* for debugging strange error */
573             /*if (!strcmp(filep+strlen(filep)-3, "478") ||
574             !strcmp(filep+strlen(filep)-3, "503"))
575             _asm int 3;*/
576             code = cm_Create(dscp, filep, 0, &attr, &scp, userp, &req);
577         }
578     }
579     cm_ReleaseSCache(dscp);
580
581     if (code)
582     {
583         free(buffer);
584         MAP_RETURN(code);
585     }
586
587     SCPL_LOCK;
588     st = malloc(sizeof(scp_status_t));
589     st->scp = scp;
590     st->fid = BUF_FILEHASH(&scp->fid);
591     st->next = scp_list_head;
592     scp_list_head = st;
593     SCPL_UNLOCK;
594
595     *fid = st->fid;
596     *granted = access;
597
598     return 0;
599 }
600
601 /* this does not fill the attribs member completely.  additional flags must
602    be added in the kernel, such as read-only. */
603 uc_stat(ULONG fid, ULONG *attribs, LARGE_INTEGER *size, LARGE_INTEGER *creation,
604                 LARGE_INTEGER *access, LARGE_INTEGER *change, LARGE_INTEGER *written)
605 {
606     cm_scache_t *scp;
607     cm_req_t req;
608     ULONG code;
609
610     scp = ifs_FindScp(fid);
611     if (!scp)
612         return IFSL_BAD_INPUT;
613
614     /* stat file; don't want callback */
615     cm_InitReq(&req);
616     lock_ObtainMutex(&(scp->mx));
617     cm_HoldUser(userp);
618     code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS);
619     cm_ReleaseUser(userp);
620
621     if (code)
622         lock_ReleaseMutex(&(scp->mx));
623     MAP_RETURN(code);
624
625     code = ifs_CopyInfo(scp, attribs, size, creation, access, change, written);
626     lock_ReleaseMutex(&(scp->mx));
627     MAP_RETURN(code);
628
629     return 0;
630 }
631
632 /* set atime, mtime, etc. */
633 uc_setinfo(ULONG fid, ULONG attribs, LARGE_INTEGER creation, LARGE_INTEGER access,
634                    LARGE_INTEGER change, LARGE_INTEGER written)
635 {
636     return IFSL_GENERIC_FAILURE;
637 }
638
639 //FIX/quota errors
640 /* truncate or extend file, in cache and on server */
641 uc_trunc(ULONG fid, LARGE_INTEGER size)
642 {
643     ULONG code, gr;
644     cm_scache_t *scp;
645     cm_req_t req;
646     osi_hyper_t oldLen, writePos;
647     long written;
648
649     scp = ifs_FindScp(fid);
650     if (!scp)
651         return IFSL_BAD_INPUT;
652
653     /*code = ifs_CheckAcl(scp, FILE_WRITE_DATA, &gr);
654     if (code)
655     return code;
656     if (!(gr & FILE_WRITE_DATA))
657     return IFSL_NO_ACCESS;*/
658
659     cm_InitReq(&req);
660     lock_ObtainMutex(&(scp->mx));
661
662     code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS);
663
664     if (code)
665         lock_ReleaseMutex(&(scp->mx));
666     MAP_RETURN(code);
667
668     oldLen = scp->length;
669     lock_ReleaseMutex(&(scp->mx));
670
671     code = cm_SetLength(scp, &size, userp, &req);
672     MAP_RETURN(code);
673     /*code = cm_FSync(scp, userp, &req);
674     MAP_RETURN(code);*/
675     /*code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS);
676     MAP_RETURN(code);*/
677
678 #if 0
679     /* attempt to write last byte of file.  fails to help because of delayed writing. */
680     if (oldLen.QuadPart < size.QuadPart)
681     {
682         writePos.QuadPart = size.QuadPart - 1;
683         WriteData(scp, writePos, 1, &"\0\0\0", userp, &written);
684         MAP_RETURN(code);
685         if (written != 1)
686             return IFSL_UNSPEC;
687     }
688 #endif
689     /*cm_SyncOp(scp, NULL, 
690     cm_Flush(*/
691     //MAP_RETURN(code);
692
693     return 0;
694 }
695
696 /* read data from a file */
697 uc_read(ULONG fid, LARGE_INTEGER offset, ULONG length, ULONG *read, char *data)
698 {
699     ULONG code;
700     cm_scache_t *scp;
701     cm_req_t req;
702
703     //_asm int 3;
704
705     *read = 0;
706  
707     scp = ifs_FindScp(fid);
708     if (!scp)
709         return IFSL_BAD_INPUT;
710
711     if (scp->fileType == CM_SCACHETYPE_DIRECTORY)
712         return IFSL_IS_A_DIR;
713
714     code = ReadData(scp, offset, (unsigned long)length, data, userp, read);
715     MAP_RETURN(code);
716
717     return 0;
718 }
719
720 //FIX/ handle quota errors properly
721 /* write data to a file */
722 uc_write(ULONG fid, LARGE_INTEGER offset, ULONG length, ULONG *written, char *data)
723 {
724     ULONG code, gr;
725     cm_scache_t *scp;
726     cm_req_t req;
727
728     scp = ifs_FindScp(fid);
729     if (!scp)
730         return IFSL_BAD_INPUT;
731
732     /*code = ifs_CheckAcl(scp, FILE_WRITE_DATA, &gr);
733     if (code)
734     return code;
735     if (!(gr & FILE_WRITE_DATA))
736     return IFSL_NO_ACCESS;*/
737
738     if (offset.QuadPart == -1)                          // perhaps re-stat here?
739         offset = scp->length;
740     code = WriteData(scp, offset, (unsigned long)length, data, userp, written);
741     MAP_RETURN(code);
742
743     return 0;
744 }
745
746 //need downcall for new length
747
748 uc_rename(ULONG fid, WCHAR *curr, WCHAR *new_dir, WCHAR *new_name, ULONG *new_fid)
749 {
750     int code;
751     cm_req_t req;
752     //struct vnode *node;
753     cm_attr_t attr;
754     wchar_t *buf;
755     char *curdir, *curfile, *newdir, *newfile;
756     cm_scache_t *dscp1, *dscp2, *scp;
757     char b1[MAX_PATH], b2[MAX_PATH], b3[MAX_PATH];
758     ULONG fid2;
759
760
761     code = !(scp = ifs_FindScp(fid));
762     if (!code)
763         code = ifs_ConvertFileName(curr, -1, b1, MAX_PATH);
764     if (!code)
765         code = ifs_ConvertFileName(new_name, -1, b2, MAX_PATH);
766     if (!code)
767         code = ifs_ConvertFileName(new_dir, -1, b3, MAX_PATH);
768     if (!code)
769     {
770         ifs_FindComponents(b1, &curdir, &curfile);
771         ifs_FindComponents(b2, &newdir, &newfile);
772         newdir = b3;
773         /*lock_ReleaseMutex(&scp->mx);
774         cm_FSync(scp, userp, &req);
775         if (scp->refCount != 1)
776         _asm int 3;
777         ifs_InternalClose(&scp);*/
778         uc_close(fid);
779         code = cm_NameI(cm_data.rootSCachep, curdir, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp1);
780     }
781     if (!code)
782     {
783         if (!strcmp(curdir, newdir))
784         {
785             dscp2 = dscp1;
786             dscp1->refCount++;
787         }
788         else
789             code = cm_NameI(cm_data.rootSCachep, newdir, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp2);
790         if (!code)
791         {
792             code = cm_Rename(dscp1, curfile, dscp2, newfile, userp, &req);
793             //ifs_InternalClose(&dscp2);
794             //cm_InitReq(&req);
795             //code = cm_NameI(cm_rootSCachep, newdir, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp2);
796             if (!code)
797             {
798                 code = ifs_ConvertFileName(curr, -1, b1, MAX_PATH);
799                 code = uc_namei(b1, new_fid);
800                 //if (fid != fid2)
801                 //      _asm int 3;
802                 //code = cm_Lookup(dscp2, newfile, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW, userp, &req, &node->scp);
803             }
804             ifs_InternalClose(&dscp2);
805         }
806         else
807         {
808             //code = cm_Lookup(dscp1, curfile, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW, userp, &req, &node->scp);
809             code = ifs_ConvertFileName(curr, -1, b1, MAX_PATH);
810             code = uc_namei(b1, new_fid);
811             //if (fid != fid2)
812             //  _asm int 3;
813         }
814         ifs_InternalClose(&dscp1);
815     }
816
817     return 0;
818 }
819
820 ifs_ReaddirCallback(cm_scache_t *scp, cm_dirEntry_t *entry, void *param, osi_hyper_t *offset)
821 {
822     readdir_context_t *context;
823     ULONG name_len, gr;
824     readdir_data_t *info;
825     char short_name[14], *endp;
826     ULONG code;
827     cm_req_t req;
828     cm_scache_t *child_scp;
829     cm_fid_t child_fid;
830     int t;
831
832     context = param;
833
834     name_len = strlen(entry->name);
835
836     info = (readdir_data_t *)context->buf_pos;
837     if (context->length - (context->buf_pos - context->buf) < sizeof(readdir_data_t) + name_len * sizeof(WCHAR) + sizeof(LARGE_INTEGER))
838     {
839         if (context->count == 0)
840             return CM_ERROR_BUFFERTOOSMALL;
841         info->cookie = *offset;
842         return CM_ERROR_STOPNOW;
843     }
844
845     if ((context->matchString && context->matchString[0] && (!strcmp(context->matchString, entry->name) || context->matchString[0]=='*')) ||
846          !(context->matchString && context->matchString[0]))
847         ;       
848     else
849         return 0;
850
851     cm_InitReq(&req);
852     cm_HoldUser(userp);
853     child_scp = NULL;
854
855     child_fid.cell = scp->fid.cell;
856     child_fid.volume = scp->fid.volume;
857     child_fid.vnode = ntohl(entry->fid.vnode);
858     child_fid.unique = ntohl(entry->fid.unique);
859     code = cm_GetSCache(&child_fid, &child_scp, userp, &req);
860     //code = cm_Lookup(scp, entry->name, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW, userp, &req, &child_scp);
861     if (code || !child_scp)
862     {
863         cm_ReleaseUser(userp);
864         return 0;
865     }
866
867     //if (child_scp->refCount == 1)
868     {
869         lock_ObtainMutex(&child_scp->mx);
870         code = cm_SyncOp(child_scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);                // do not need callback
871         lock_ReleaseMutex(&child_scp->mx);
872     }
873
874     if (code)   /* perhaps blank fields we do not know, and continue.  bad filents should not prevent readdirs. */
875         ;
876
877     info->cookie = *offset;
878
879     lock_ObtainMutex(&(child_scp->mx));
880     code = ifs_CopyInfo(child_scp, &info->attribs, &info->size, &info->creation, &info->access, &info->change, &info->write);
881     //ifs_CheckAcl(child_scp, FILE_WRITE_DATA, &gr);    /* perhaps add flag to not loop, to avoid network traffic if not found*/
882     //if (gr & FILE_READ_DATA && !(gr & FILE_WRITE_DATA))
883     //  info->attribs |= FILE_ATTRIBUTE_READONLY;
884     lock_ReleaseMutex(&(child_scp->mx));
885     ifs_InternalClose(&child_scp);
886     MAP_RETURN(code);
887
888     cm_Gen8Dot3Name(entry, short_name, &endp);
889     *endp = '\0';
890     info->short_name_length = sizeof(WCHAR)*((t=MultiByteToWideChar(CP_UTF8, 0, short_name, -1, info->short_name, 14))?t-1:0);
891     info->name_length = sizeof(WCHAR)*((t=MultiByteToWideChar(CP_UTF8, 0, entry->name, -1, info->name, 600))?t-1:0);
892
893     context->buf_pos = ((char*)info) + sizeof(readdir_data_t) + info->name_length;
894     context->count++;
895
896     info = (readdir_data_t *)context->buf_pos;
897     info->cookie.QuadPart = -1;
898
899     return 0;
900 }
901
902 uc_readdir(ULONG fid, LARGE_INTEGER cookie_in, WCHAR *filter, ULONG *count, char *data, ULONG *len)
903 {
904     ULONG code;
905     char buffer[2048];
906     cm_req_t req;
907     cm_scache_t *scp, *child_scp;
908     readdir_context_t context;
909     LARGE_INTEGER cookie;
910
911     if (cookie_in.QuadPart == -1)
912     {
913         *len = 0;
914         *count = 0;
915         return 0;
916     }
917
918     scp = ifs_FindScp(fid);
919     if (!scp)
920         return IFSL_BAD_INPUT;
921     code = ifs_ConvertFileName(filter, -1, buffer, 2048);
922     if (code)
923         return code;
924
925     cm_InitReq(&req);
926     cm_HoldUser(userp);
927
928     cookie = cookie_in;
929     context.matchString = buffer;
930     context.buf_pos = context.buf = data;
931     context.length = *len;
932     context.count = 0;
933     *count = 0;
934
935     //restart:
936
937     ((LARGE_INTEGER *)context.buf)->QuadPart = -1;
938
939     code = cm_ApplyDir(scp, ifs_ReaddirCallback, &context, &cookie, userp, &req, NULL);
940
941     context.buf_pos += sizeof(LARGE_INTEGER);
942
943     if (code != CM_ERROR_STOPNOW)
944         goto done;
945
946     //(*count)++;
947
948     if (code)
949         goto done;
950
951     //goto restart;
952
953     code = 0;
954
955   done:
956
957     *count = context.count;
958
959     cm_ReleaseUser(userp);
960     *len = context.buf_pos - context.buf;
961
962     code = ifs_MapCmError(code);
963     return code;
964 }
965
966 uc_close(ULONG fid)
967 {
968     ULONG code;
969     cm_scache_t *scp;
970     cm_req_t req;
971     scp_status_t *prev, *curr;
972
973     scp = ifs_FindScp(fid);
974     if (!scp)
975         return IFSL_BAD_INPUT;
976
977     cm_InitReq(&req);
978     cm_FSync(scp, userp, &req);
979
980     SCPL_LOCK;  /* perhaps this should be earlier */
981
982     lock_ObtainMutex(&(scp->mx));
983     cm_ReleaseSCache(scp);
984     if (!scp->refCount)
985         cm_DiscardSCache(scp);
986     lock_ReleaseMutex(&(scp->mx));
987  
988     prev = NULL, curr = scp_list_head;
989
990     while (curr)
991     {
992         if (curr->fid == fid)
993         {
994             if (prev)
995                 prev->next = curr->next;
996             else
997                 scp_list_head = curr->next;
998             free(curr);
999             break;
1000         }
1001         prev = curr;
1002         curr = curr->next;
1003     }
1004
1005     SCPL_UNLOCK;
1006
1007     return 0;
1008 }
1009
1010 uc_unlink(WCHAR *name)
1011 {
1012     char buffer[2048];
1013     long code;
1014     cm_scache_t *scp, *dscp;
1015     char *dirp, *filep;
1016     unsigned char removed;
1017     cm_req_t req;
1018     scp_status_t *st;
1019
1020     cm_InitReq(&req);
1021
1022     code = ifs_ConvertFileName(name, -1, buffer, 2048);
1023     MAP_RETURN(code);
1024     removed = ifs_FindComponents(buffer, &dirp, &filep);
1025
1026     code = cm_NameI(cm_data.rootSCachep, dirp, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp);
1027     MAP_RETURN(code);
1028     if (*filep)
1029     {
1030         code = cm_Unlink(dscp, filep, userp, &req);
1031         if (code)
1032             code = cm_RemoveDir(dscp, filep, userp, &req);
1033     }
1034     else
1035         ;               /* problem */
1036     cm_ReleaseSCache(dscp);
1037     MAP_RETURN(code);
1038
1039     return 0;
1040 }
1041
1042
1043 int uc_ioctl_write(ULONG length, char *data, ULONG *key)
1044 {
1045     int code;
1046     cm_req_t req;
1047     smb_ioctl_t *iop;
1048
1049     iop = malloc(sizeof(smb_ioctl_t));
1050     memset(iop, 0, sizeof(smb_ioctl_t));
1051     smb_IoctlPrepareWrite(NULL, iop);
1052
1053     memcpy(iop->inDatap + iop->inCopied, data, length);
1054     iop->inCopied += length;
1055     *key = (ULONG)iop;
1056
1057     return 0;
1058 }
1059
1060 int uc_ioctl_read(ULONG key, ULONG *length, char *data)
1061 {
1062     int code;
1063     cm_req_t req;
1064     smb_ioctl_t *iop;
1065
1066     iop = key;
1067     osi_assert(iop);
1068
1069     cm_HoldUser(userp);
1070     smb_IoctlPrepareRead(NULL, iop, userp);
1071     cm_ReleaseUser(userp);
1072
1073     *length = iop->outDatap - iop->outAllocp;
1074     memcpy(data, iop->outAllocp, *length);
1075     free(iop);
1076
1077     return 0;
1078 }
1079
1080 int ifs_Init(char **reason)
1081 {
1082     HANDLE kcom;
1083
1084     //_asm int 3;
1085     kcom = CreateFile("\\\\.\\afscom\\upcallhook", GENERIC_READ | GENERIC_WRITE,
1086                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
1087                        /*FILE_FLAG_OVERLAPPED*/0, NULL);
1088     if (kcom == INVALID_HANDLE_VALUE)
1089     {
1090         *reason = "error creating communications file";
1091         return CM_ERROR_REMOTECONN;
1092     }
1093     CloseHandle(kcom);
1094
1095     memset(user_map, 0, 32*sizeof(struct user_map_entry));
1096     InitializeCriticalSection(&mapLock);
1097     InitializeCriticalSection(&scp_list_lock);
1098
1099     return 0;
1100 }
1101
1102 ifs_TransactRpc(char *outbuf, int outlen, char *inbuf, int *inlen)
1103 {
1104     HANDLE hf;
1105     int ret;
1106     DWORD err, read = 0;
1107     DWORD inmax;
1108
1109     if (!outbuf || !inbuf)
1110         return IFSL_GENERIC_FAILURE;
1111
1112     hf = CreateFile("\\\\.\\afscom\\downcall", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
1113     if (hf == INVALID_HANDLE_VALUE)
1114         return 0;
1115
1116     inmax = *inlen;
1117     if (!DeviceIoControl(hf, IOCTL_AFSRDR_DOWNCALL, outbuf, outlen, inbuf, inmax, inlen, NULL))
1118     {
1119         CloseHandle(hf);
1120         return IFSL_GENERIC_FAILURE;
1121     }
1122
1123     CloseHandle(hf);
1124     return inlen ? IFSL_SUCCESS : IFSL_GENERIC_FAILURE;
1125 }
1126
1127
1128 DWORD WINAPI ifs_MainLoop(LPVOID param)
1129 {
1130     HANDLE pipe;
1131     DWORD written;
1132     unsigned char *bufIn, *bufOut;
1133     DWORD lenIn, lenOut, status;
1134     DWORD err;
1135     OVERLAPPED olp;
1136     rpc_t rpc;
1137     BOOL st;
1138
1139     bufIn = VirtualAlloc(NULL, TRANSFER_BUF_SIZE, MEM_COMMIT, PAGE_READWRITE);
1140     bufOut = VirtualAlloc(NULL, TRANSFER_BUF_SIZE, MEM_COMMIT, PAGE_READWRITE);
1141     if (!bufIn || !bufOut)
1142     {
1143         if (bufIn) VirtualFree(bufIn, 0, MEM_RELEASE);
1144         if (bufOut) VirtualFree(bufOut, 0, MEM_RELEASE);
1145         printf("could not allocate transfer bufs\n");
1146         PostMessage(NULL, WM_CLOSE, 0, 0);
1147         return 1;
1148     }
1149
1150     //_asm int 3;
1151     pipe = CreateFile("\\\\.\\afscom\\upcallhook", GENERIC_READ | GENERIC_WRITE,
1152                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
1153                        /*FILE_FLAG_OVERLAPPED*/0, NULL);
1154     if (pipe == INVALID_HANDLE_VALUE)
1155     {
1156         VirtualFree(bufIn, 0, MEM_RELEASE);
1157         VirtualFree(bufOut, 0, MEM_RELEASE);
1158         printf("error creating communications file\n");
1159         PostMessage(NULL, WM_CLOSE, 0, 0);
1160         return 1;
1161     }
1162
1163     //_asm int 3;
1164     while (1)
1165     {
1166         if (WaitForSingleObject(DoTerminate, 0) == WAIT_OBJECT_0)
1167             break;
1168         st = ReadFile(pipe, bufIn, TRANSFER_BUF_SIZE, &lenIn, NULL);
1169         if (!st)
1170             if (GetLastError() == ERROR_INVALID_HANDLE)
1171                 break;
1172             else
1173                 continue;
1174
1175         ZeroMemory(&rpc, sizeof(rpc));
1176         rpc.in_buf = rpc.in_pos = bufIn;
1177         rpc.out_buf = rpc.out_pos = bufOut;
1178
1179         rpc_parse(&rpc);
1180
1181         st = WriteFile(pipe, rpc.out_buf, rpc.out_pos - rpc.out_buf, &written, NULL);
1182         if (!st)
1183             if (GetLastError() == ERROR_INVALID_HANDLE)
1184                 break;
1185             else
1186                 continue;
1187     }       
1188
1189     return 1;
1190 }