winnt-win2000-win98-afs-client-updates-20010623
[openafs.git] / src / WINNT / afsd / cm_ioctl.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #else
16 #include <sys/socket.h>
17 #endif /* !DJGPP */
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <malloc.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <time.h>
24
25 #include <osi.h>
26
27 #include "afsd.h"
28 #include "afsd_init.h"
29
30 #include "smb.h"
31
32 #ifndef DJGPP
33 #include <rxkad.h>
34 #include "afsrpc.h"
35 #else
36 #include <rx/rxkad.h>
37 #include "afsrpc95.h"
38 #endif
39
40 #include "cm_rpc.h"
41
42 /* Copied from afs_tokens.h */
43 #define PIOCTL_LOGON    0x1
44 #define MAX_PATH 260
45
46 osi_mutex_t cm_Afsdsbmt_Lock;
47
48 extern afs_int32 cryptall;
49
50 void cm_InitIoctl(void)
51 {
52         lock_InitializeMutex(&cm_Afsdsbmt_Lock, "AFSDSBMT.INI Access Lock");
53 }
54
55 long cm_FlushFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
56 {
57         long code;
58
59         lock_ObtainWrite(&scp->bufCreateLock);
60         code = buf_FlushCleanPages(scp, userp, reqp);
61         
62         lock_ObtainMutex(&scp->mx);
63         scp->cbServerp = NULL;
64         scp->cbExpires = 0;
65         lock_ReleaseMutex(&scp->mx);
66
67         lock_ReleaseWrite(&scp->bufCreateLock);
68         cm_dnlcPurgedp(scp);
69
70         return code;
71 }
72
73 /*
74  * cm_ResetACLCache -- invalidate ACL info for a user that has just
75  *                      obtained or lost tokens
76  */
77 void cm_ResetACLCache(cm_user_t *userp)
78 {
79         cm_scache_t *scp;
80         int hash;
81
82         lock_ObtainWrite(&cm_scacheLock);
83         for (hash=0; hash < cm_hashTableSize; hash++) {
84                 for (scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
85                         scp->refCount++;
86                         lock_ReleaseWrite(&cm_scacheLock);
87                         lock_ObtainMutex(&scp->mx);
88                         cm_InvalidateACLUser(scp, userp);
89                         lock_ReleaseMutex(&scp->mx);
90                         lock_ObtainWrite(&cm_scacheLock);
91                         scp->refCount--;
92                 }
93         }
94         lock_ReleaseWrite(&cm_scacheLock);
95 }
96
97 /*
98  *  TranslateExtendedChars - This is a fix for TR 54482.
99  *
100  *  If an extended character (80 - FF) is entered into a file
101  *  or directory name in Windows, the character is translated
102  *  into the OEM character map before being passed to us.  Why
103  *  this occurs is unknown.  Our pioctl functions must match
104  *  this translation for paths given via our own commands (like
105  *  fs).  If we do not do this, then we will try to perform an
106  *  operation on a non-translated path, which we will fail to 
107  *  find, since the path was created with the translated chars.
108  *  This function performs the required translation.
109  */
110 void TranslateExtendedChars(char *str)
111 {
112   char *p;
113   
114         if (!str || !*str)
115                 return;
116
117 #ifndef DJGPP
118         CharToOem(str, str);
119 #else
120         p = str;
121         while (*p) *p++ &= 0x7f;  /* turn off high bit; probably not right */
122 #endif
123 }
124         
125 /* parse the passed-in file name and do a namei on it.  If we fail,
126  * return an error code, otherwise return the vnode located in *scpp.
127  */
128 long cm_ParseIoctlPath(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
129         cm_scache_t **scpp)
130 {
131         long code;
132         cm_scache_t *substRootp;
133
134         /* This is usually the file name, but for StatMountPoint it is the path. */
135         TranslateExtendedChars(ioctlp->inDatap);
136
137         code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
138                 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
139                 userp, ioctlp->tidPathp, reqp, &substRootp);
140         if (code) return code;
141         
142         code = cm_NameI(substRootp, ioctlp->inDatap, CM_FLAG_FOLLOW,
143                 userp, NULL, reqp, scpp);
144         if (code) return code;
145         
146         /* # of bytes of path */
147         code = strlen(ioctlp->inDatap) + 1;
148         ioctlp->inDatap += code;
149
150         /* This is usually nothing, but for StatMountPoint it is the file name. */
151         TranslateExtendedChars(ioctlp->inDatap);
152
153         /* and return success */
154         return 0;
155 }
156
157 void cm_SkipIoctlPath(smb_ioctl_t *ioctlp)
158 {
159         long temp;
160         
161         temp = strlen(ioctlp->inDatap) + 1;
162         ioctlp->inDatap += temp;
163 }
164
165
166 /* format the specified path to look like "/afs/<cellname>/usr", by
167  * adding "/afs" (if necessary) in front, changing any \'s to /'s, and
168  * removing any trailing "/"'s. One weirdo caveat: "/afs" will be
169  * intentionally returned as "/afs/"--this makes submount manipulation
170  * easier (because we can always jump past the initial "/afs" to find
171  * the AFS path that should be written into afsdsbmt.ini).
172  */
173 void cm_NormalizeAfsPath (char *outpathp, char *inpathp)
174 {
175         char *cp;
176
177         if (!strnicmp (inpathp, "/afs", strlen("/afs")))
178                 lstrcpy (outpathp, inpathp);
179         else if (!strnicmp (inpathp, "\\afs", strlen("\\afs")))
180                 lstrcpy (outpathp, inpathp);
181         else if ((inpathp[0] == '/') || (inpathp[0] == '\\'))
182                 sprintf (outpathp, "/afs%s", inpathp);
183         else // inpathp looks like "<cell>/usr"
184                 sprintf (outpathp, "/afs/%s", inpathp);
185
186         for (cp = outpathp; *cp != 0; ++cp) {
187                 if (*cp == '\\')
188                         *cp = '/';
189         }
190
191         if (strlen(outpathp) && (outpathp[strlen(outpathp)-1] == '/')) {
192            outpathp[strlen(outpathp)-1] = 0;
193         }
194
195         if (!strcmpi (outpathp, "/afs")) {
196            strcpy (outpathp, "/afs/");
197         }
198 }
199
200 /* parse the passed-in file name and do a namei on its parent.  If we fail,
201  * return an error code, otherwise return the vnode located in *scpp.
202  */
203 long cm_ParseIoctlParent(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
204                          cm_scache_t **scpp, char *leafp)
205 {
206         long code;
207         char tbuffer[1024];
208         char *tp, *jp;
209         cm_scache_t *substRootp;
210
211         strcpy(tbuffer, ioctlp->inDatap);
212         tp = strrchr(tbuffer, '\\');
213         jp = strrchr(tbuffer, '/');
214         if (!tp)
215                 tp = jp;
216         else if (jp && (tp - tbuffer) < (jp - tbuffer))
217                 tp = jp;
218         if (!tp) {
219                 strcpy(tbuffer, "\\");
220                 if (leafp) strcpy(leafp, ioctlp->inDatap);
221         }
222         else {
223                 *tp = 0;
224                 if (leafp) strcpy(leafp, tp+1);
225         }
226
227         code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
228                 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
229                 userp, ioctlp->tidPathp, reqp, &substRootp);
230         if (code) return code;
231
232         code = cm_NameI(substRootp, tbuffer, CM_FLAG_FOLLOW,
233                 userp, NULL, reqp, scpp);
234         if (code) return code;
235         
236         /* # of bytes of path */
237         code = strlen(ioctlp->inDatap) + 1;
238         ioctlp->inDatap += code;
239
240         /* and return success */
241         return 0;
242 }
243
244 long cm_IoctlGetACL(smb_ioctl_t *ioctlp, cm_user_t *userp)
245 {
246         cm_conn_t *connp;
247         cm_scache_t *scp;
248         AFSOpaque acl;
249         AFSFetchStatus fileStatus;
250         AFSVolSync volSync;
251         long code;
252         AFSFid fid;
253         int tlen;
254         cm_req_t req;
255
256         cm_InitReq(&req);
257
258         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
259         if (code) return code;
260         
261         /* now make the get acl call */
262         fid.Volume = scp->fid.volume;
263         fid.Vnode = scp->fid.vnode;
264         fid.Unique = scp->fid.unique;
265         do {
266                 acl.AFSOpaque_val = ioctlp->outDatap;
267                 acl.AFSOpaque_len = 0;
268                 code = cm_Conn(&scp->fid, userp, &req, &connp);
269                 if (code) continue;
270                 
271                 code = RXAFS_FetchACL(connp->callp, &fid, &acl, &fileStatus, &volSync);
272         } while (cm_Analyze(connp, userp, &req, &scp->fid,
273                             &volSync, NULL, code));
274         code = cm_MapRPCError(code, &req);
275         cm_ReleaseSCache(scp);
276         
277         if (code) return code;
278         
279         /* skip over return data */
280         tlen = strlen(ioctlp->outDatap) + 1;
281         ioctlp->outDatap += tlen;
282
283         /* and return success */
284         return 0;
285 }
286
287 long cm_IoctlGetFileCellName(struct smb_ioctl *ioctlp, struct cm_user *userp)
288 {
289         long code;
290         cm_scache_t *scp;
291         cm_cell_t *cellp;
292         cm_req_t req;
293
294         cm_InitReq(&req);
295
296         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
297         if (code) return code;
298         
299         cellp = cm_FindCellByID(scp->fid.cell);
300         if (cellp) {
301                 strcpy(ioctlp->outDatap, cellp->namep);
302                 ioctlp->outDatap += strlen(ioctlp->outDatap) + 1;
303                 code = 0;
304         }
305         else code = CM_ERROR_NOSUCHCELL;
306         
307         cm_ReleaseSCache(scp);
308         return code;
309 }
310
311 long cm_IoctlSetACL(struct smb_ioctl *ioctlp, struct cm_user *userp)
312 {
313         cm_conn_t *connp;
314         cm_scache_t *scp;
315         AFSOpaque acl;
316         AFSFetchStatus fileStatus;
317         AFSVolSync volSync;
318         long code;
319         AFSFid fid;
320         cm_req_t req;
321
322         cm_InitReq(&req);
323
324         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
325         if (code) return code;
326         
327         /* now make the get acl call */
328         fid.Volume = scp->fid.volume;
329         fid.Vnode = scp->fid.vnode;
330         fid.Unique = scp->fid.unique;
331         do {
332                 acl.AFSOpaque_val = ioctlp->inDatap;
333                 acl.AFSOpaque_len = strlen(ioctlp->inDatap)+1;
334                 code = cm_Conn(&scp->fid, userp, &req, &connp);
335                 if (code) continue;
336                 
337                 code = RXAFS_StoreACL(connp->callp, &fid, &acl, &fileStatus, &volSync);
338         } while (cm_Analyze(connp, userp, &req, &scp->fid,
339                             &volSync, NULL, code));
340         code = cm_MapRPCError(code, &req);
341
342         /* invalidate cache info, since we just trashed the ACL cache */
343         lock_ObtainMutex(&scp->mx);
344         cm_DiscardSCache(scp);
345         lock_ReleaseMutex(&scp->mx);
346
347         cm_ReleaseSCache(scp);
348         
349         return code;
350 }
351
352 long cm_IoctlFlushVolume(struct smb_ioctl *ioctlp, struct cm_user *userp)
353 {
354         long code;
355         cm_scache_t *scp;
356         unsigned long volume;
357         int i;
358         cm_req_t req;
359
360         cm_InitReq(&req);
361
362         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
363         if (code) return code;
364         
365         volume = scp->fid.volume;
366         cm_ReleaseSCache(scp);
367
368         lock_ObtainWrite(&cm_scacheLock);
369         for(i=0; i<cm_hashTableSize; i++) {
370                 for(scp = cm_hashTablep[i]; scp; scp = scp->nextp) {
371                         if (scp->fid.volume == volume) {
372                                 scp->refCount++;
373                                 lock_ReleaseWrite(&cm_scacheLock);
374
375                                 /* now flush the file */
376                                 cm_FlushFile(scp, userp, &req);
377
378                                 lock_ObtainWrite(&cm_scacheLock);
379                                 scp->refCount--;
380                         }
381                 }
382         }
383         lock_ReleaseWrite(&cm_scacheLock);
384
385         return code;
386 }
387
388 long cm_IoctlFlushFile(struct smb_ioctl *ioctlp, struct cm_user *userp)
389 {
390         long code;
391         cm_scache_t *scp;
392         cm_req_t req;
393
394         cm_InitReq(&req);
395
396         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
397         if (code) return code;
398         
399         cm_FlushFile(scp, userp, &req);
400         cm_ReleaseSCache(scp);
401
402         return 0;
403 }
404
405 long cm_IoctlSetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
406 {
407         cm_scache_t *scp;
408         char volName[32];
409         char offLineMsg[256];
410         char motd[256];
411         cm_conn_t *tcp;
412         long code;
413         AFSFetchVolumeStatus volStat;
414         AFSStoreVolumeStatus storeStat;
415         cm_volume_t *tvp;
416         char *cp;
417         cm_cell_t *cellp;
418         cm_req_t req;
419
420         cm_InitReq(&req);
421
422         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
423         if (code) return code;
424
425         cellp = cm_FindCellByID(scp->fid.cell);
426         osi_assert(cellp);
427
428         if (scp->flags & CM_SCACHEFLAG_RO) {
429                 cm_ReleaseSCache(scp);
430                 return CM_ERROR_READONLY;
431         }
432
433         code = cm_GetVolumeByID(cellp, scp->fid.volume, userp, &req, &tvp);
434         if (code) {
435                 cm_ReleaseSCache(scp);
436                 return code;
437         }
438
439         /* Copy the junk out, using cp as a roving pointer. */
440         cp = ioctlp->inDatap;
441         memcpy((char *)&volStat, cp, sizeof(AFSFetchVolumeStatus));
442         cp += sizeof(AFSFetchVolumeStatus);
443         strcpy(volName, cp);
444         cp += strlen(volName)+1;
445         strcpy(offLineMsg, cp);
446         cp +=  strlen(offLineMsg)+1;
447         strcpy(motd, cp);
448         storeStat.Mask = 0;
449         if (volStat.MinQuota != -1) {
450                 storeStat.MinQuota = volStat.MinQuota;
451                 storeStat.Mask |= AFS_SETMINQUOTA;
452         }
453         if (volStat.MaxQuota != -1) {
454                 storeStat.MaxQuota = volStat.MaxQuota;
455                 storeStat.Mask |= AFS_SETMAXQUOTA;
456         }
457
458         do {
459                 code = cm_Conn(&scp->fid, userp, &req, &tcp);
460                 if (code) continue;
461
462                 code = RXAFS_SetVolumeStatus(tcp->callp, scp->fid.volume,
463                         &storeStat, volName, offLineMsg, motd);
464         } while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, code));
465         code = cm_MapRPCError(code, &req);
466
467         /* return on failure */
468         cm_ReleaseSCache(scp);
469         if (code) {
470                 return code;
471         }
472
473         /* we are sending parms back to make compat. with prev system.  should
474          * change interface later to not ask for current status, just set
475          * new status
476          */
477         cp = ioctlp->outDatap;
478         memcpy(cp, (char *)&volStat, sizeof(VolumeStatus));
479         cp += sizeof(VolumeStatus);
480         strcpy(cp, volName);
481         cp += strlen(volName)+1;
482         strcpy(cp, offLineMsg);
483         cp += strlen(offLineMsg)+1;
484         strcpy(cp, motd);
485         cp += strlen(motd)+1;
486
487         /* now return updated return data pointer */
488         ioctlp->outDatap = cp;
489
490         return 0;
491 }
492
493 long cm_IoctlGetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
494 {
495         char volName[32];
496         cm_scache_t *scp;
497         char offLineMsg[256];
498         char motd[256];
499         cm_conn_t *tcp;
500         register long code;
501         AFSFetchVolumeStatus volStat;
502         register char *cp;
503         char *Name;
504         char *OfflineMsg;
505         char *MOTD;
506         cm_req_t req;
507
508         cm_InitReq(&req);
509
510         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
511         if (code) return code;
512
513         Name = volName;
514         OfflineMsg = offLineMsg;
515         MOTD = motd;
516         do {
517                 code = cm_Conn(&scp->fid, userp, &req, &tcp);
518                 if (code) continue;
519
520                 code = RXAFS_GetVolumeStatus(tcp->callp, scp->fid.volume,
521                         &volStat, &Name, &OfflineMsg, &MOTD);
522         } while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, code));
523         code = cm_MapRPCError(code, &req);
524
525         cm_ReleaseSCache(scp);
526         if (code) return code;
527
528         /* Copy all this junk into msg->im_data, keeping track of the lengths. */
529         cp = ioctlp->outDatap;
530         memcpy(cp, (char *)&volStat, sizeof(AFSFetchVolumeStatus));
531         cp += sizeof(AFSFetchVolumeStatus);
532         strcpy(cp, volName);
533         cp += strlen(volName)+1;
534         strcpy(cp, offLineMsg);
535         cp += strlen(offLineMsg)+1;
536         strcpy(cp, motd);
537         cp += strlen(motd)+1;
538
539         /* return new size */
540         ioctlp->outDatap = cp;
541
542         return 0;
543 }
544
545 long cm_IoctlWhereIs(struct smb_ioctl *ioctlp, struct cm_user *userp)
546 {
547         long code;
548         cm_scache_t *scp;
549         cm_cell_t *cellp;
550         cm_volume_t *tvp;
551         cm_serverRef_t *tsrp;
552         cm_server_t *tsp;
553         unsigned long volume;
554         char *cp;
555         cm_req_t req;
556
557         cm_InitReq(&req);
558
559         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
560         if (code) return code;
561         
562         volume = scp->fid.volume;
563
564         cellp = cm_FindCellByID(scp->fid.cell);
565         osi_assert(cellp);
566
567         cm_ReleaseSCache(scp);
568
569         code = cm_GetVolumeByID(cellp, volume, userp, &req, &tvp);
570         if (code) return code;
571         
572         cp = ioctlp->outDatap;
573         
574         lock_ObtainMutex(&tvp->mx);
575         tsrp = cm_GetVolServers(tvp, volume);
576         lock_ObtainRead(&cm_serverLock);
577         while(tsrp) {
578                 tsp = tsrp->server;
579                 memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
580                 cp += sizeof(long);
581                 tsrp = tsrp->next;
582         }
583         lock_ReleaseRead(&cm_serverLock);
584         lock_ReleaseMutex(&tvp->mx);
585
586         /* still room for terminating NULL, add it on */
587         volume = 0;     /* reuse vbl */
588         memcpy(cp, (char *)&volume, sizeof(long));
589         cp += sizeof(long);
590
591         ioctlp->outDatap = cp;
592         cm_PutVolume(tvp);
593         return 0;
594 }
595
596 long cm_IoctlStatMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
597 {
598         long code;
599         cm_scache_t *dscp;
600         cm_scache_t *scp;
601         char *cp;
602         cm_req_t req;
603
604         cm_InitReq(&req);
605
606         code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
607         if (code) return code;
608         
609         cp = ioctlp->inDatap;
610
611         code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
612         cm_ReleaseSCache(dscp);
613         if (code) return code;
614         
615         lock_ObtainMutex(&scp->mx);
616
617         /* now check that this is a real mount point */
618         if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
619                 lock_ReleaseMutex(&scp->mx);
620                 cm_ReleaseSCache(scp);
621                 return CM_ERROR_INVAL;
622         }
623         
624         code = cm_ReadMountPoint(scp, userp, &req);
625         if (code == 0) {
626                 cp = ioctlp->outDatap;
627                 strcpy(cp, scp->mountPointStringp);
628                 cp += strlen(cp) + 1;
629                 ioctlp->outDatap = cp;
630         }
631         lock_ReleaseMutex(&scp->mx);
632         cm_ReleaseSCache(scp);
633
634         return code;
635 }
636
637 long cm_IoctlDeleteMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
638 {
639         long code;
640         cm_scache_t *dscp;
641         cm_scache_t *scp;
642         char *cp;
643         cm_req_t req;
644
645         cm_InitReq(&req);
646
647         code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
648         if (code) return code;
649         
650         cp = ioctlp->inDatap;
651
652         code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
653         
654         /* if something went wrong, bail out now */
655         if (code) {
656                 goto done;
657         }
658         
659         lock_ObtainMutex(&scp->mx);
660         code = cm_SyncOp(scp, NULL, userp, &req, 0,
661                 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
662         if (code) {
663                 lock_ReleaseMutex(&scp->mx);
664                 cm_ReleaseSCache(scp);
665                 goto done;
666         }
667         
668         /* now check that this is a real mount point */
669         if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
670                 lock_ReleaseMutex(&scp->mx);
671                 cm_ReleaseSCache(scp);
672                 code = CM_ERROR_INVAL;
673                 goto done;
674         }
675         
676         /* time to make the RPC, so drop the lock */
677         lock_ReleaseMutex(&scp->mx);
678         cm_ReleaseSCache(scp);
679         
680         /* easier to do it this way */
681         code = cm_Unlink(dscp, cp, userp, &req);
682         if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
683                 smb_NotifyChange(FILE_ACTION_REMOVED,
684                                  FILE_NOTIFY_CHANGE_DIR_NAME,
685                                  dscp, cp, NULL, TRUE);
686
687 done:
688         cm_ReleaseSCache(dscp);
689         return code;
690 }
691
692 long cm_IoctlCheckServers(struct smb_ioctl *ioctlp, struct cm_user *userp)
693 {
694         cm_cell_t *cellp;
695         chservinfo_t csi;
696         char *tp;
697         char *cp;
698         long temp;
699         cm_server_t *tsp;
700         int haveCell;
701         
702         cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
703         tp = ioctlp->inDatap;
704         haveCell = 0;
705
706         memcpy(&temp, tp, sizeof(temp));
707         if (temp == 0x12345678) {       /* For afs3.3 version */
708                 memcpy(&csi, tp, sizeof(csi));
709                 if (csi.tinterval >= 0) {
710                         cp = ioctlp->outDatap;
711                         memcpy(cp, (char *)&cm_daemonCheckInterval, sizeof(long));
712                         ioctlp->outDatap += sizeof(long);
713                         if (csi.tinterval > 0) {
714                                 if (!smb_SUser(userp))
715                                         return CM_ERROR_NOACCESS;
716                                 cm_daemonCheckInterval = csi.tinterval;
717                         }
718                         return 0;
719                 }
720                 if (csi.tsize)
721                         haveCell = 1;
722                 temp = csi.tflags;
723                 cp = csi.tbuffer;
724         } else {        /* For pre afs3.3 versions */
725                 memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
726                 ioctlp->inDatap = cp = ioctlp->inDatap + sizeof(long);
727                 if (cp - ioctlp->inAllocp < ioctlp->inCopied)   /* still more data available */
728                         haveCell = 1;
729         }
730
731         /* 
732          * 1: fast check, don't contact servers.
733          * 2: local cell only.
734          */
735         if (haveCell) {
736                 /* have cell name, too */
737                 cellp = cm_GetCell(cp, 0);
738                 if (!cellp) return CM_ERROR_NOSUCHCELL;
739         }
740         else cellp = (cm_cell_t *) 0;
741         if (!cellp && (temp & 2)) {
742                 /* use local cell */
743                 cellp = cm_FindCellByID(1);
744         }
745         if (!(temp & 1)) {      /* if not fast, call server checker routine */
746                 /* check down servers */
747                 cm_CheckServers(CM_FLAG_CHECKDOWNSERVERS | CM_FLAG_CHECKUPSERVERS,
748                         cellp);
749         }
750
751         /* now return the current down server list */
752         cp = ioctlp->outDatap;
753         lock_ObtainRead(&cm_serverLock);
754         for(tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
755                 if (cellp && tsp->cellp != cellp) continue;     /* cell spec'd and wrong */
756                 if ((tsp->flags & CM_SERVERFLAG_DOWN)
757                         && tsp->type == CM_SERVER_FILE) {
758                         memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
759                         cp += sizeof(long);
760                 }
761         }
762         lock_ReleaseRead(&cm_serverLock);
763
764         ioctlp->outDatap = cp;
765         return 0;
766 }
767
768 long cm_IoctlGag(struct smb_ioctl *ioctlp, struct cm_user *userp)
769 {
770         /* we don't print anything superfluous, so we don't support the gag call */
771         return CM_ERROR_INVAL;
772 }
773
774 long cm_IoctlCheckVolumes(struct smb_ioctl *ioctlp, struct cm_user *userp)
775 {
776         cm_CheckVolumes();
777         return 0;
778 }
779
780 long cm_IoctlSetCacheSize(struct smb_ioctl *ioctlp, struct cm_user *userp)
781 {
782         long temp;
783         long code;
784         
785         cm_SkipIoctlPath(ioctlp);
786
787         memcpy(&temp, ioctlp->inDatap, sizeof(temp));
788         if (temp == 0) temp = buf_nOrigBuffers;
789         else {
790                 /* temp is in 1K units, convert to # of buffers */
791                 temp = temp / (buf_bufferSize / 1024);
792         }
793
794         /* now adjust the cache size */
795         code = buf_SetNBuffers(temp);
796
797         return code;
798 }
799
800 long cm_IoctlTraceControl(struct smb_ioctl *ioctlp, struct cm_user *userp)
801 {
802         long inValue;
803         
804         cm_SkipIoctlPath(ioctlp);
805         
806         memcpy(&inValue, ioctlp->inDatap, sizeof(long));
807
808         /* print trace */
809         if (inValue & 8) {
810                 afsd_ForceTrace(FALSE);
811         }
812         
813         if (inValue & 2) {
814                 /* set tracing value to low order bit */
815                 if ((inValue & 1) == 0) {
816                         /* disable tracing */
817                         osi_LogDisable(afsd_logp);
818                 }
819                 else {
820                         /* enable tracing */
821                         osi_LogEnable(afsd_logp);
822                 }
823         }
824
825         /* see if we're supposed to do a reset, too */
826         if (inValue & 4) {
827                 osi_LogReset(afsd_logp);
828         }
829
830         /* and copy out tracing flag */
831         inValue = afsd_logp->enabled;   /* use as a temp vbl */
832         memcpy(ioctlp->outDatap, &inValue, sizeof(long));
833         ioctlp->outDatap += sizeof(long);
834         return 0;
835 }
836
837 long cm_IoctlGetCacheParms(struct smb_ioctl *ioctlp, struct cm_user *userp)
838 {
839         cm_cacheParms_t parms;
840         
841         memset(&parms, 0, sizeof(parms));
842
843         /* first we get, in 1K units, the cache size */
844         parms.parms[0] = buf_nbuffers * (buf_bufferSize / 1024);
845         
846         /* and then the actual # of buffers in use (not in the free list, I guess,
847          * will be what we do).
848          */
849         parms.parms[1] = (buf_nbuffers - buf_CountFreeList()) * (buf_bufferSize / 1024);
850         
851         memcpy(ioctlp->outDatap, &parms, sizeof(parms));
852         ioctlp->outDatap += sizeof(parms);
853
854         return 0;
855 }
856
857 long cm_IoctlGetCell(struct smb_ioctl *ioctlp, struct cm_user *userp)
858 {
859         long whichCell;
860         long magic = 0;
861         cm_cell_t *tcellp;
862         cm_serverRef_t *serverRefp;
863         cm_server_t *serverp;
864         long i;
865         char *cp;
866         char *tp;
867         char *basep;
868
869         cm_SkipIoctlPath(ioctlp);
870
871         tp = ioctlp->inDatap;
872
873         memcpy((char *)&whichCell, tp, sizeof(long));
874         tp += sizeof(long);
875         
876         /* see if more than one long passed in, ignoring the null pathname (the -1) */
877         if (ioctlp->inCopied-1 > sizeof(long)) {
878                 memcpy((char *)&magic, tp, sizeof(long));
879         }
880
881         lock_ObtainRead(&cm_cellLock);
882         for(tcellp = cm_allCellsp; tcellp; tcellp = tcellp->nextp) {
883                 if (whichCell == 0) break;
884                 whichCell--;
885         }
886         lock_ReleaseRead(&cm_cellLock);
887         if (tcellp) {
888                 int max = 8;
889
890                 cp = ioctlp->outDatap;
891
892                 if (magic == 0x12345678) {
893                         memcpy(cp, (char *)&magic, sizeof(long));
894                         max = 13;
895                 }
896                 memset(cp, 0, max * sizeof(long));
897                 basep = cp;
898                 lock_ObtainRead(&cm_serverLock);        /* for going down server list */
899                 serverRefp = tcellp->vlServersp;
900                 for(i=0; i<max; i++) {
901                         if (!serverRefp) break;
902                         serverp = serverRefp->server;
903                         memcpy(cp, &serverp->addr.sin_addr.s_addr, sizeof(long));
904                         cp += sizeof(long);
905                         serverRefp = serverRefp->next;
906                 }
907                 lock_ReleaseRead(&cm_serverLock);
908                 cp = basep + max * sizeof(afs_int32);
909                 strcpy(cp, tcellp->namep);
910                 cp += strlen(tcellp->namep)+1;
911                 ioctlp->outDatap = cp;
912         }
913
914         if (tcellp) return 0;
915         else return CM_ERROR_NOMORETOKENS;      /* mapped to EDOM */
916 }
917
918 long cm_IoctlNewCell(struct smb_ioctl *ioctlp, struct cm_user *userp)
919 {
920         /* don't need to do, since NT cache manager will re-read afsdcell.ini
921          * on every access to a new cell.
922          */
923         return CM_ERROR_INVAL;
924 }
925
926 long cm_IoctlGetWsCell(smb_ioctl_t *ioctlp, cm_user_t *userp)
927 {
928         /* if we don't know our default cell, return failure */
929         if (cm_rootCellp == NULL) {
930                 return CM_ERROR_NOSUCHCELL;
931         }
932
933         /* return the default cellname to the caller */
934         strcpy(ioctlp->outDatap, cm_rootCellp->namep);
935         ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
936         
937         /* done: success */
938         return 0;
939 }
940
941 long cm_IoctlSysName(struct smb_ioctl *ioctlp, struct cm_user *userp)
942 {
943         long setSysName;
944         char *cp;
945         
946         cm_SkipIoctlPath(ioctlp);
947
948         memcpy(&setSysName, ioctlp->inDatap, sizeof(long));
949         ioctlp->inDatap += sizeof(long);
950         
951         if (setSysName) {
952                 strcpy(cm_sysName, ioctlp->inDatap);
953         }
954         else {
955                 /* return the sysname to the caller */
956                 setSysName = 1; /* really means "found sys name */
957                 cp = ioctlp->outDatap;
958                 memcpy(cp, &setSysName, sizeof(long));
959                 cp += sizeof(long);     /* skip found flag */
960                 strcpy(cp, cm_sysName);
961                 cp += strlen(cp) + 1;   /* skip name and terminating null char */
962                 ioctlp->outDatap = cp;
963         }
964         
965         /* done: success */
966         return 0;
967 }
968
969 long cm_IoctlGetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
970 {
971         long temp;
972         cm_cell_t *cellp;
973
974         cm_SkipIoctlPath(ioctlp);
975
976         cellp = cm_GetCell(ioctlp->inDatap, 0);
977         if (!cellp) return CM_ERROR_NOSUCHCELL;
978
979         temp = 0;
980         lock_ObtainMutex(&cellp->mx);
981         if (cellp->flags & CM_CELLFLAG_SUID)
982                 temp |= CM_SETCELLFLAG_SUID;
983         lock_ReleaseMutex(&cellp->mx);
984         
985         /* now copy out parm */
986         memcpy(ioctlp->outDatap, &temp, sizeof(long));
987         ioctlp->outDatap += sizeof(long);
988
989         return 0;
990 }
991
992 long cm_IoctlSetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
993 {
994         long temp;
995         cm_cell_t *cellp;
996
997         cm_SkipIoctlPath(ioctlp);
998
999         cellp = cm_GetCell(ioctlp->inDatap + 2*sizeof(long), 0);
1000         if (!cellp) return CM_ERROR_NOSUCHCELL;
1001
1002         memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
1003
1004         lock_ObtainMutex(&cellp->mx);
1005         if (temp & CM_SETCELLFLAG_SUID)
1006                 cellp->flags |= CM_CELLFLAG_SUID;
1007         else
1008                 cellp->flags &= ~CM_CELLFLAG_SUID;
1009         lock_ReleaseMutex(&cellp->mx);
1010
1011         return 0;
1012 }
1013
1014 long cm_IoctlSetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
1015 {
1016         cm_SSetPref_t     *spin; /* input */
1017         cm_SPref_t        *srvin;   /* one input component */
1018         cm_server_t       *tsp;
1019         int               i, vlonly, noServers, type;
1020         struct sockaddr_in      tmp;
1021         unsigned short    rank;
1022
1023         cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
1024
1025         spin       = (cm_SSetPref_t *)ioctlp->inDatap;
1026         noServers  = spin->num_servers;
1027         vlonly     = spin->flags;
1028         if ( vlonly )
1029                 type = CM_SERVER_VLDB;
1030         else    type = CM_SERVER_FILE;
1031
1032         for ( i=0; i < noServers; i++) 
1033         {
1034                 srvin          = &(spin->servers[i]);
1035                 rank           = srvin->rank + (rand() & 0x000f);
1036                 tmp.sin_addr   = srvin->host;
1037                 tmp.sin_family = AF_INET;
1038
1039                 tsp = cm_FindServer(&tmp, type);
1040                 if ( tsp )              /* an existing server */
1041                 {
1042                         tsp->ipRank = rank; /* no need to protect by mutex*/
1043
1044                         if ( type == CM_SERVER_FILE) /* fileserver */
1045                         {
1046                             /* find volumes which might have RO copy 
1047                             /* on server and change the ordering of 
1048                             ** their RO list */
1049                             cm_ChangeRankVolume(tsp);
1050                         }
1051                         else    
1052                         {
1053                             /* set preferences for an existing vlserver */
1054                             cm_ChangeRankCellVLServer(tsp);
1055                         }
1056                 }
1057                 else                    /* add a new server without a cell*/
1058                 {
1059                         tsp = cm_NewServer(&tmp, type, NULL);
1060                         tsp->ipRank = rank;
1061                 }
1062                 cm_PutServer(tsp);
1063         }
1064         return 0;
1065 }
1066
1067 long cm_IoctlGetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
1068 {
1069         cm_SPrefRequest_t *spin; /* input */
1070         cm_SPrefInfo_t    *spout;   /* output */
1071         cm_SPref_t        *srvout;   /* one output component */
1072         cm_server_t       *tsp;
1073         int               i, vlonly, noServers;
1074
1075         cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
1076
1077         spin      = (cm_SPrefRequest_t *)ioctlp->inDatap;
1078         spout     = (cm_SPrefInfo_t *) ioctlp->outDatap;
1079         srvout    = spout->servers;
1080         noServers = spin->num_servers; 
1081         vlonly    = spin->flags & CM_SPREF_VLONLY;
1082         spout->num_servers = 0;
1083
1084         lock_ObtainRead(&cm_serverLock); /* get server lock */
1085
1086         for(tsp=cm_allServersp, i=0; tsp && noServers; tsp=tsp->allNextp,i++){
1087                 if (spin->offset > i) {
1088                         continue;    /* catch up to where we left off */
1089                 }
1090
1091                 if ( vlonly && (tsp->type == CM_SERVER_FILE) )
1092                         continue;   /* ignore fileserver for -vlserver option*/
1093                 if ( !vlonly && (tsp->type == CM_SERVER_VLDB) )
1094                         continue;   /* ignore vlservers */
1095
1096                 srvout->host = tsp->addr.sin_addr;
1097                 srvout->rank = tsp->ipRank;
1098                 srvout++;       
1099                 spout->num_servers++;
1100                 noServers--;
1101         }
1102         lock_ReleaseRead(&cm_serverLock); /* release server lock */
1103
1104         if ( tsp )      /* we ran out of space in the output buffer */
1105                 spout->next_offset = i;
1106         else    
1107                 spout->next_offset = 0; 
1108         ioctlp->outDatap += sizeof(cm_SPrefInfo_t) + 
1109                         (spout->num_servers -1 ) * sizeof(cm_SPref_t) ;
1110         return 0;
1111 }
1112
1113 long cm_IoctlStoreBehind(struct smb_ioctl *ioctlp, struct cm_user *userp)
1114 {
1115         /* we ignore default asynchrony since we only have one way
1116          * of doing this today.
1117          */
1118         return 0;
1119 }
1120
1121 long cm_IoctlCreateMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
1122 {
1123         char leaf[256];
1124         long code;
1125         cm_scache_t *dscp;
1126         cm_attr_t tattr;
1127         char *cp;
1128         cm_req_t req;
1129         char mpInfo[256];
1130         char fullCell[256];
1131         char volume[256];
1132         char cell[256];
1133
1134         cm_InitReq(&req);
1135         
1136         code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
1137         if (code) return code;
1138
1139         /* Translate chars for the mount point name */
1140         TranslateExtendedChars(leaf);
1141
1142         /* 
1143          * The fs command allows the user to specify partial cell names on NT.  These must
1144          * be expanded to the full cell name for mount points so that the mount points will
1145          * work on UNIX clients.
1146          */
1147
1148         /* Extract the possibly partial cell name */
1149         strcpy(cell, ioctlp->inDatap + 1);      /* Skip the mp type character */
1150         
1151         if (cp = strchr(cell, ':')) {
1152                 /* Extract the volume name */
1153                 *cp = 0;
1154                 strcpy(volume,  cp + 1);
1155         
1156                 /* Get the full name for this cell */
1157                 code = cm_SearchCellFile(cell, fullCell, 0, 0);
1158                 if (code)
1159                         return CM_ERROR_NOSUCHCELL;
1160         
1161                 sprintf(mpInfo, "%c%s:%s", *ioctlp->inDatap, fullCell, volume);
1162         } else {
1163                 /* No cell name specified */
1164                 strcpy(mpInfo, ioctlp->inDatap);
1165         }
1166
1167         /* create the symlink with mode 644.  The lack of X bits tells
1168          * us that it is a mount point.
1169          */
1170         tattr.mask = CM_ATTRMASK_UNIXMODEBITS | CM_ATTRMASK_CLIENTMODTIME;
1171         tattr.unixModeBits = 0644;
1172         tattr.clientModTime = time(NULL);
1173
1174         code = cm_SymLink(dscp, leaf, mpInfo, 0, &tattr, userp, &req);
1175         if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1176                 smb_NotifyChange(FILE_ACTION_ADDED,
1177                                  FILE_NOTIFY_CHANGE_DIR_NAME,
1178                                  dscp, leaf, NULL, TRUE);
1179
1180         cm_ReleaseSCache(dscp);
1181
1182         return code;
1183 }
1184
1185 long cm_IoctlSymlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
1186 {
1187         char leaf[256];
1188         long code;
1189         cm_scache_t *dscp;
1190         cm_attr_t tattr;
1191         char *cp;
1192         cm_req_t req;
1193
1194         cm_InitReq(&req);
1195
1196         code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
1197         if (code) return code;
1198
1199         /* Translate chars for the link name */
1200         TranslateExtendedChars(leaf);
1201
1202         /* Translate chars for the linked to name */
1203         TranslateExtendedChars(ioctlp->inDatap);
1204
1205         cp = ioctlp->inDatap;           /* contents of link */
1206
1207         /* Create symlink with mode 0755. */
1208         tattr.mask = CM_ATTRMASK_UNIXMODEBITS;
1209         tattr.unixModeBits = 0755;
1210
1211         code = cm_SymLink(dscp, leaf, cp, 0, &tattr, userp, &req);
1212         if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1213                 smb_NotifyChange(FILE_ACTION_ADDED,
1214                                  FILE_NOTIFY_CHANGE_FILE_NAME
1215                                    | FILE_NOTIFY_CHANGE_DIR_NAME,
1216                                  dscp, leaf, NULL, TRUE);
1217
1218         cm_ReleaseSCache(dscp);
1219
1220         return code;
1221 }
1222
1223 extern long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1224         cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1225         cm_user_t *userp, cm_req_t *reqp);
1226
1227 long cm_IoctlListlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
1228 {
1229         long code;
1230         cm_scache_t *dscp;
1231         cm_scache_t *scp;
1232         char *cp;
1233         cm_space_t *spacep;
1234         cm_scache_t *newRootScp;
1235         cm_req_t req;
1236
1237         cm_InitReq(&req);
1238
1239         code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
1240         if (code) return code;
1241
1242         cp = ioctlp->inDatap;
1243
1244         code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
1245         cm_ReleaseSCache(dscp);
1246         if (code) return code;
1247
1248         /* Check that it's a real symlink */
1249         if (scp->fileType != CM_SCACHETYPE_SYMLINK){
1250                 cm_ReleaseSCache(scp);
1251                 return CM_ERROR_INVAL;
1252         }
1253
1254         code = cm_AssembleLink(scp, "", &newRootScp, &spacep, userp, &req);
1255         cm_ReleaseSCache(scp);
1256         if (code == 0) {
1257                 cp = ioctlp->outDatap;
1258                 if (newRootScp != NULL) {
1259                         strcpy(cp, "/afs/");
1260                         cp += strlen(cp);
1261                 }
1262                 strcpy(cp, spacep->data);
1263                 cp += strlen(cp) + 1;
1264                 ioctlp->outDatap = cp;
1265                 cm_FreeSpace(spacep);
1266                 if (newRootScp != NULL)
1267                         cm_ReleaseSCache(newRootScp);
1268         }
1269
1270         return code;
1271 }
1272
1273 long cm_IoctlDeletelink(struct smb_ioctl *ioctlp, struct cm_user *userp)
1274 {
1275         long code;
1276         cm_scache_t *dscp;
1277         cm_scache_t *scp;
1278         char *cp;
1279         cm_req_t req;
1280
1281         cm_InitReq(&req);
1282
1283         code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
1284         if (code) return code;
1285
1286         cp = ioctlp->inDatap;
1287
1288         code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
1289         
1290         /* if something went wrong, bail out now */
1291         if (code) {
1292                 goto done;
1293         }
1294         
1295         lock_ObtainMutex(&scp->mx);
1296         code = cm_SyncOp(scp, NULL, userp, &req, 0,
1297                 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1298         if (code) {
1299                 lock_ReleaseMutex(&scp->mx);
1300                 cm_ReleaseSCache(scp);
1301                 goto done;
1302         }
1303         
1304         /* now check that this is a real symlink */
1305         if (scp->fileType != CM_SCACHETYPE_SYMLINK) {
1306                 lock_ReleaseMutex(&scp->mx);
1307                 cm_ReleaseSCache(scp);
1308                 code = CM_ERROR_INVAL;
1309                 goto done;
1310         }
1311         
1312         /* time to make the RPC, so drop the lock */
1313         lock_ReleaseMutex(&scp->mx);
1314         cm_ReleaseSCache(scp);
1315         
1316         /* easier to do it this way */
1317         code = cm_Unlink(dscp, cp, userp, &req);
1318         if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1319                 smb_NotifyChange(FILE_ACTION_REMOVED,
1320                                  FILE_NOTIFY_CHANGE_FILE_NAME
1321                                    | FILE_NOTIFY_CHANGE_DIR_NAME,
1322                                  dscp, cp, NULL, TRUE);
1323
1324 done:
1325         cm_ReleaseSCache(dscp);
1326         return code;
1327 }
1328
1329 long cm_IoctlSetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
1330 {
1331         char *saveDataPtr;
1332         char *tp;
1333         int ticketLen;
1334         char *ticket;
1335         int ctSize;
1336         struct ClearToken ct;
1337         cm_cell_t *cellp;
1338         cm_ucell_t *ucellp;
1339         char *uname = NULL;
1340         afs_uuid_t uuid;
1341         int flags;
1342         char sessionKey[8];
1343
1344         saveDataPtr = ioctlp->inDatap;
1345
1346         cm_SkipIoctlPath(ioctlp);
1347
1348         tp = ioctlp->inDatap;
1349
1350         /* ticket length */
1351         memcpy(&ticketLen, tp, sizeof(ticketLen));
1352         tp += sizeof(ticketLen);
1353         if (ticketLen < MINKTCTICKETLEN || ticketLen > MAXKTCTICKETLEN)
1354                 return CM_ERROR_INVAL;
1355
1356         /* remember ticket and skip over it for now */
1357         ticket = tp;
1358         tp += ticketLen;
1359
1360         /* clear token size */
1361         memcpy(&ctSize, tp, sizeof(ctSize));
1362         tp += sizeof(ctSize);
1363         if (ctSize != sizeof(struct ClearToken))
1364                 return CM_ERROR_INVAL;
1365
1366         /* clear token */
1367         memcpy(&ct, tp, ctSize);
1368         tp += ctSize;
1369         if (ct.AuthHandle == -1)
1370                 ct.AuthHandle = 999;    /* more rxvab compat stuff */
1371
1372         /* more stuff, if any */
1373         if (ioctlp->inCopied > tp - saveDataPtr) {
1374                 /* flags:  logon flag */
1375                 memcpy(&flags, tp, sizeof(int));
1376                 tp += sizeof(int);
1377
1378                 /* cell name */
1379                 cellp = cm_GetCell(tp, CM_FLAG_CREATE);
1380                 if (!cellp) return CM_ERROR_NOSUCHCELL;
1381                 tp += strlen(tp) + 1;
1382
1383                 /* user name */
1384                 uname = tp;
1385                 tp += strlen(tp) + 1;
1386
1387 #ifndef DJGPP   /* for win95, session key is back in pioctl */
1388                 /* uuid */
1389                 memcpy(&uuid, tp, sizeof(uuid));
1390                 if (!cm_FindTokenEvent(uuid, sessionKey))
1391                         return CM_ERROR_INVAL;
1392 #endif /* !DJGPP */
1393         } else
1394                 cellp = cm_rootCellp;
1395
1396         if (flags & PIOCTL_LOGON) {
1397           userp = smb_FindCMUserByName(/*ioctlp->fidp->vcp,*/ uname,
1398                                                               ioctlp->fidp->vcp->rname);
1399         }
1400         
1401         /* store the token */
1402         lock_ObtainMutex(&userp->mx);
1403         ucellp = cm_GetUCell(userp, cellp);
1404         ucellp->ticketLen = ticketLen;
1405         if (ucellp->ticketp)
1406                 free(ucellp->ticketp);  /* Discard old token if any */
1407         ucellp->ticketp = malloc(ticketLen);
1408         memcpy(ucellp->ticketp, ticket, ticketLen);
1409 #ifndef DJGPP
1410         /*
1411          * Get the session key from the RPC, rather than from the pioctl.
1412          */
1413         /*
1414         memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
1415          */
1416         memcpy(ucellp->sessionKey.data, sessionKey, sizeof(sessionKey));
1417 #else
1418         /* for win95, we are getting the session key from the pioctl */
1419         memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
1420 #endif /* !DJGPP */
1421         ucellp->kvno = ct.AuthHandle;
1422         ucellp->expirationTime = ct.EndTimestamp;
1423         ucellp->gen++;
1424         if (uname) strcpy(ucellp->userName, uname);
1425         ucellp->flags |= CM_UCELLFLAG_RXKAD;
1426         lock_ReleaseMutex(&userp->mx);
1427
1428         if (flags & PIOCTL_LOGON) {
1429                 ioctlp->flags |= SMB_IOCTLFLAG_LOGON;
1430         }
1431
1432         cm_ResetACLCache(userp);
1433
1434         return 0;
1435 }
1436
1437 long cm_IoctlGetTokenIter(struct smb_ioctl *ioctlp, struct cm_user *userp)
1438 {
1439         char *tp, *cp;
1440         int iterator;
1441         int temp;
1442         cm_ucell_t *ucellp;
1443         struct ClearToken ct;
1444
1445         cm_SkipIoctlPath(ioctlp);
1446
1447         tp = ioctlp->inDatap;
1448         cp = ioctlp->outDatap;
1449
1450         /* iterator */
1451         memcpy(&iterator, tp, sizeof(iterator));
1452         tp += sizeof(iterator);
1453
1454         lock_ObtainMutex(&userp->mx);
1455
1456         /* look for token */
1457         for (;;iterator++) {
1458                 ucellp = cm_FindUCell(userp, iterator);
1459                 if (!ucellp) {
1460                         lock_ReleaseMutex(&userp->mx);
1461                         return CM_ERROR_NOMORETOKENS;
1462                 }
1463                 if (ucellp->flags & CM_UCELLFLAG_RXKAD)
1464                         break;
1465         }
1466
1467         /* new iterator */
1468         temp = ucellp->iterator + 1;
1469         memcpy(cp, &temp, sizeof(temp));
1470         cp += sizeof(temp);
1471
1472         /* ticket length */
1473         memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
1474         cp += sizeof(ucellp->ticketLen);
1475
1476         /* ticket */
1477         memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
1478         cp += ucellp->ticketLen;
1479
1480         /* clear token size */
1481         temp = sizeof(ct);
1482         memcpy(cp, &temp, sizeof(temp));
1483         cp += sizeof(temp);
1484
1485         /* clear token */
1486         ct.AuthHandle = ucellp->kvno;
1487 #ifndef DJGPP
1488         /*
1489          * Don't give out a real session key here
1490          */
1491         /*
1492         memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
1493          */
1494         memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
1495 #else
1496         memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
1497 #endif /* !DJGPP */
1498         ct.ViceId = 37;                 /* XXX */
1499         ct.BeginTimestamp = 0;          /* XXX */
1500         ct.EndTimestamp = ucellp->expirationTime;
1501         memcpy(cp, &ct, sizeof(ct));
1502         cp += sizeof(ct);
1503
1504         /* Primary flag (unused) */
1505         temp = 0;
1506         memcpy(cp, &temp, sizeof(temp));
1507         cp += sizeof(temp);
1508
1509         /* cell name */
1510         strcpy(cp, ucellp->cellp->namep);
1511         cp += strlen(cp) + 1;
1512
1513         /* user name */
1514         strcpy(cp, ucellp->userName);
1515         cp += strlen(cp) + 1;
1516
1517         ioctlp->outDatap = cp;
1518
1519         lock_ReleaseMutex(&userp->mx);
1520
1521         return 0;
1522 }
1523
1524 long cm_IoctlGetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
1525 {
1526         char *cp;
1527         int temp;
1528         cm_cell_t *cellp;
1529         cm_ucell_t *ucellp;
1530         struct ClearToken ct;
1531         char *tp;
1532 #ifndef DJGPP
1533         afs_uuid_t uuid;
1534 #endif /* !DJGPP */
1535
1536         cm_SkipIoctlPath(ioctlp);
1537
1538         tp = ioctlp->inDatap;
1539
1540         cp = ioctlp->outDatap;
1541
1542         /* cell name is right here */
1543         cellp = cm_GetCell(tp, 0);
1544         if (!cellp) return CM_ERROR_NOSUCHCELL;
1545         tp += strlen(tp) + 1;
1546
1547 #ifndef DJGPP
1548         /* uuid */
1549         memcpy(&uuid, tp, sizeof(uuid));
1550 #endif /* !DJGPP */
1551
1552         lock_ObtainMutex(&userp->mx);
1553
1554         ucellp = cm_GetUCell(userp, cellp);
1555         if (!ucellp || !(ucellp->flags & CM_UCELLFLAG_RXKAD)) {
1556                 lock_ReleaseMutex(&userp->mx);
1557                 return CM_ERROR_NOMORETOKENS;
1558         }
1559
1560         /* ticket length */
1561         memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
1562         cp += sizeof(ucellp->ticketLen);
1563
1564         /* ticket */
1565         memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
1566         cp += ucellp->ticketLen;
1567
1568         /* clear token size */
1569         temp = sizeof(ct);
1570         memcpy(cp, &temp, sizeof(temp));
1571         cp += sizeof(temp);
1572
1573         /* clear token */
1574         ct.AuthHandle = ucellp->kvno;
1575 #ifndef DJGPP
1576         /*
1577          * Don't give out a real session key here
1578          */
1579         /*
1580         memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
1581          */
1582         memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
1583 #else
1584         memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
1585 #endif /* !DJGPP */
1586         ct.ViceId = 37;                 /* XXX */
1587         ct.BeginTimestamp = 0;          /* XXX */
1588         ct.EndTimestamp = ucellp->expirationTime;
1589         memcpy(cp, &ct, sizeof(ct));
1590         cp += sizeof(ct);
1591
1592         /* Primary flag (unused) */
1593         temp = 0;
1594         memcpy(cp, &temp, sizeof(temp));
1595         cp += sizeof(temp);
1596
1597         /* cell name */
1598         strcpy(cp, ucellp->cellp->namep);
1599         cp += strlen(cp) + 1;
1600
1601         /* user name */
1602         strcpy(cp, ucellp->userName);
1603         cp += strlen(cp) + 1;
1604
1605         ioctlp->outDatap = cp;
1606
1607         lock_ReleaseMutex(&userp->mx);
1608
1609 #ifndef DJGPP
1610         cm_RegisterNewTokenEvent(uuid, ucellp->sessionKey.data);
1611 #endif /* !DJGPP */
1612
1613         return 0;
1614 }
1615
1616 long cm_IoctlDelToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
1617 {
1618         char *cp;
1619         cm_cell_t *cellp;
1620         cm_ucell_t *ucellp;
1621
1622         cm_SkipIoctlPath(ioctlp);
1623
1624         cp = ioctlp->outDatap;
1625
1626         /* cell name is right here */
1627         cellp = cm_GetCell(ioctlp->inDatap, 0);
1628         if (!cellp) return CM_ERROR_NOSUCHCELL;
1629
1630         lock_ObtainMutex(&userp->mx);
1631
1632         ucellp = cm_GetUCell(userp, cellp);
1633         if (!ucellp) {
1634                 lock_ReleaseMutex(&userp->mx);
1635                 return CM_ERROR_NOMORETOKENS;
1636         }
1637
1638         if (ucellp->ticketp) {
1639                 free(ucellp->ticketp);
1640                 ucellp->ticketp = NULL;
1641         }
1642         ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
1643         ucellp->gen++;
1644
1645         lock_ReleaseMutex(&userp->mx);
1646
1647         cm_ResetACLCache(userp);
1648
1649         return 0;
1650 }
1651
1652 long cm_IoctlDelAllToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
1653 {
1654         cm_ucell_t *ucellp;
1655
1656         lock_ObtainMutex(&userp->mx);
1657
1658         for (ucellp = userp->cellInfop; ucellp; ucellp = ucellp->nextp) {
1659                 ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
1660                 ucellp->gen++;
1661         }
1662
1663         lock_ReleaseMutex(&userp->mx);
1664
1665         cm_ResetACLCache(userp);
1666
1667         return 0;
1668 }
1669
1670 long cm_IoctlMakeSubmount(smb_ioctl_t *ioctlp, cm_user_t *userp)
1671 {
1672         char afspath[MAX_PATH];
1673         char *submountreqp;
1674         int iteration;
1675         int submountDataSize;
1676         char *submountData;
1677         char *submountName;
1678         int nextAutoSubmount;
1679
1680         cm_SkipIoctlPath(ioctlp);
1681
1682         /* Serialize this one, to prevent simultaneous mods
1683          * to afsdsbmt.ini
1684          */
1685         lock_ObtainMutex(&cm_Afsdsbmt_Lock);
1686
1687         /* Parse the input parameters--first the required afs path,
1688          * then the requested submount name (which may be "").
1689          */
1690         cm_NormalizeAfsPath (afspath, ioctlp->inDatap);
1691         submountreqp = ioctlp->inDatap + (strlen(ioctlp->inDatap)+1);
1692
1693         /* If the caller supplied a suggested submount name, see if
1694          * that submount name is in use... if so, the submount's path
1695          * has to match our path.
1696          */
1697         if (submountreqp && *submountreqp) {
1698                 char submountPathNormalized[MAX_PATH];
1699                 char submountPath[MAX_PATH];
1700                 int submountPathLen;
1701
1702                 submountPathLen = GetPrivateProfileString("AFS Submounts",
1703                                         submountreqp, "", submountPath,
1704                                         sizeof(submountPath), "afsdsbmt.ini");
1705
1706                 if ((submountPathLen == 0) ||
1707                     (submountPathLen == sizeof(submountPath) - 1)) {
1708
1709                         /* The suggested submount name isn't in use now--
1710                          * so we can safely map the requested submount name
1711                          * to the supplied path. Remember not to write the
1712                          * leading "/afs" when writing out the submount.
1713                          */
1714                         WritePrivateProfileString("AFS Submounts",
1715                                         submountreqp, &afspath[strlen("/afs")],
1716                                         "afsdsbmt.ini");
1717
1718                         strcpy(ioctlp->outDatap, submountreqp);
1719                         ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1720                         lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
1721                         return 0;
1722                 }
1723
1724                 /* The suggested submount name is already in use--if the
1725                  * supplied path matches the submount's path, we can still
1726                  * use the suggested submount name.
1727                  */
1728                 cm_NormalizeAfsPath (submountPathNormalized, submountPath);
1729                 if (!strcmp (submountPathNormalized, afspath)) {
1730                         strcpy(ioctlp->outDatap, submountreqp);
1731                         ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1732                         lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
1733                         return 0;
1734                 }
1735         }
1736
1737         /* At this point, the user either didn't request a particular
1738          * submount name, or that submount name couldn't be used.
1739          * Look through afsdsbmt.ini to see if there are any submounts
1740          * already associated with the specified path. The first
1741          * step in doing that search is to load the AFS Submounts
1742          * section of afsdsbmt.ini into memory.
1743          */
1744
1745         submountDataSize = 1024;
1746         submountData = malloc (submountDataSize);
1747
1748         for (iteration = 0; iteration < 5; ++iteration) {
1749
1750                 int sectionSize;
1751                 sectionSize = GetPrivateProfileString("AFS Submounts",
1752                                         NULL, "", submountData,
1753                                         submountDataSize, "afsdsbmt.ini");
1754                 if (sectionSize < submountDataSize-2)
1755                         break;
1756
1757                 free (submountData);
1758                 submountDataSize *= 2;
1759                 submountData = malloc (submountDataSize);
1760         }
1761
1762         /* Having obtained a list of all available submounts, start
1763          * searching that list for a path which matches the requested
1764          * AFS path. We'll also keep track of the highest "auto15"/"auto47"
1765          * submount, in case we need to add a new one later.
1766          */
1767
1768         nextAutoSubmount = 1;
1769
1770         for (submountName = submountData;
1771                 submountName && *submountName;
1772                 submountName += 1+strlen(submountName)) {
1773
1774                 char submountPathNormalized[MAX_PATH];
1775                 char submountPath[MAX_PATH] = "";
1776                 int submountPathLen;
1777
1778                 /* If this is an Auto### submount, remember its ### value */
1779
1780                 if ((!strnicmp (submountName, "auto", 4)) &&
1781                     (isdigit (submountName[strlen("auto")]))) {
1782                         int thisAutoSubmount;
1783                         thisAutoSubmount = atoi (&submountName[strlen("auto")]);
1784                         nextAutoSubmount = max (nextAutoSubmount,
1785                                                 thisAutoSubmount+1);
1786                 }
1787
1788                 /* We have the name of a submount in the AFS Submounts
1789                  * section; read that entry to find out what path it
1790                  * maps to.
1791                  */
1792                 submountPathLen = GetPrivateProfileString("AFS Submounts",
1793                                         submountName, "", submountPath,
1794                                         sizeof(submountPath), "afsdsbmt.ini");
1795
1796                 if ((submountPathLen == 0) ||
1797                     (submountPathLen == sizeof(submountPath) - 1)) {
1798                         continue;
1799                 }
1800
1801                 /* See if the path for this submount matches the path
1802                  * that our caller specified. If so, we can return
1803                  * this submount.
1804                  */
1805                 cm_NormalizeAfsPath (submountPathNormalized, submountPath);
1806                 if (!strcmp (submountPathNormalized, afspath)) {
1807
1808                         strcpy(ioctlp->outDatap, submountName);
1809                         ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1810                         free (submountData);
1811                         lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
1812                         return 0;
1813
1814                 }
1815         }
1816
1817         free (submountData);
1818
1819         /* We've been through the entire list of existing submounts, and
1820          * didn't find any which matched the specified path. So, we'll
1821          * just have to add one. Remember not to write the leading "/afs"
1822          * when writing out the submount.
1823          */
1824
1825         sprintf(ioctlp->outDatap, "auto%ld", nextAutoSubmount);
1826
1827         WritePrivateProfileString("AFS Submounts", ioctlp->outDatap,
1828                                         &afspath[lstrlen("/afs")],
1829                                         "afsdsbmt.ini");
1830
1831         ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1832         lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
1833         return 0;
1834 }
1835
1836 long cm_IoctlGetRxkcrypt(smb_ioctl_t *ioctlp, cm_user_t *userp)
1837 {
1838         memcpy(ioctlp->outDatap, &cryptall, sizeof(cryptall));
1839         ioctlp->outDatap += sizeof(cryptall);
1840
1841         return 0;
1842 }
1843
1844 long cm_IoctlSetRxkcrypt(smb_ioctl_t *ioctlp, cm_user_t *userp)
1845 {
1846         cm_SkipIoctlPath(ioctlp);
1847
1848         memcpy(&cryptall, ioctlp->inDatap, sizeof(cryptall));
1849
1850         return 0;
1851 }
1852
1853 #ifdef DJGPP
1854 extern int afsd_shutdown(int);
1855 extern int afs_shutdown;
1856
1857 long cm_IoctlShutdown(smb_ioctl_t *ioctlp, cm_user_t *userp)
1858 {
1859   afs_shutdown = 1;   /* flag to shut down */
1860   return 0;
1861 }
1862 #endif /* DJGPP */