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