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