windows-cm_ioctl_query_opts-20080115
[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 #include <afs/cellconfig.h>
13 #include <afs/ptserver.h>
14 #include <ubik.h>
15
16 #include <windows.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <malloc.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <time.h>
23
24 #include <osi.h>
25
26 #include "afsd.h"
27 #include "afsd_init.h"
28 #include <WINNT\afsreg.h>
29
30 #include "smb.h"
31
32 #include <rx/rxkad.h>
33 #include "afsrpc.h"
34
35 #include "cm_rpc.h"
36 #include <strsafe.h>
37 #include <winioctl.h>
38 #include <..\afsrdr\kif.h>
39 #include <rx\rx.h>
40
41 #ifdef _DEBUG
42 #include <crtdbg.h>
43 #endif
44
45 /* Copied from afs_tokens.h */
46 #define PIOCTL_LOGON    0x1
47 #define MAX_PATH 260
48
49 osi_mutex_t cm_Afsdsbmt_Lock;
50
51 extern afs_int32 cryptall;
52 extern char cm_NetbiosName[];
53
54 extern void afsi_log(char *pattern, ...);
55
56 void cm_InitIoctl(void)
57 {
58     lock_InitializeMutex(&cm_Afsdsbmt_Lock, "AFSDSBMT.INI Access Lock");
59 }
60
61 long cm_CleanFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
62 {
63     long code;
64
65     lock_ObtainWrite(&scp->bufCreateLock);
66     code = buf_CleanVnode(scp, userp, reqp);
67         
68     lock_ObtainMutex(&scp->mx);
69     cm_DiscardSCache(scp);
70     lock_ReleaseMutex(&scp->mx);
71
72     lock_ReleaseWrite(&scp->bufCreateLock);
73     osi_Log2(afsd_logp,"cm_CleanFile scp 0x%x returns error: [%x]",scp, code);
74     return code;
75 }
76
77 long cm_FlushFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
78 {
79     long code;
80
81 #ifdef AFS_FREELANCE_CLIENT
82     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
83         cm_noteLocalMountPointChange();
84         return 0;
85     }
86 #endif
87
88     lock_ObtainWrite(&scp->bufCreateLock);
89     code = buf_FlushCleanPages(scp, userp, reqp);
90         
91     lock_ObtainMutex(&scp->mx);
92     cm_DiscardSCache(scp);
93
94     lock_ReleaseMutex(&scp->mx);
95
96     lock_ReleaseWrite(&scp->bufCreateLock);
97     osi_Log2(afsd_logp,"cm_FlushFile scp 0x%x returns error: [%x]",scp, code);
98     return code;
99 }
100
101 long cm_FlushParent(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
102 {
103     long code = 0;
104     cm_scache_t * pscp;
105   
106     pscp = cm_FindSCacheParent(scp);
107   
108     /* now flush the file */
109     code = cm_FlushFile(pscp, userp, reqp);
110     cm_ReleaseSCache(pscp);
111
112     return code;
113 }
114
115
116 long cm_FlushVolume(cm_user_t *userp, cm_req_t *reqp, afs_uint32 cell, afs_uint32 volume)
117 {
118     long code = 0;
119     cm_scache_t *scp;
120     int i;
121
122 #ifdef AFS_FREELANCE_CLIENT
123     if ( cell == AFS_FAKE_ROOT_CELL_ID && volume == AFS_FAKE_ROOT_VOL_ID ) {
124         cm_noteLocalMountPointChange();
125         return 0;
126     }
127 #endif
128
129     lock_ObtainWrite(&cm_scacheLock);
130     for (i=0; i<cm_data.scacheHashTableSize; i++) {
131         for (scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp) {
132             if (scp->fid.volume == volume && scp->fid.cell == cell) {
133                 cm_HoldSCacheNoLock(scp);
134                 lock_ReleaseWrite(&cm_scacheLock);
135
136                 /* now flush the file */
137                 code = cm_FlushFile(scp, userp, reqp);
138                 lock_ObtainWrite(&cm_scacheLock);
139                 cm_ReleaseSCacheNoLock(scp);
140             }
141         }
142     }
143     lock_ReleaseWrite(&cm_scacheLock);
144
145     return code;
146 }
147
148 /*
149  * cm_ResetACLCache -- invalidate ACL info for a user that has just
150  *                      obtained or lost tokens
151  */
152 void cm_ResetACLCache(cm_user_t *userp)
153 {
154     cm_scache_t *scp;
155     int hash;
156
157     lock_ObtainWrite(&cm_scacheLock);
158     for (hash=0; hash < cm_data.scacheHashTableSize; hash++) {
159         for (scp=cm_data.scacheHashTablep[hash]; scp; scp=scp->nextp) {
160             cm_HoldSCacheNoLock(scp);
161             lock_ReleaseWrite(&cm_scacheLock);
162             lock_ObtainMutex(&scp->mx);
163             cm_InvalidateACLUser(scp, userp);
164             lock_ReleaseMutex(&scp->mx);
165             lock_ObtainWrite(&cm_scacheLock);
166             cm_ReleaseSCacheNoLock(scp);
167         }
168     }
169     lock_ReleaseWrite(&cm_scacheLock);
170 }       
171
172 /*
173  *  TranslateExtendedChars - This is a fix for TR 54482.
174  *
175  *  If an extended character (80 - FF) is entered into a file
176  *  or directory name in Windows, the character is translated
177  *  into the OEM character map before being passed to us.  Why
178  *  this occurs is unknown.  Our pioctl functions must match
179  *  this translation for paths given via our own commands (like
180  *  fs).  If we do not do this, then we will try to perform an
181  *  operation on a non-translated path, which we will fail to 
182  *  find, since the path was created with the translated chars.
183  *  This function performs the required translation.
184  */
185 void TranslateExtendedChars(char *str)
186 {
187     if (!str || !*str)
188         return;
189
190     CharToOem(str, str);
191 }
192         
193 /* parse the passed-in file name and do a namei on it.  If we fail,
194  * return an error code, otherwise return the vnode located in *scpp.
195  */
196 #define CM_PARSE_FLAG_LITERAL 1
197
198 long cm_ParseIoctlPath(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
199         cm_scache_t **scpp, afs_uint32 flags)
200 {
201     long code;
202 #ifndef AFSIFS
203     cm_scache_t *substRootp = NULL;
204     cm_scache_t *iscp = NULL;
205 #endif
206     char * relativePath;
207     char * lastComponent = NULL;
208     afs_uint32 follow = (flags & CM_PARSE_FLAG_LITERAL ? CM_FLAG_NOMOUNTCHASE : CM_FLAG_FOLLOW);
209
210     relativePath = ioctlp->inDatap;
211     /* setup the next data value for the caller to use */
212     ioctlp->inDatap += (long)strlen(ioctlp->inDatap) + 1;;
213
214     osi_Log1(afsd_logp, "cm_ParseIoctlPath %s", osi_LogSaveString(afsd_logp,relativePath));
215
216     /* This is usually the file name, but for StatMountPoint it is the path. */
217     /* ioctlp->inDatap can be either of the form:
218      *    \path\.
219      *    \path\file
220      *    \\netbios-name\submount\path\.
221      *    \\netbios-name\submount\path\file
222      */
223     TranslateExtendedChars(relativePath);
224
225     /* This is usually nothing, but for StatMountPoint it is the file name. */
226     TranslateExtendedChars(ioctlp->inDatap);
227
228 #ifdef AFSIFS
229     /* we have passed the whole path, including the afs prefix.
230        when the pioctl call is made, we perform an ioctl to afsrdr
231        and it returns the correct (full) path.  therefore, there is
232        no drive letter, and the path is absolute. */
233     code = cm_NameI(cm_data.rootSCachep, relativePath,
234                      CM_FLAG_CASEFOLD,
235                      userp, "", reqp, scpp);
236
237     if (code) {
238         osi_Log1(afsd_logp,"cm_ParseIoctlPath code 0x%x", code);
239         return code;
240     }
241 #else /* AFSIFS */
242
243     if (relativePath[0] == relativePath[1] &&
244          relativePath[1] == '\\' && 
245          !_strnicmp(cm_NetbiosName,relativePath+2,strlen(cm_NetbiosName))) 
246     {
247         char shareName[256];
248         char *sharePath;
249         int shareFound, i;
250
251         /* We may have found a UNC path. 
252          * If the first component is the NetbiosName,
253          * then throw out the second component (the submount)
254          * since it had better expand into the value of ioctl->tidPathp
255          */
256         char * p;
257         p = relativePath + 2 + strlen(cm_NetbiosName) + 1;                      /* buffer overflow vuln.? */
258         if ( !_strnicmp("all", p, 3) )
259             p += 4;
260
261         for (i = 0; *p && *p != '\\'; i++,p++ ) {
262             shareName[i] = *p;
263         }
264         p++;                    /* skip past trailing slash */
265         shareName[i] = 0;       /* terminate string */
266
267         shareFound = smb_FindShare(ioctlp->fidp->vcp, ioctlp->uidp, shareName, &sharePath);
268         if ( shareFound ) {
269             /* we found a sharename, therefore use the resulting path */
270             code = cm_NameI(cm_data.rootSCachep, ioctlp->prefix->data,
271                              CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
272                              userp, sharePath, reqp, &substRootp);
273             free(sharePath);
274             if (code) {
275                 osi_Log1(afsd_logp,"cm_ParseIoctlPath [1] code 0x%x", code);
276                 return code;
277             }
278
279             lastComponent = strrchr(p, '\\');
280             if (lastComponent && (lastComponent - p) > 1 &&strlen(lastComponent) > 1) {
281                 *lastComponent = '\0';
282                 lastComponent++;
283
284                 code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
285                                  userp, NULL, reqp, &iscp);
286                 if (code == 0)
287                     code = cm_NameI(iscp, lastComponent, CM_FLAG_CASEFOLD | follow,
288                                     userp, NULL, reqp, scpp);
289                 if (iscp)
290                     cm_ReleaseSCache(iscp);
291             } else {
292                 code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD,
293                                 userp, NULL, reqp, scpp);
294             }
295             cm_ReleaseSCache(substRootp);
296             if (code) {
297                 osi_Log1(afsd_logp,"cm_ParseIoctlPath [2] code 0x%x", code);
298                 return code;
299             }
300         } else {
301             /* otherwise, treat the name as a cellname mounted off the afs root.
302              * This requires that we reconstruct the shareName string with 
303              * leading and trailing slashes.
304              */
305             p = relativePath + 2 + strlen(cm_NetbiosName) + 1;
306             if ( !_strnicmp("all", p, 3) )
307                 p += 4;
308
309             shareName[0] = '/';
310             for (i = 1; *p && *p != '\\'; i++,p++ ) {
311                 shareName[i] = *p;
312             }
313             p++;                    /* skip past trailing slash */
314             shareName[i++] = '/';       /* add trailing slash */
315             shareName[i] = 0;       /* terminate string */
316
317
318             code = cm_NameI(cm_data.rootSCachep, ioctlp->prefix->data,
319                              CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
320                              userp, shareName, reqp, &substRootp);
321             if (code) {
322                 osi_Log1(afsd_logp,"cm_ParseIoctlPath [3] code 0x%x", code);
323                 return code;
324             }
325
326             lastComponent = strrchr(p, '\\');
327             if (lastComponent && (lastComponent - p) > 1 &&strlen(lastComponent) > 1) {
328                 *lastComponent = '\0';
329                 lastComponent++;
330
331                 code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
332                                  userp, NULL, reqp, &iscp);
333                 if (code == 0)
334                     code = cm_NameI(iscp, lastComponent, CM_FLAG_CASEFOLD | follow,
335                                     userp, NULL, reqp, scpp);
336                 if (iscp)
337                     cm_ReleaseSCache(iscp);
338             } else {
339                 code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD,
340                                 userp, NULL, reqp, scpp);
341             }
342
343             if (code) {
344                 cm_ReleaseSCache(substRootp);
345                 osi_Log1(afsd_logp,"cm_ParseIoctlPath code [4] 0x%x", code);
346                 return code;
347             }
348         }
349     } else {
350         code = cm_NameI(cm_data.rootSCachep, ioctlp->prefix->data,
351                          CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
352                          userp, ioctlp->tidPathp, reqp, &substRootp);
353         if (code) {
354             osi_Log1(afsd_logp,"cm_ParseIoctlPath [6] code 0x%x", code);
355             return code;
356         }
357         
358         lastComponent = strrchr(relativePath, '\\');
359         if (lastComponent && (lastComponent - relativePath) > 1 && strlen(lastComponent) > 1) {
360             *lastComponent = '\0';
361             lastComponent++;
362
363             code = cm_NameI(substRootp, relativePath, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
364                              userp, NULL, reqp, &iscp);
365             if (code == 0)
366                 code = cm_NameI(iscp, lastComponent, CM_FLAG_CASEFOLD | follow,
367                                  userp, NULL, reqp, scpp);
368             if (iscp)
369                 cm_ReleaseSCache(iscp);
370         } else {
371             code = cm_NameI(substRootp, relativePath, CM_FLAG_CASEFOLD | follow,
372                              userp, NULL, reqp, scpp);
373         }
374         if (code) {
375             cm_ReleaseSCache(substRootp);
376             osi_Log1(afsd_logp,"cm_ParseIoctlPath [7] code 0x%x", code);
377             return code;
378         }
379     }
380 #endif /* AFSIFS */
381
382     if (substRootp)
383         cm_ReleaseSCache(substRootp);
384
385     /* and return success */
386     osi_Log1(afsd_logp,"cm_ParseIoctlPath [8] code 0x%x", code);
387     return 0;
388 }
389
390 void cm_SkipIoctlPath(smb_ioctl_t *ioctlp)
391 {
392     size_t temp;
393         
394     temp = strlen(ioctlp->inDatap) + 1;
395     ioctlp->inDatap += temp;
396 }       
397
398 /* 
399  * Must be called before cm_ParseIoctlPath or cm_SkipIoctlPath 
400  */
401 static cm_ioctlQueryOptions_t * 
402 cm_IoctlGetQueryOptions(struct smb_ioctl *ioctlp, struct cm_user *userp)
403 {
404     afs_uint32 pathlen = strlen(ioctlp->inDatap) + 1;
405     char *p = ioctlp->inDatap + pathlen;
406     cm_ioctlQueryOptions_t * optionsp = NULL;
407
408     if (ioctlp->inCopied > p - ioctlp->inAllocp) {
409         optionsp = (cm_ioctlQueryOptions_t *)p;
410         if (optionsp->size < 12 /* minimum size of struct */)
411             optionsp = NULL;
412     }
413
414     return optionsp;
415 }
416
417 /* 
418  * Must be called after cm_ParseIoctlPath or cm_SkipIoctlPath
419  * or any other time that ioctlp->inDatap points at the 
420  * cm_ioctlQueryOptions_t object.
421  */
422 static void
423 cm_IoctlSkipQueryOptions(struct smb_ioctl *ioctlp, struct cm_user *userp)
424 {
425     cm_ioctlQueryOptions_t * optionsp = (cm_ioctlQueryOptions_t *)ioctlp->inDatap;
426     ioctlp->inDatap += optionsp->size;
427 }
428
429 /* format the specified path to look like "/afs/<cellname>/usr", by
430  * adding "/afs" (if necessary) in front, changing any \'s to /'s, and
431  * removing any trailing "/"'s. One weirdo caveat: "/afs" will be
432  * intentionally returned as "/afs/"--this makes submount manipulation
433  * easier (because we can always jump past the initial "/afs" to find
434  * the AFS path that should be written into afsdsbmt.ini).
435  */
436 void cm_NormalizeAfsPath(char *outpathp, long outlen, char *inpathp)
437 {
438     char *cp;
439     char bslash_mountRoot[256];
440        
441     strncpy(bslash_mountRoot, cm_mountRoot, sizeof(bslash_mountRoot) - 1);
442     bslash_mountRoot[0] = '\\';
443        
444     if (!strnicmp (inpathp, cm_mountRoot, strlen(cm_mountRoot)))
445         StringCbCopy(outpathp, outlen, inpathp);
446     else if (!strnicmp (inpathp, bslash_mountRoot, strlen(bslash_mountRoot)))
447         StringCbCopy(outpathp, outlen, inpathp);
448     else if ((inpathp[0] == '/') || (inpathp[0] == '\\'))
449         StringCbPrintfA(outpathp, outlen, "%s%s", cm_mountRoot, inpathp);
450     else // inpathp looks like "<cell>/usr"
451         StringCbPrintfA(outpathp, outlen, "%s/%s", cm_mountRoot, inpathp);
452
453     for (cp = outpathp; *cp != 0; ++cp) {
454         if (*cp == '\\')
455             *cp = '/';
456     }       
457
458     if (strlen(outpathp) && (outpathp[strlen(outpathp)-1] == '/')) {
459         outpathp[strlen(outpathp)-1] = 0;
460     }
461
462     if (!strcmpi (outpathp, cm_mountRoot)) {
463         StringCbCopy(outpathp, outlen, cm_mountRoot);
464     }
465 }
466
467 #define LEAF_SIZE 256
468 /* parse the passed-in file name and do a namei on its parent.  If we fail,
469  * return an error code, otherwise return the vnode located in *scpp.
470  */
471 long cm_ParseIoctlParent(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
472                          cm_scache_t **scpp, char *leafp)
473 {
474     long code;
475     char tbuffer[1024];
476     char *tp, *jp;
477     cm_scache_t *substRootp = NULL;
478
479     StringCbCopyA(tbuffer, sizeof(tbuffer), ioctlp->inDatap);
480     tp = strrchr(tbuffer, '\\');
481     jp = strrchr(tbuffer, '/');
482     if (!tp)
483         tp = jp;
484     else if (jp && (tp - tbuffer) < (jp - tbuffer))
485         tp = jp;
486     if (!tp) {
487         StringCbCopyA(tbuffer, sizeof(tbuffer), "\\");
488         if (leafp) 
489             StringCbCopyA(leafp, LEAF_SIZE, ioctlp->inDatap);
490     }
491     else {
492         *tp = 0;
493         if (leafp) 
494             StringCbCopyA(leafp, LEAF_SIZE, tp+1);
495     }   
496
497     if (tbuffer[0] == tbuffer[1] &&
498         tbuffer[1] == '\\' && 
499         !_strnicmp(cm_NetbiosName,tbuffer+2,strlen(cm_NetbiosName))) 
500     {
501         char shareName[256];
502         char *sharePath;
503         int shareFound, i;
504
505         /* We may have found a UNC path. 
506          * If the first component is the NetbiosName,
507          * then throw out the second component (the submount)
508          * since it had better expand into the value of ioctl->tidPathp
509          */
510         char * p;
511         p = tbuffer + 2 + strlen(cm_NetbiosName) + 1;
512         if ( !_strnicmp("all", p, 3) )
513             p += 4;
514
515         for (i = 0; *p && *p != '\\'; i++,p++ ) {
516             shareName[i] = *p;
517         }
518         p++;                    /* skip past trailing slash */
519         shareName[i] = 0;       /* terminate string */
520
521         shareFound = smb_FindShare(ioctlp->fidp->vcp, ioctlp->uidp, shareName, &sharePath);
522         if ( shareFound ) {
523             /* we found a sharename, therefore use the resulting path */
524             code = cm_NameI(cm_data.rootSCachep, ioctlp->prefix->data,
525                              CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
526                              userp, sharePath, reqp, &substRootp);
527             free(sharePath);
528             if (code) return code;
529
530             code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
531                              userp, NULL, reqp, scpp);
532             cm_ReleaseSCache(substRootp);
533             if (code) return code;
534         } else {
535             /* otherwise, treat the name as a cellname mounted off the afs root.
536              * This requires that we reconstruct the shareName string with 
537              * leading and trailing slashes.
538              */
539             p = tbuffer + 2 + strlen(cm_NetbiosName) + 1;
540             if ( !_strnicmp("all", p, 3) )
541                 p += 4;
542
543             shareName[0] = '/';
544             for (i = 1; *p && *p != '\\'; i++,p++ ) {
545                 shareName[i] = *p;
546             }
547             p++;                    /* skip past trailing slash */
548             shareName[i++] = '/';       /* add trailing slash */
549             shareName[i] = 0;       /* terminate string */
550
551             code = cm_NameI(cm_data.rootSCachep, ioctlp->prefix->data,
552                              CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
553                              userp, shareName, reqp, &substRootp);
554             if (code) return code;
555
556             code = cm_NameI(substRootp, p, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
557                             userp, NULL, reqp, scpp);
558             cm_ReleaseSCache(substRootp);
559             if (code) return code;
560         }
561     } else {
562         code = cm_NameI(cm_data.rootSCachep, ioctlp->prefix->data,
563                         CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
564                         userp, ioctlp->tidPathp, reqp, &substRootp);
565         if (code) return code;
566
567         code = cm_NameI(substRootp, tbuffer, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
568                         userp, NULL, reqp, scpp);
569         cm_ReleaseSCache(substRootp);
570         if (code) return code;
571     }
572
573     /* # of bytes of path */
574     code = (long)strlen(ioctlp->inDatap) + 1;
575     ioctlp->inDatap += code;
576
577     /* and return success */
578     return 0;
579 }
580
581 long cm_IoctlGetACL(smb_ioctl_t *ioctlp, cm_user_t *userp)
582 {
583     cm_conn_t *connp;
584     cm_scache_t *scp;
585     AFSOpaque acl;
586     AFSFetchStatus fileStatus;
587     AFSVolSync volSync;
588     long code;
589     AFSFid fid;
590     int tlen;
591     cm_req_t req;
592     struct rx_connection * callp;
593     cm_ioctlQueryOptions_t *optionsp;
594     afs_uint32 flags = 0;
595
596     cm_InitReq(&req);
597
598     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
599     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
600         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
601
602     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
603         cm_SkipIoctlPath(ioctlp);
604
605         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
606     } else {
607         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
608     }
609     if (code) 
610         return code;
611
612     /* now make the get acl call */
613 #ifdef AFS_FREELANCE_CLIENT
614     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
615         code = 0;
616         ioctlp->outDatap[0] ='\0';
617     } else
618 #endif
619     {
620         fid.Volume = scp->fid.volume;
621         fid.Vnode = scp->fid.vnode;
622         fid.Unique = scp->fid.unique;
623         do {
624             acl.AFSOpaque_val = ioctlp->outDatap;
625             acl.AFSOpaque_len = 0;
626             code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
627             if (code) continue;
628
629             callp = cm_GetRxConn(connp);
630             code = RXAFS_FetchACL(callp, &fid, &acl, &fileStatus, &volSync);
631             rx_PutConnection(callp);
632
633         } while (cm_Analyze(connp, userp, &req, &scp->fid, &volSync, NULL, NULL, code));
634         code = cm_MapRPCError(code, &req);
635         cm_ReleaseSCache(scp);
636
637         if (code) return code;
638     }
639     /* skip over return data */
640     tlen = (int)strlen(ioctlp->outDatap) + 1;
641     ioctlp->outDatap += tlen;
642
643     /* and return success */
644     return 0;
645 }
646
647 long cm_IoctlGetFileCellName(struct smb_ioctl *ioctlp, struct cm_user *userp)
648 {
649     long code;
650     cm_scache_t *scp;
651     cm_cell_t *cellp;
652     cm_req_t req;
653     cm_ioctlQueryOptions_t *optionsp;
654     afs_uint32 flags = 0;
655
656     cm_InitReq(&req);
657
658     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
659     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
660         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
661
662     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
663         cm_SkipIoctlPath(ioctlp);
664         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
665     } else {
666         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
667     }
668     if (code) 
669         return code;
670
671 #ifdef AFS_FREELANCE_CLIENT
672     if ( cm_freelanceEnabled && 
673          scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
674          scp->fid.volume==AFS_FAKE_ROOT_VOL_ID &&
675          scp->fid.vnode==0x1 && scp->fid.unique==0x1 ) {
676         StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), "Freelance.Local.Root");
677         ioctlp->outDatap += strlen(ioctlp->outDatap) + 1;
678         code = 0;
679     } else 
680 #endif /* AFS_FREELANCE_CLIENT */
681     {
682         cellp = cm_FindCellByID(scp->fid.cell, CM_FLAG_NOPROBE);
683         if (cellp) {
684             StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), cellp->name);
685             ioctlp->outDatap += strlen(ioctlp->outDatap) + 1;
686             code = 0;
687         }
688         else 
689             code = CM_ERROR_NOSUCHCELL;
690     }
691
692     cm_ReleaseSCache(scp);
693     return code;
694 }
695
696 long cm_IoctlSetACL(struct smb_ioctl *ioctlp, struct cm_user *userp)
697 {
698     cm_conn_t *connp;
699     cm_scache_t *scp;
700     AFSOpaque acl;
701     AFSFetchStatus fileStatus;
702     AFSVolSync volSync;
703     long code;
704     AFSFid fid;
705     cm_req_t req;
706     struct rx_connection * callp;
707
708     cm_InitReq(&req);
709
710     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, 0);
711     if (code) return code;
712         
713 #ifdef AFS_FREELANCE_CLIENT
714     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
715         code = CM_ERROR_NOACCESS;
716     } else
717 #endif
718     {
719         /* now make the get acl call */
720         fid.Volume = scp->fid.volume;
721         fid.Vnode = scp->fid.vnode;
722         fid.Unique = scp->fid.unique;
723         do {
724             acl.AFSOpaque_val = ioctlp->inDatap;
725             acl.AFSOpaque_len = (u_int)strlen(ioctlp->inDatap)+1;
726             code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
727             if (code) continue;
728
729             callp = cm_GetRxConn(connp);
730             code = RXAFS_StoreACL(callp, &fid, &acl, &fileStatus, &volSync);
731             rx_PutConnection(callp);
732
733         } while (cm_Analyze(connp, userp, &req, &scp->fid, &volSync, NULL, NULL, code));
734         code = cm_MapRPCError(code, &req);
735
736         /* invalidate cache info, since we just trashed the ACL cache */
737         lock_ObtainMutex(&scp->mx);
738         cm_DiscardSCache(scp);
739         lock_ReleaseMutex(&scp->mx);
740     }
741     cm_ReleaseSCache(scp);
742
743     return code;
744 }
745
746
747
748 long cm_IoctlFlushAllVolumes(struct smb_ioctl *ioctlp, struct cm_user *userp)
749 {
750     long code;
751     cm_scache_t *scp;
752     int i;
753     cm_req_t req;
754
755     cm_InitReq(&req);
756
757     cm_SkipIoctlPath(ioctlp);   /* we don't care about the path */
758
759     lock_ObtainWrite(&cm_scacheLock);
760     for (i=0; i<cm_data.scacheHashTableSize; i++) {
761         for (scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp) {
762             cm_HoldSCacheNoLock(scp);
763             lock_ReleaseWrite(&cm_scacheLock);
764
765             /* now flush the file */
766             code = cm_FlushFile(scp, userp, &req);
767             lock_ObtainWrite(&cm_scacheLock);
768             cm_ReleaseSCacheNoLock(scp);
769         }
770     }
771     lock_ReleaseWrite(&cm_scacheLock);
772
773     return code;
774 }
775
776 long cm_IoctlFlushVolume(struct smb_ioctl *ioctlp, struct cm_user *userp)
777 {
778     long code;
779     cm_scache_t *scp;
780     unsigned long volume;
781     unsigned long cell;
782     cm_req_t req;
783     cm_ioctlQueryOptions_t *optionsp;
784     afs_uint32 flags = 0;
785
786     cm_InitReq(&req);
787
788     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
789     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
790         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
791
792     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
793         cm_SkipIoctlPath(ioctlp);
794         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
795     } else {
796         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
797     }
798     if (code) 
799         return code;
800
801 #ifdef AFS_FREELANCE_CLIENT
802     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
803         code = CM_ERROR_NOACCESS;
804     } else
805 #endif
806     {
807         volume = scp->fid.volume;
808         cell = scp->fid.cell;
809         cm_ReleaseSCache(scp);
810
811         code = cm_FlushVolume(userp, &req, cell, volume);
812     }
813     return code;
814 }
815
816 long cm_IoctlFlushFile(struct smb_ioctl *ioctlp, struct cm_user *userp)
817 {
818     long code;
819     cm_scache_t *scp;
820     cm_req_t req;
821     cm_ioctlQueryOptions_t *optionsp;
822     afs_uint32 flags = 0;
823
824     cm_InitReq(&req);
825
826     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
827     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
828         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
829
830     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
831         cm_SkipIoctlPath(ioctlp);
832         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
833     } else {
834         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
835     }
836     if (code) 
837         return code;
838
839 #ifdef AFS_FREELANCE_CLIENT
840     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
841         code = CM_ERROR_NOACCESS;
842     } else
843 #endif
844     {
845         cm_FlushFile(scp, userp, &req);
846     }
847     cm_ReleaseSCache(scp);
848
849     return 0;
850 }
851
852 long cm_IoctlSetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
853 {
854     cm_scache_t *scp;
855     char volName[32];
856     char offLineMsg[256];
857     char motd[256];
858     cm_conn_t *tcp;
859     long code;
860     AFSFetchVolumeStatus volStat;
861     AFSStoreVolumeStatus storeStat;
862     cm_volume_t *tvp;
863     char *cp;
864     cm_cell_t *cellp;
865     cm_req_t req;
866     struct rx_connection * callp;
867
868     cm_InitReq(&req);
869
870     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, 0);
871     if (code) return code;
872
873 #ifdef AFS_FREELANCE_CLIENT
874     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
875         code = CM_ERROR_NOACCESS;
876     } else
877 #endif
878     {
879         cellp = cm_FindCellByID(scp->fid.cell, 0);
880         osi_assertx(cellp, "null cm_cell_t");
881
882         if (scp->flags & CM_SCACHEFLAG_RO) {
883             cm_ReleaseSCache(scp);
884             return CM_ERROR_READONLY;
885         }
886
887         code = cm_GetVolumeByID(cellp, scp->fid.volume, userp, &req, 
888                                  CM_GETVOL_FLAG_CREATE, &tvp);
889         if (code) {
890             cm_ReleaseSCache(scp);
891             return code;
892         }
893         cm_PutVolume(tvp);
894
895         /* Copy the junk out, using cp as a roving pointer. */
896         cp = ioctlp->inDatap;
897         memcpy((char *)&volStat, cp, sizeof(AFSFetchVolumeStatus));
898         cp += sizeof(AFSFetchVolumeStatus);
899         StringCbCopyA(volName, sizeof(volName), cp);
900         cp += strlen(volName)+1;
901         StringCbCopyA(offLineMsg, sizeof(offLineMsg), cp);
902         cp +=  strlen(offLineMsg)+1;
903         StringCbCopyA(motd, sizeof(motd), cp);
904         storeStat.Mask = 0;
905         if (volStat.MinQuota != -1) {
906             storeStat.MinQuota = volStat.MinQuota;
907             storeStat.Mask |= AFS_SETMINQUOTA;
908         }
909         if (volStat.MaxQuota != -1) {
910             storeStat.MaxQuota = volStat.MaxQuota;
911             storeStat.Mask |= AFS_SETMAXQUOTA;
912         }
913
914         do {
915             code = cm_ConnFromFID(&scp->fid, userp, &req, &tcp);
916             if (code) continue;
917
918             callp = cm_GetRxConn(tcp);
919             code = RXAFS_SetVolumeStatus(callp, scp->fid.volume,
920                                           &storeStat, volName, offLineMsg, motd);
921             rx_PutConnection(callp);
922
923         } while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, NULL, code));
924         code = cm_MapRPCError(code, &req);
925     }
926     
927     /* return on failure */
928     cm_ReleaseSCache(scp);
929     if (code) {
930         return code;
931     }
932
933     /* we are sending parms back to make compat. with prev system.  should
934      * change interface later to not ask for current status, just set
935      * new status
936      */
937     cp = ioctlp->outDatap;
938     memcpy(cp, (char *)&volStat, sizeof(VolumeStatus));
939     cp += sizeof(VolumeStatus);
940     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), volName);
941     cp += strlen(volName)+1;
942     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), offLineMsg);
943     cp += strlen(offLineMsg)+1;
944     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), motd);
945     cp += strlen(motd)+1;
946
947     /* now return updated return data pointer */
948     ioctlp->outDatap = cp;
949
950     return 0;
951 }       
952
953 long cm_IoctlGetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
954 {
955     char volName[32];
956     cm_scache_t *scp;
957     char offLineMsg[256];
958     char motd[256];
959     cm_conn_t *connp;
960     register long code;
961     AFSFetchVolumeStatus volStat;
962     register char *cp;
963     char *Name;
964     char *OfflineMsg;
965     char *MOTD;
966     cm_req_t req;
967     struct rx_connection * callp;
968     cm_ioctlQueryOptions_t *optionsp;
969     afs_uint32 flags = 0;
970
971     cm_InitReq(&req);
972
973     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
974     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
975         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
976
977     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
978         cm_SkipIoctlPath(ioctlp);
979         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
980     } else {
981         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
982     }
983     if (code) 
984         return code;
985
986 #ifdef AFS_FREELANCE_CLIENT
987     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
988         code = 0;
989         strncpy(volName, "Freelance.Local.Root", sizeof(volName));
990         offLineMsg[0] = '\0';
991         strncpy(motd, "Freelance mode in use.", sizeof(motd));
992         volStat.Vid = scp->fid.volume;
993         volStat.MaxQuota = 0;
994         volStat.BlocksInUse = 100;
995         volStat.PartBlocksAvail = 0;
996         volStat.PartMaxBlocks = 100;
997     } else
998 #endif
999     {
1000         Name = volName;
1001         OfflineMsg = offLineMsg;
1002         MOTD = motd;
1003         do {
1004             code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
1005             if (code) continue;
1006
1007             callp = cm_GetRxConn(connp);
1008             code = RXAFS_GetVolumeStatus(callp, scp->fid.volume,
1009                                          &volStat, &Name, &OfflineMsg, &MOTD);
1010             rx_PutConnection(callp);
1011
1012         } while (cm_Analyze(connp, userp, &req, &scp->fid, NULL, NULL, NULL, code));
1013         code = cm_MapRPCError(code, &req);
1014     }
1015
1016     cm_ReleaseSCache(scp);
1017     if (code) return code;
1018
1019     /* Copy all this junk into msg->im_data, keeping track of the lengths. */
1020     cp = ioctlp->outDatap;
1021     memcpy(cp, (char *)&volStat, sizeof(AFSFetchVolumeStatus));
1022     cp += sizeof(AFSFetchVolumeStatus);
1023     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), volName);
1024     cp += strlen(volName)+1;
1025     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), offLineMsg);
1026     cp += strlen(offLineMsg)+1;
1027     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), motd);
1028     cp += strlen(motd)+1;
1029
1030     /* return new size */
1031     ioctlp->outDatap = cp;
1032
1033     return 0;
1034 }
1035
1036 long cm_IoctlGetFid(struct smb_ioctl *ioctlp, struct cm_user *userp)
1037 {
1038     cm_scache_t *scp;
1039     register long code;
1040     register char *cp;
1041     cm_fid_t fid;
1042     cm_req_t req;
1043     cm_ioctlQueryOptions_t * optionsp;
1044     afs_uint32 flags = 0;
1045
1046     cm_InitReq(&req);
1047
1048     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
1049     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
1050         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
1051
1052     code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
1053     if (code) return code;
1054
1055     memset(&fid, 0, sizeof(cm_fid_t));
1056     fid.cell   = scp->fid.cell;
1057     fid.volume = scp->fid.volume;
1058     fid.vnode  = scp->fid.vnode;
1059     fid.unique = scp->fid.unique;
1060
1061     cm_ReleaseSCache(scp);
1062
1063     /* Copy all this junk into msg->im_data, keeping track of the lengths. */
1064     cp = ioctlp->outDatap;
1065     memcpy(cp, (char *)&fid, sizeof(cm_fid_t));
1066     cp += sizeof(cm_fid_t);
1067
1068     /* return new size */
1069     ioctlp->outDatap = cp;
1070
1071     return 0;
1072 }
1073
1074 long cm_IoctlGetFileType(struct smb_ioctl *ioctlp, struct cm_user *userp)
1075 {
1076     cm_scache_t *scp;
1077     register long code;
1078     register char *cp;
1079     afs_uint32 fileType = 0;
1080     cm_req_t req;
1081     cm_ioctlQueryOptions_t * optionsp;
1082     afs_uint32 flags = 0;
1083
1084     cm_InitReq(&req);
1085
1086     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
1087     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
1088         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
1089
1090     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
1091         cm_SkipIoctlPath(ioctlp);
1092         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
1093     } else {
1094         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
1095     }
1096     if (code) 
1097         return code;
1098
1099     fileType = scp->fileType;
1100     cm_ReleaseSCache(scp);
1101
1102     /* Copy all this junk into msg->im_data, keeping track of the lengths. */
1103     cp = ioctlp->outDatap;
1104     memcpy(cp, (char *)&fileType, sizeof(fileType));
1105     cp += sizeof(fileType);
1106
1107     /* return new size */
1108     ioctlp->outDatap = cp;
1109
1110     return 0;
1111 }
1112
1113 long cm_IoctlGetOwner(struct smb_ioctl *ioctlp, struct cm_user *userp)
1114 {
1115     cm_scache_t *scp;
1116     register long code;
1117     register char *cp;
1118     cm_req_t req;
1119     cm_ioctlQueryOptions_t *optionsp;
1120     afs_uint32 flags = 0;
1121
1122     cm_InitReq(&req);
1123
1124     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
1125     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
1126         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
1127
1128     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
1129         cm_SkipIoctlPath(ioctlp);
1130         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
1131     } else {
1132         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
1133     }
1134     if (code) 
1135         return code;
1136
1137     /* Copy all this junk into msg->im_data, keeping track of the lengths. */
1138     cp = ioctlp->outDatap;
1139     memcpy(cp, (char *)&scp->owner, sizeof(afs_uint32));
1140     cp += sizeof(afs_uint32);
1141     memcpy(cp, (char *)&scp->group, sizeof(afs_uint32));
1142     cp += sizeof(afs_uint32);
1143
1144     /* return new size */
1145     ioctlp->outDatap = cp;
1146
1147     cm_ReleaseSCache(scp);
1148
1149     return 0;
1150 }
1151
1152 long cm_IoctlWhereIs(struct smb_ioctl *ioctlp, struct cm_user *userp)
1153 {
1154     long code;
1155     cm_scache_t *scp;
1156     cm_cell_t *cellp;
1157     cm_volume_t *tvp;
1158     cm_serverRef_t **tsrpp, *current;
1159     cm_server_t *tsp;
1160     unsigned long volume;
1161     char *cp;
1162     cm_req_t req;
1163     cm_ioctlQueryOptions_t *optionsp;
1164     afs_uint32 flags = 0;
1165
1166     cm_InitReq(&req);
1167
1168     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
1169     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
1170         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
1171
1172     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
1173         cm_SkipIoctlPath(ioctlp);
1174         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
1175     } else {
1176         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
1177     }
1178     if (code) 
1179         return code;
1180
1181     volume = scp->fid.volume;
1182
1183     cellp = cm_FindCellByID(scp->fid.cell, 0);
1184
1185     cm_ReleaseSCache(scp);
1186
1187     if (!cellp)
1188         return CM_ERROR_NOSUCHCELL;
1189
1190 #ifdef AFS_FREELANCE_CLIENT
1191     if ( cellp->cellID == AFS_FAKE_ROOT_CELL_ID) {
1192         struct in_addr addr;
1193
1194         addr.s_net = 127;
1195         addr.s_host = 0;
1196         addr.s_lh = 0;
1197         addr.s_impno = 1;
1198
1199         cp = ioctlp->outDatap;
1200         
1201         memcpy(cp, (char *)&addr, sizeof(addr));
1202         cp += sizeof(addr);
1203
1204         /* still room for terminating NULL, add it on */
1205         addr.s_addr = 0;
1206         memcpy(cp, (char *)&addr, sizeof(addr));
1207         cp += sizeof(addr);
1208
1209         ioctlp->outDatap = cp;
1210     } else 
1211 #endif
1212     {
1213         code = cm_GetVolumeByID(cellp, volume, userp, &req, CM_GETVOL_FLAG_CREATE, &tvp);
1214         if (code) 
1215             return code;
1216         
1217         cp = ioctlp->outDatap;
1218         
1219         lock_ObtainMutex(&tvp->mx);
1220         tsrpp = cm_GetVolServers(tvp, volume);
1221         lock_ObtainRead(&cm_serverLock);
1222         for (current = *tsrpp; current; current = current->next) {
1223             tsp = current->server;
1224             memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
1225             cp += sizeof(long);
1226         }
1227         lock_ReleaseRead(&cm_serverLock);
1228         cm_FreeServerList(tsrpp, 0);
1229         lock_ReleaseMutex(&tvp->mx);
1230
1231         /* still room for terminating NULL, add it on */
1232         volume = 0;     /* reuse vbl */
1233         memcpy(cp, (char *)&volume, sizeof(long));
1234         cp += sizeof(long);
1235
1236         ioctlp->outDatap = cp;
1237         cm_PutVolume(tvp);
1238     }
1239     return 0;
1240 }       
1241
1242 long cm_IoctlStatMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
1243 {
1244     long code;
1245     cm_scache_t *dscp;
1246     cm_scache_t *scp;
1247     char *cp;
1248     cm_req_t req;
1249
1250     cm_InitReq(&req);
1251
1252     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp, 0);
1253     if (code) return code;
1254
1255     cp = ioctlp->inDatap;
1256
1257     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
1258     cm_ReleaseSCache(dscp);
1259     if (code) return code;
1260         
1261     lock_ObtainMutex(&scp->mx);
1262
1263     /* now check that this is a real mount point */
1264     if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
1265         lock_ReleaseMutex(&scp->mx);
1266         cm_ReleaseSCache(scp);
1267         return CM_ERROR_INVAL;
1268     }
1269
1270     code = cm_ReadMountPoint(scp, userp, &req);
1271     if (code == 0) {
1272         cp = ioctlp->outDatap;
1273         StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), scp->mountPointStringp);
1274         cp += strlen(cp) + 1;
1275         ioctlp->outDatap = cp;
1276     }
1277     lock_ReleaseMutex(&scp->mx);
1278     cm_ReleaseSCache(scp);
1279
1280     return code;
1281 }       
1282
1283 long cm_IoctlDeleteMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
1284 {
1285     long code;
1286     cm_scache_t *dscp;
1287     cm_scache_t *scp;
1288     char *cp;
1289     cm_req_t req;
1290
1291     cm_InitReq(&req);
1292
1293     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp, 0);
1294     if (code) return code;
1295
1296     cp = ioctlp->inDatap;
1297
1298     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
1299         
1300     /* if something went wrong, bail out now */
1301     if (code) {
1302         goto done2;
1303     }
1304         
1305     lock_ObtainMutex(&scp->mx);
1306     code = cm_SyncOp(scp, NULL, userp, &req, 0,
1307                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1308     if (code) {     
1309         lock_ReleaseMutex(&scp->mx);
1310         cm_ReleaseSCache(scp);
1311         goto done2;
1312     }
1313
1314     /* now check that this is a real mount point */
1315     if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
1316         lock_ReleaseMutex(&scp->mx);
1317         cm_ReleaseSCache(scp);
1318         code = CM_ERROR_INVAL;
1319         goto done1;
1320     }
1321
1322     /* time to make the RPC, so drop the lock */
1323     lock_ReleaseMutex(&scp->mx);
1324     cm_ReleaseSCache(scp);
1325
1326     /* easier to do it this way */
1327     code = cm_Unlink(dscp, cp, userp, &req);
1328     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1329         smb_NotifyChange(FILE_ACTION_REMOVED,
1330                           FILE_NOTIFY_CHANGE_DIR_NAME,
1331                           dscp, cp, NULL, TRUE);
1332
1333   done1:
1334     lock_ObtainMutex(&scp->mx);
1335     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1336     lock_ReleaseMutex(&scp->mx);
1337
1338   done2:
1339     cm_ReleaseSCache(dscp);
1340     return code;
1341 }
1342
1343 long cm_IoctlCheckServers(struct smb_ioctl *ioctlp, struct cm_user *userp)
1344 {
1345     cm_cell_t *cellp;
1346     chservinfo_t csi;
1347     char *tp;
1348     char *cp;
1349     long temp;
1350     cm_server_t *tsp;
1351     int haveCell;
1352         
1353     cm_SkipIoctlPath(ioctlp);   /* we don't care about the path */
1354     tp = ioctlp->inDatap;
1355     haveCell = 0;
1356
1357     memcpy(&temp, tp, sizeof(temp));
1358     if (temp == 0x12345678) {   /* For afs3.3 version */
1359         memcpy(&csi, tp, sizeof(csi));
1360         if (csi.tinterval >= 0) {
1361             cp = ioctlp->outDatap;
1362             memcpy(cp, (char *)&cm_daemonCheckDownInterval, sizeof(long));
1363             ioctlp->outDatap += sizeof(long);
1364             if (csi.tinterval > 0) {
1365                 if (!smb_SUser(userp))
1366                     return CM_ERROR_NOACCESS;
1367                 cm_daemonCheckDownInterval = csi.tinterval;
1368             }
1369             return 0;
1370         }
1371         if (csi.tsize)
1372             haveCell = 1;
1373         temp = csi.tflags;
1374         cp = csi.tbuffer;
1375     } else {    /* For pre afs3.3 versions */
1376         memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
1377         ioctlp->inDatap = cp = ioctlp->inDatap + sizeof(long);
1378         if (cp - ioctlp->inAllocp < ioctlp->inCopied)   /* still more data available */
1379             haveCell = 1;
1380     }       
1381
1382     /* 
1383      * 1: fast check, don't contact servers.
1384      * 2: local cell only.
1385      */
1386     if (haveCell) {
1387         /* have cell name, too */
1388         cellp = cm_GetCell(cp, (temp & 1) ? CM_FLAG_NOPROBE : 0);
1389         if (!cellp) 
1390             return CM_ERROR_NOSUCHCELL;
1391     }
1392     else cellp = (cm_cell_t *) 0;
1393     if (!cellp && (temp & 2)) {
1394         /* use local cell */
1395         cellp = cm_FindCellByID(1, 0);
1396     }
1397     if (!(temp & 1)) {  /* if not fast, call server checker routine */
1398         /* check down servers */
1399         cm_CheckServers(CM_FLAG_CHECKDOWNSERVERS | CM_FLAG_CHECKUPSERVERS,
1400                          cellp);
1401     }       
1402
1403     /* now return the current down server list */
1404     cp = ioctlp->outDatap;
1405     lock_ObtainRead(&cm_serverLock);
1406     for (tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
1407         if (cellp && tsp->cellp != cellp) continue;     /* cell spec'd and wrong */
1408         if ((tsp->flags & CM_SERVERFLAG_DOWN)
1409              && tsp->type == CM_SERVER_FILE) {
1410             memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
1411             cp += sizeof(long);
1412         }
1413     }
1414     lock_ReleaseRead(&cm_serverLock);
1415
1416     ioctlp->outDatap = cp;
1417     return 0;
1418 }
1419
1420 long cm_IoctlGag(struct smb_ioctl *ioctlp, struct cm_user *userp)
1421 {
1422     /* we don't print anything superfluous, so we don't support the gag call */
1423     return CM_ERROR_INVAL;
1424 }
1425
1426 long cm_IoctlCheckVolumes(struct smb_ioctl *ioctlp, struct cm_user *userp)
1427 {
1428     cm_RefreshVolumes();
1429     return 0;
1430 }       
1431
1432 long cm_IoctlSetCacheSize(struct smb_ioctl *ioctlp, struct cm_user *userp)
1433 {
1434     afs_uint64 temp;
1435     long code;
1436
1437     cm_SkipIoctlPath(ioctlp);
1438
1439     memcpy(&temp, ioctlp->inDatap, sizeof(temp));
1440     if (temp == 0) 
1441         temp = cm_data.buf_nOrigBuffers;
1442     else {
1443         /* temp is in 1K units, convert to # of buffers */
1444         temp = temp / (cm_data.buf_blockSize / 1024);
1445     }       
1446
1447     /* now adjust the cache size */
1448     code = buf_SetNBuffers(temp);
1449
1450     return code;
1451 }
1452
1453 long cm_IoctlTraceControl(struct smb_ioctl *ioctlp, struct cm_user *userp)
1454 {
1455     long inValue;
1456         
1457     cm_SkipIoctlPath(ioctlp);
1458         
1459     memcpy(&inValue, ioctlp->inDatap, sizeof(long));
1460
1461     /* print trace */
1462     if (inValue & 8) {
1463         afsd_ForceTrace(FALSE);
1464         buf_ForceTrace(FALSE);
1465     }
1466         
1467     if (inValue & 2) {
1468         /* set tracing value to low order bit */
1469         if ((inValue & 1) == 0) {
1470             /* disable tracing */
1471             osi_LogDisable(afsd_logp);
1472             rx_DebugOnOff(FALSE);
1473         }
1474         else {
1475             /* enable tracing */
1476             osi_LogEnable(afsd_logp);
1477             rx_DebugOnOff(TRUE);
1478         }
1479     }
1480
1481     /* see if we're supposed to do a reset, too */
1482     if (inValue & 4) {
1483         osi_LogReset(afsd_logp);
1484     }
1485
1486     /* and copy out tracing flag */
1487     inValue = afsd_logp->enabled;       /* use as a temp vbl */
1488     memcpy(ioctlp->outDatap, &inValue, sizeof(long));
1489     ioctlp->outDatap += sizeof(long);
1490     return 0;
1491 }       
1492
1493 long cm_IoctlGetCacheParms(struct smb_ioctl *ioctlp, struct cm_user *userp)
1494 {
1495     cm_cacheParms_t parms;
1496
1497     memset(&parms, 0, sizeof(parms));
1498
1499     /* first we get, in 1K units, the cache size */
1500     parms.parms[0] = cm_data.buf_nbuffers * (cm_data.buf_blockSize / 1024);
1501
1502     /* and then the actual # of buffers in use (not in the free list, I guess,
1503      * will be what we do).
1504      */
1505     parms.parms[1] = (cm_data.buf_nbuffers - buf_CountFreeList()) * (cm_data.buf_blockSize / 1024);
1506
1507     memcpy(ioctlp->outDatap, &parms, sizeof(parms));
1508     ioctlp->outDatap += sizeof(parms);
1509
1510     return 0;
1511 }
1512
1513 long cm_IoctlGetCell(struct smb_ioctl *ioctlp, struct cm_user *userp)
1514 {
1515     long whichCell;
1516     long magic = 0;
1517     cm_cell_t *tcellp;
1518     cm_serverRef_t *serverRefp;
1519     cm_server_t *serverp;
1520     long i;
1521     char *cp;
1522     char *tp;
1523     char *basep;
1524
1525     cm_SkipIoctlPath(ioctlp);
1526
1527     tp = ioctlp->inDatap;
1528
1529     memcpy((char *)&whichCell, tp, sizeof(long));
1530     tp += sizeof(long);
1531
1532     /* see if more than one long passed in, ignoring the null pathname (the -1) */
1533     if (ioctlp->inCopied-1 > sizeof(long)) {
1534         memcpy((char *)&magic, tp, sizeof(long));
1535     }
1536
1537     lock_ObtainRead(&cm_cellLock);
1538     for (tcellp = cm_data.allCellsp; tcellp; tcellp = tcellp->allNextp) {
1539         if (whichCell == 0) break;
1540         whichCell--;
1541     }
1542     lock_ReleaseRead(&cm_cellLock);
1543     if (tcellp) {
1544         int max = 8;
1545
1546         cp = ioctlp->outDatap;
1547
1548         if (magic == 0x12345678) {
1549             memcpy(cp, (char *)&magic, sizeof(long));
1550             max = 13;
1551         }
1552         memset(cp, 0, max * sizeof(long));
1553         basep = cp;
1554         lock_ObtainRead(&cm_serverLock);        /* for going down server list */
1555         /* jaltman - do the reference counts to serverRefp contents need to be increased? */
1556         serverRefp = tcellp->vlServersp;
1557         for (i=0; i<max; i++) {
1558             if (!serverRefp) break;
1559             serverp = serverRefp->server;
1560             memcpy(cp, &serverp->addr.sin_addr.s_addr, sizeof(long));
1561             cp += sizeof(long);
1562             serverRefp = serverRefp->next;
1563         }
1564         lock_ReleaseRead(&cm_serverLock);
1565         cp = basep + max * sizeof(afs_int32);
1566         StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), tcellp->name);
1567         cp += strlen(tcellp->name)+1;
1568         ioctlp->outDatap = cp;
1569     }
1570
1571     if (tcellp) 
1572         return 0;
1573     else 
1574         return CM_ERROR_NOMORETOKENS;   /* mapped to EDOM */
1575 }
1576
1577 extern long cm_AddCellProc(void *rockp, struct sockaddr_in *addrp, char *namep);
1578
1579 long cm_IoctlNewCell(struct smb_ioctl *ioctlp, struct cm_user *userp)
1580 {
1581     /* NT cache manager will read cell information from CellServDB each time
1582      * cell is accessed. So, this call is necessary only if list of server for a cell 
1583      * changes (or IP addresses of cell servers changes).
1584      * All that needs to be done is to refresh server information for all cells that 
1585      * are already loaded.
1586   
1587      * cell list will be cm_CellLock and cm_ServerLock will be held for write.
1588      */  
1589   
1590     cm_cell_t *cp;
1591   
1592     cm_SkipIoctlPath(ioctlp);
1593     lock_ObtainWrite(&cm_cellLock);
1594   
1595     for (cp = cm_data.allCellsp; cp; cp=cp->allNextp) 
1596     {
1597         long code;
1598         lock_ObtainMutex(&cp->mx);
1599         /* delete all previous server lists - cm_FreeServerList will ask for write on cm_ServerLock*/
1600         cm_FreeServerList(&cp->vlServersp, CM_FREESERVERLIST_DELETE);
1601         cp->vlServersp = NULL;
1602         code = cm_SearchCellFile(cp->name, cp->name, cm_AddCellProc, cp);
1603 #ifdef AFS_AFSDB_ENV
1604         if (code) {
1605             if (cm_dnsEnabled) {
1606                 int ttl;
1607                 code = cm_SearchCellByDNS(cp->name, cp->name, &ttl, cm_AddCellProc, cp);
1608                 if ( code == 0 ) { /* got cell from DNS */
1609                     cp->flags |= CM_CELLFLAG_DNS;
1610                     cp->flags &= ~CM_CELLFLAG_VLSERVER_INVALID;
1611                     cp->timeout = time(0) + ttl;
1612                 }
1613             }
1614         } 
1615         else {
1616             cp->flags &= ~CM_CELLFLAG_DNS;
1617         }
1618 #endif /* AFS_AFSDB_ENV */
1619         if (code) {
1620             cp->flags |= CM_CELLFLAG_VLSERVER_INVALID;
1621         }
1622         else {
1623             cp->flags &= ~CM_CELLFLAG_VLSERVER_INVALID;
1624             cm_RandomizeServer(&cp->vlServersp);
1625         }
1626         lock_ReleaseMutex(&cp->mx);
1627     }
1628     
1629     lock_ReleaseWrite(&cm_cellLock);
1630     return 0;       
1631 }
1632
1633 long cm_IoctlGetWsCell(smb_ioctl_t *ioctlp, cm_user_t *userp)
1634 {
1635         long code = 0;
1636
1637         if (cm_freelanceEnabled) {
1638             if (cm_GetRootCellName(ioctlp->outDatap))
1639                 StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), "Freelance.Local.Root");
1640             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1641         } else if (cm_data.rootCellp) {
1642             /* return the default cellname to the caller */
1643             StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), cm_data.rootCellp->name);
1644             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1645         } else {
1646             /* if we don't know our default cell, return failure */
1647             code = CM_ERROR_NOSUCHCELL;
1648     }
1649
1650     return code;
1651 }
1652
1653 long cm_IoctlSysName(struct smb_ioctl *ioctlp, struct cm_user *userp)
1654 {
1655     long setSysName, foundname = 0;
1656     char *cp, *cp2, inname[MAXSYSNAME], outname[MAXSYSNAME];
1657     int t, count, num = 0;
1658     char **sysnamelist[MAXSYSNAME];
1659         
1660     cm_SkipIoctlPath(ioctlp);
1661
1662     memcpy(&setSysName, ioctlp->inDatap, sizeof(long));
1663     ioctlp->inDatap += sizeof(long);
1664         
1665     if (setSysName) {
1666         /* check my args */
1667         if ( setSysName < 0 || setSysName > MAXNUMSYSNAMES )
1668             return EINVAL;
1669         cp2 = ioctlp->inDatap;
1670         for ( cp=ioctlp->inDatap, count = 0; count < setSysName; count++ ) {
1671             /* won't go past end of ioctlp->inDatap since maxsysname*num < ioctlp->inDatap length */
1672             t = (int)strlen(cp);
1673             if (t >= MAXSYSNAME || t <= 0)
1674                 return EINVAL;
1675             /* check for names that can shoot us in the foot */
1676             if (*cp == '.' && (cp[1] == 0 || (cp[1] == '.' && cp[2] == 0)))
1677                 return EINVAL;
1678             cp += t + 1;
1679         }
1680         /* args ok */
1681
1682         /* inname gets first entry in case we're being a translator */
1683         /* (we are never a translator) */
1684         t = (int)strlen(ioctlp->inDatap);
1685         memcpy(inname, ioctlp->inDatap, t + 1);
1686         ioctlp->inDatap += t + 1;
1687         num = count;
1688     }
1689
1690     /* Not xlating, so local case */
1691     if (!cm_sysName)
1692         osi_panic("cm_IoctlSysName: !cm_sysName\n", __FILE__, __LINE__);
1693
1694     if (!setSysName) {      /* user just wants the info */
1695         StringCbCopyA(outname, sizeof(outname), cm_sysName);
1696         foundname = cm_sysNameCount;
1697         *sysnamelist = cm_sysNameList;
1698     } else {        
1699         /* Local guy; only root can change sysname */
1700         /* clear @sys entries from the dnlc, once afs_lookup can
1701          * do lookups of @sys entries and thinks it can trust them */
1702         /* privs ok, store the entry, ... */
1703         StringCbCopyA(cm_sysName, sizeof(cm_sysName), inname);
1704         StringCbCopyA(cm_sysNameList[0], MAXSYSNAME, inname);
1705         if (setSysName > 1) {       /* ... or list */
1706             cp = ioctlp->inDatap;
1707             for (count = 1; count < setSysName; ++count) {
1708                 if (!cm_sysNameList[count])
1709                     osi_panic("cm_IoctlSysName: no cm_sysNameList entry to write\n",
1710                                __FILE__, __LINE__);
1711                 t = (int)strlen(cp);
1712                 StringCbCopyA(cm_sysNameList[count], MAXSYSNAME, cp);
1713                 cp += t + 1;
1714             }
1715         }
1716         cm_sysNameCount = setSysName;
1717     }
1718
1719     if (!setSysName) {
1720         /* return the sysname to the caller */
1721         cp = ioctlp->outDatap;
1722         memcpy(cp, (char *)&foundname, sizeof(afs_int32));
1723         cp += sizeof(afs_int32);        /* skip found flag */
1724         if (foundname) {
1725             StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), outname);
1726             cp += strlen(outname) + 1;  /* skip name and terminating null char */
1727             for ( count=1; count < foundname ; ++count) {   /* ... or list */
1728                 if ( !(*sysnamelist)[count] )
1729                     osi_panic("cm_IoctlSysName: no cm_sysNameList entry to read\n", 
1730                                __FILE__, __LINE__);
1731                 t = (int)strlen((*sysnamelist)[count]);
1732                 if (t >= MAXSYSNAME)
1733                     osi_panic("cm_IoctlSysName: sysname entry garbled\n", 
1734                                __FILE__, __LINE__);
1735                 StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), (*sysnamelist)[count]);
1736                 cp += t + 1;
1737             }
1738         }
1739         ioctlp->outDatap = cp;
1740     }
1741         
1742     /* done: success */
1743     return 0;
1744 }
1745
1746 long cm_IoctlGetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
1747 {
1748     long temp;
1749     cm_cell_t *cellp;
1750
1751     cm_SkipIoctlPath(ioctlp);
1752
1753     cellp = cm_GetCell(ioctlp->inDatap, 0);
1754     if (!cellp) 
1755         return CM_ERROR_NOSUCHCELL;
1756
1757     temp = 0;
1758     lock_ObtainMutex(&cellp->mx);
1759     if (cellp->flags & CM_CELLFLAG_SUID)
1760         temp |= CM_SETCELLFLAG_SUID;
1761     lock_ReleaseMutex(&cellp->mx);
1762         
1763     /* now copy out parm */
1764     memcpy(ioctlp->outDatap, &temp, sizeof(long));
1765     ioctlp->outDatap += sizeof(long);
1766
1767     return 0;
1768 }
1769
1770 long cm_IoctlSetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
1771 {
1772     long temp;
1773     cm_cell_t *cellp;
1774
1775     cm_SkipIoctlPath(ioctlp);
1776
1777     cellp = cm_GetCell(ioctlp->inDatap + 2*sizeof(long), 0);
1778     if (!cellp) 
1779         return CM_ERROR_NOSUCHCELL;
1780
1781     memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
1782
1783     lock_ObtainMutex(&cellp->mx);
1784     if (temp & CM_SETCELLFLAG_SUID)
1785         cellp->flags |= CM_CELLFLAG_SUID;
1786     else
1787         cellp->flags &= ~CM_CELLFLAG_SUID;
1788     lock_ReleaseMutex(&cellp->mx);
1789
1790     return 0;
1791 }
1792
1793 long cm_IoctlSetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
1794 {
1795     cm_SSetPref_t         *spin; /* input */
1796     cm_SPref_t        *srvin;   /* one input component */
1797     cm_server_t       *tsp;
1798     int                   i, vlonly, noServers, type;
1799     struct sockaddr_in  tmp;
1800     unsigned short        rank;
1801
1802     cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
1803
1804     spin           = (cm_SSetPref_t *)ioctlp->inDatap;
1805     noServers  = spin->num_servers;
1806     vlonly     = spin->flags;
1807     if ( vlonly )
1808         type = CM_SERVER_VLDB;
1809     else    
1810         type = CM_SERVER_FILE;
1811
1812     for ( i=0; i < noServers; i++) 
1813     {
1814         srvin          = &(spin->servers[i]);
1815         rank           = srvin->rank + (rand() & 0x000f);
1816         tmp.sin_addr   = srvin->host;
1817         tmp.sin_family = AF_INET;
1818
1819         tsp = cm_FindServer(&tmp, type);
1820         if ( tsp )              /* an existing server - ref count increased */
1821         {
1822             tsp->ipRank = rank; /* no need to protect by mutex*/
1823
1824             if (type == CM_SERVER_FILE)
1825             {   /* fileserver */
1826                 /* find volumes which might have RO copy 
1827                 /* on server and change the ordering of 
1828                  * their RO list 
1829                  */
1830                 cm_ChangeRankVolume(tsp);
1831             }
1832             else        
1833             {
1834                 /* set preferences for an existing vlserver */
1835                 cm_ChangeRankCellVLServer(tsp);
1836             }
1837         }
1838         else    /* add a new server without a cell */
1839         {
1840             tsp = cm_NewServer(&tmp, type, NULL, CM_FLAG_NOPROBE); /* refcount = 1 */
1841             tsp->ipRank = rank;
1842         }
1843         lock_ObtainMutex(&tsp->mx);
1844         tsp->flags |= CM_SERVERFLAG_PREF_SET;
1845         lock_ReleaseMutex(&tsp->mx);
1846         cm_PutServer(tsp);  /* decrease refcount */
1847     }
1848     return 0;
1849 }
1850
1851 long cm_IoctlGetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
1852 {
1853     cm_SPrefRequest_t *spin; /* input */
1854     cm_SPrefInfo_t    *spout;   /* output */
1855     cm_SPref_t        *srvout;   /* one output component */
1856     cm_server_t       *tsp;
1857     int                   i, vlonly, noServers;
1858
1859     cm_SkipIoctlPath(ioctlp);       /* we don't care about the path */
1860
1861     spin      = (cm_SPrefRequest_t *)ioctlp->inDatap;
1862     spout     = (cm_SPrefInfo_t *) ioctlp->outDatap;
1863     srvout    = spout->servers;
1864     noServers = spin->num_servers; 
1865     vlonly    = spin->flags & CM_SPREF_VLONLY;
1866     spout->num_servers = 0;
1867
1868     lock_ObtainRead(&cm_serverLock); /* get server lock */
1869
1870     for (tsp=cm_allServersp, i=0; tsp && noServers; tsp=tsp->allNextp,i++){
1871         if (spin->offset > i) {
1872             continue;    /* catch up to where we left off */
1873         }
1874
1875         if ( vlonly && (tsp->type == CM_SERVER_FILE) )
1876             continue;   /* ignore fileserver for -vlserver option*/
1877         if ( !vlonly && (tsp->type == CM_SERVER_VLDB) )
1878             continue;   /* ignore vlservers */
1879
1880         srvout->host = tsp->addr.sin_addr;
1881         srvout->rank = tsp->ipRank;
1882         srvout++;       
1883         spout->num_servers++;
1884         noServers--;
1885     }
1886     lock_ReleaseRead(&cm_serverLock); /* release server lock */
1887
1888     if ( tsp )  /* we ran out of space in the output buffer */
1889         spout->next_offset = i;
1890     else    
1891         spout->next_offset = 0; 
1892     ioctlp->outDatap += sizeof(cm_SPrefInfo_t) + 
1893         (spout->num_servers -1 ) * sizeof(cm_SPref_t) ;
1894     return 0;
1895 }
1896
1897 long cm_IoctlStoreBehind(struct smb_ioctl *ioctlp, struct cm_user *userp)
1898 {
1899     /* we ignore default asynchrony since we only have one way
1900      * of doing this today.
1901      */
1902     return 0;
1903 }       
1904
1905 long cm_IoctlCreateMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
1906 {
1907     char leaf[LEAF_SIZE];
1908     long code;
1909     cm_scache_t *dscp;
1910     cm_attr_t tattr;
1911     char *cp;
1912     cm_req_t req;
1913     char mpInfo[256];
1914     char fullCell[256];
1915     char volume[256];
1916     char cell[256];
1917     int ttl;
1918
1919     cm_InitReq(&req);
1920         
1921     code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
1922     if (code) 
1923         return code;
1924
1925     /* Translate chars for the mount point name */
1926     TranslateExtendedChars(leaf);
1927
1928     /* 
1929      * The fs command allows the user to specify partial cell names on NT.  These must
1930      * be expanded to the full cell name for mount points so that the mount points will
1931      * work on UNIX clients.
1932      */
1933
1934     /* Extract the possibly partial cell name */
1935     StringCbCopyA(cell, sizeof(cell), ioctlp->inDatap + 1);      /* Skip the mp type character */
1936         
1937     if (cp = strchr(cell, ':')) {
1938         /* Extract the volume name */
1939         *cp = 0;
1940         StringCbCopyA(volume,  sizeof(volume), cp + 1);
1941         
1942         /* Get the full name for this cell */
1943         code = cm_SearchCellFile(cell, fullCell, 0, 0);
1944 #ifdef AFS_AFSDB_ENV
1945         if (code && cm_dnsEnabled)
1946             code = cm_SearchCellByDNS(cell, fullCell, &ttl, 0, 0);
1947 #endif
1948         if (code) {
1949             cm_ReleaseSCache(dscp);
1950             return CM_ERROR_NOSUCHCELL;
1951         }
1952         
1953         StringCbPrintfA(mpInfo, sizeof(mpInfo), "%c%s:%s", *ioctlp->inDatap, fullCell, volume);
1954     } else {
1955         /* No cell name specified */
1956         StringCbCopyA(mpInfo, sizeof(mpInfo), ioctlp->inDatap);
1957     }
1958
1959 #ifdef AFS_FREELANCE_CLIENT
1960     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1961         /* we are adding the mount point to the root dir., so call
1962          * the freelance code to do the add. */
1963         osi_Log0(afsd_logp,"IoctlCreateMountPoint within Freelance root dir");
1964         code = cm_FreelanceAddMount(leaf, fullCell, volume, 
1965                                     *ioctlp->inDatap == '%', NULL);
1966         cm_ReleaseSCache(dscp);
1967         return code;
1968     }
1969 #endif
1970     /* create the symlink with mode 644.  The lack of X bits tells
1971      * us that it is a mount point.
1972      */
1973     tattr.mask = CM_ATTRMASK_UNIXMODEBITS | CM_ATTRMASK_CLIENTMODTIME;
1974     tattr.unixModeBits = 0644;
1975     tattr.clientModTime = time(NULL);
1976
1977     code = cm_SymLink(dscp, leaf, mpInfo, 0, &tattr, userp, &req);
1978     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1979         smb_NotifyChange(FILE_ACTION_ADDED,
1980                          FILE_NOTIFY_CHANGE_DIR_NAME,
1981                          dscp, leaf, NULL, TRUE);
1982
1983     cm_ReleaseSCache(dscp);
1984     return code;
1985 }
1986
1987 long cm_IoctlSymlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
1988 {
1989     char leaf[LEAF_SIZE];
1990     long code;
1991     cm_scache_t *dscp;
1992     cm_attr_t tattr;
1993     char *cp;
1994     cm_req_t req;
1995
1996     cm_InitReq(&req);
1997
1998     code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
1999     if (code) return code;
2000
2001     /* Translate chars for the link name */
2002     TranslateExtendedChars(leaf);
2003
2004     /* Translate chars for the linked to name */
2005     TranslateExtendedChars(ioctlp->inDatap);
2006
2007     cp = ioctlp->inDatap;               /* contents of link */
2008
2009 #ifdef AFS_FREELANCE_CLIENT
2010     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
2011         /* we are adding the symlink to the root dir., so call
2012          * the freelance code to do the add. */
2013         if (cp[0] == cp[1] && cp[1] == '\\' && 
2014             !_strnicmp(cm_NetbiosName,cp+2,strlen(cm_NetbiosName))) 
2015         {
2016             /* skip \\AFS\ or \\AFS\all\ */
2017             char * p;
2018             p = cp + 2 + strlen(cm_NetbiosName) + 1;
2019             if ( !_strnicmp("all", p, 3) )
2020                 p += 4;
2021             cp = p;
2022         }
2023         osi_Log0(afsd_logp,"IoctlCreateSymlink within Freelance root dir");
2024         code = cm_FreelanceAddSymlink(leaf, cp, NULL);
2025         cm_ReleaseSCache(dscp);
2026         return code;
2027     }
2028 #endif
2029
2030     /* Create symlink with mode 0755. */
2031     tattr.mask = CM_ATTRMASK_UNIXMODEBITS;
2032     tattr.unixModeBits = 0755;
2033
2034     code = cm_SymLink(dscp, leaf, cp, 0, &tattr, userp, &req);
2035     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
2036         smb_NotifyChange(FILE_ACTION_ADDED,
2037                           FILE_NOTIFY_CHANGE_FILE_NAME
2038                           | FILE_NOTIFY_CHANGE_DIR_NAME,
2039                           dscp, leaf, NULL, TRUE);
2040
2041     cm_ReleaseSCache(dscp);
2042
2043     return code;
2044 }
2045
2046
2047 long cm_IoctlListlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
2048 {
2049     long code;
2050     cm_scache_t *dscp;
2051     cm_scache_t *scp;
2052     char *cp;
2053     cm_space_t *spacep;
2054     cm_scache_t *newRootScp;
2055     cm_req_t req;
2056
2057     cm_InitReq(&req);
2058
2059     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp, 0);
2060     if (code) return code;
2061
2062     cp = ioctlp->inDatap;
2063
2064     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
2065     cm_ReleaseSCache(dscp);
2066     if (code) return code;
2067
2068     /* Check that it's a real symlink */
2069     if (scp->fileType != CM_SCACHETYPE_SYMLINK &&
2070         scp->fileType != CM_SCACHETYPE_DFSLINK &&
2071         scp->fileType != CM_SCACHETYPE_INVALID) {
2072         cm_ReleaseSCache(scp);
2073         return CM_ERROR_INVAL;
2074     }
2075
2076     code = cm_AssembleLink(scp, "", &newRootScp, &spacep, userp, &req);
2077     cm_ReleaseSCache(scp);
2078     if (code == 0) {
2079         cp = ioctlp->outDatap;
2080         if (newRootScp != NULL) {
2081             StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), cm_mountRoot);
2082             StringCbCatA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), "/");
2083             cp += strlen(cp);
2084         }
2085         StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), spacep->data);
2086         cp += strlen(cp) + 1;
2087         ioctlp->outDatap = cp;
2088         cm_FreeSpace(spacep);
2089         if (newRootScp != NULL)
2090             cm_ReleaseSCache(newRootScp);
2091         code = 0;
2092     } else if (code == CM_ERROR_PATH_NOT_COVERED && 
2093                 scp->fileType == CM_SCACHETYPE_DFSLINK ||
2094                code == CM_ERROR_NOSUCHPATH &&
2095                 scp->fileType == CM_SCACHETYPE_INVALID) {
2096
2097         cp = ioctlp->outDatap;
2098         StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), scp->mountPointStringp);
2099         cp += strlen(cp) + 1;
2100         ioctlp->outDatap = cp;
2101         code = 0;
2102     }
2103
2104     return code;
2105 }
2106
2107 long cm_IoctlIslink(struct smb_ioctl *ioctlp, struct cm_user *userp)
2108 {/*CHECK FOR VALID SYMLINK*/
2109     long code;
2110     cm_scache_t *dscp;
2111     cm_scache_t *scp;
2112     char *cp;
2113     cm_req_t req;
2114
2115     cm_InitReq(&req);
2116
2117     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp, 0);
2118     if (code) return code;
2119
2120     cp = ioctlp->inDatap;
2121     osi_LogEvent("cm_IoctlListlink",NULL," name[%s]",cp);
2122
2123     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
2124     cm_ReleaseSCache(dscp);
2125     if (code) return code;
2126
2127     /* Check that it's a real symlink */
2128     if (scp->fileType != CM_SCACHETYPE_SYMLINK &&
2129         scp->fileType != CM_SCACHETYPE_DFSLINK &&
2130         scp->fileType != CM_SCACHETYPE_INVALID)
2131         code = CM_ERROR_INVAL;
2132     cm_ReleaseSCache(scp);
2133     return code;
2134 }
2135
2136 long cm_IoctlDeletelink(struct smb_ioctl *ioctlp, struct cm_user *userp)
2137 {
2138     long code;
2139     cm_scache_t *dscp;
2140     cm_scache_t *scp;
2141     char *cp;
2142     cm_req_t req;
2143
2144     cm_InitReq(&req);
2145
2146     code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp, 0);
2147     if (code) return code;
2148
2149     cp = ioctlp->inDatap;
2150
2151 #ifdef AFS_FREELANCE_CLIENT
2152     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
2153         /* we are adding the mount point to the root dir., so call
2154          * the freelance code to do the add. */
2155         osi_Log0(afsd_logp,"IoctlDeletelink from Freelance root dir");
2156         code = cm_FreelanceRemoveSymlink(cp);
2157         cm_ReleaseSCache(dscp);
2158         return code;
2159     }
2160 #endif
2161
2162     code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
2163         
2164     /* if something went wrong, bail out now */
2165     if (code)
2166         goto done3;
2167         
2168     lock_ObtainMutex(&scp->mx);
2169     code = cm_SyncOp(scp, NULL, userp, &req, 0,
2170                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2171     if (code)
2172         goto done2;
2173         
2174     /* now check that this is a real symlink */
2175     if (scp->fileType != CM_SCACHETYPE_SYMLINK &&
2176         scp->fileType != CM_SCACHETYPE_DFSLINK &&
2177         scp->fileType != CM_SCACHETYPE_INVALID) {
2178         code = CM_ERROR_INVAL;
2179         goto done1;
2180     }
2181         
2182     /* time to make the RPC, so drop the lock */
2183     lock_ReleaseMutex(&scp->mx);
2184         
2185     /* easier to do it this way */
2186     code = cm_Unlink(dscp, cp, userp, &req);
2187     if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
2188         smb_NotifyChange(FILE_ACTION_REMOVED,
2189                           FILE_NOTIFY_CHANGE_FILE_NAME
2190                           | FILE_NOTIFY_CHANGE_DIR_NAME,
2191                           dscp, cp, NULL, TRUE);
2192
2193     lock_ObtainMutex(&scp->mx);
2194   done1:
2195     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2196
2197   done2:
2198     lock_ReleaseMutex(&scp->mx);
2199     cm_ReleaseSCache(scp);
2200
2201   done3:
2202     cm_ReleaseSCache(dscp);
2203     return code;
2204 }
2205
2206 #ifdef QUERY_AFSID
2207 long cm_UsernameToId(char *uname, cm_ucell_t * ucellp, afs_uint32* uid)
2208 {
2209     afs_int32 code;
2210     namelist lnames;
2211     idlist lids;
2212     static struct afsconf_cell info;
2213     struct rx_connection *serverconns[MAXSERVERS];
2214     struct rx_securityClass *sc[3];
2215     afs_int32 scIndex = 2;      /* authenticated - we have a token */
2216     struct ubik_client *pruclient = NULL;
2217     struct afsconf_dir *tdir;
2218     int i;
2219     char * p, * r;
2220
2221     tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
2222     code = afsconf_GetCellInfo(tdir, ucellp->cellp->name, "afsprot", &info);
2223     afsconf_Close(tdir);
2224
2225     sc[0] = 0;
2226     sc[1] = 0;
2227     sc[2] = 0;
2228
2229     /* we have the token that was given to us in the settoken 
2230      * call.   we just have to use it. 
2231      */
2232     scIndex = 2;        /* kerberos ticket */
2233     sc[2] = rxkad_NewClientSecurityObject(rxkad_clear, &ucellp->sessionKey,
2234                                           ucellp->kvno, ucellp->ticketLen,
2235                                           ucellp->ticketp);
2236
2237     memset(serverconns, 0, sizeof(serverconns));        /* terminate list!!! */
2238     for (i = 0; i < info.numServers; i++)
2239         serverconns[i] =
2240             rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
2241                              info.hostAddr[i].sin_port, PRSRV, sc[scIndex],
2242                              scIndex);
2243
2244     code = ubik_ClientInit(serverconns, &pruclient);
2245     if (code) {
2246         return code;
2247     }
2248
2249     code = rxs_Release(sc[scIndex]);
2250
2251     lids.idlist_len = 0;
2252     lids.idlist_val = 0;
2253     lnames.namelist_len = 1;
2254     lnames.namelist_val = (prname *) malloc(PR_MAXNAMELEN);
2255     strncpy(lnames.namelist_val[0], uname, PR_MAXNAMELEN);
2256     lnames.namelist_val[0][PR_MAXNAMELEN-1] = '\0';
2257     for ( p=lnames.namelist_val[0], r=NULL; *p; p++ ) {
2258         if (isupper(*p))
2259             *p = tolower(*p);
2260         if (*p == '@')
2261             r = p;
2262     }
2263     if (r && !stricmp(r+1,ucellp->cellp->name))
2264         *r = '\0';
2265
2266     code = ubik_PR_NameToID(pruclient, 0, &lnames, &lids);
2267     if (lids.idlist_val) {
2268         *uid = *lids.idlist_val;
2269         free(lids.idlist_val);
2270     }
2271     if (lnames.namelist_val)
2272         free(lnames.namelist_val);
2273
2274     if ( pruclient ) {
2275         ubik_ClientDestroy(pruclient);
2276         pruclient = NULL;
2277     }
2278
2279     return 0;
2280 }
2281 #endif /* QUERY_AFSID */
2282
2283 long cm_IoctlSetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
2284 {
2285     char *saveDataPtr;
2286     char *tp;
2287     int ticketLen;
2288     char *ticket;
2289     int ctSize;
2290     struct ClearToken ct;
2291     cm_cell_t *cellp;
2292     cm_ucell_t *ucellp;
2293     char *uname = NULL;
2294     afs_uuid_t uuid;
2295     int flags;
2296     char sessionKey[8];
2297 #ifndef AFSIFS
2298     char *smbname;
2299 #endif
2300     int release_userp = 0;
2301     char * wdir = NULL;
2302
2303     saveDataPtr = ioctlp->inDatap;
2304
2305     cm_SkipIoctlPath(ioctlp);
2306
2307     tp = ioctlp->inDatap;
2308
2309     /* ticket length */
2310     memcpy(&ticketLen, tp, sizeof(ticketLen));
2311     tp += sizeof(ticketLen);
2312     if (ticketLen < MINKTCTICKETLEN || ticketLen > MAXKTCTICKETLEN)
2313         return CM_ERROR_INVAL;
2314
2315     /* remember ticket and skip over it for now */
2316     ticket = tp;
2317     tp += ticketLen;
2318
2319     /* clear token size */
2320     memcpy(&ctSize, tp, sizeof(ctSize));
2321     tp += sizeof(ctSize);
2322     if (ctSize != sizeof(struct ClearToken))
2323         return CM_ERROR_INVAL;
2324
2325     /* clear token */
2326     memcpy(&ct, tp, ctSize);
2327     tp += ctSize;
2328     if (ct.AuthHandle == -1)
2329         ct.AuthHandle = 999;    /* more rxvab compat stuff */
2330
2331     /* more stuff, if any */
2332     if (ioctlp->inCopied > tp - saveDataPtr) {
2333         /* flags:  logon flag */
2334         memcpy(&flags, tp, sizeof(int));
2335         tp += sizeof(int);
2336
2337         /* cell name */
2338         cellp = cm_GetCell(tp, CM_FLAG_CREATE | CM_FLAG_NOPROBE);
2339         if (!cellp) 
2340             return CM_ERROR_NOSUCHCELL;
2341         tp += strlen(tp) + 1;
2342
2343         /* user name */
2344         uname = tp;
2345         tp += strlen(tp) + 1;
2346
2347 #ifndef AFSIFS  /* no SMB username, so we cannot logon based on this */
2348         if (flags & PIOCTL_LOGON) {
2349             /* SMB user name with which to associate tokens */
2350             smbname = tp;
2351             osi_Log2(smb_logp,"cm_IoctlSetToken for user [%s] smbname [%s]",
2352                      osi_LogSaveString(smb_logp,uname), osi_LogSaveString(smb_logp,smbname));
2353             fprintf(stderr, "SMB name = %s\n", smbname);
2354             tp += strlen(tp) + 1;
2355         } else {
2356             osi_Log1(smb_logp,"cm_IoctlSetToken for user [%s]",
2357                      osi_LogSaveString(smb_logp, uname));
2358         }
2359 #endif
2360
2361                 /* uuid */
2362         memcpy(&uuid, tp, sizeof(uuid));
2363         if (!cm_FindTokenEvent(uuid, sessionKey))
2364             return CM_ERROR_INVAL;
2365     } else {
2366         cellp = cm_data.rootCellp;
2367         osi_Log0(smb_logp,"cm_IoctlSetToken - no name specified");
2368     }
2369
2370 #ifndef AFSIFS
2371     if (flags & PIOCTL_LOGON) {
2372         userp = smb_FindCMUserByName(smbname, ioctlp->fidp->vcp->rname,
2373                                      SMB_FLAG_CREATE|SMB_FLAG_AFSLOGON);
2374         release_userp = 1;
2375     }
2376 #endif /* AFSIFS */
2377
2378     /* store the token */
2379     lock_ObtainMutex(&userp->mx);
2380     ucellp = cm_GetUCell(userp, cellp);
2381     osi_Log1(smb_logp,"cm_IoctlSetToken ucellp %lx", ucellp);
2382     ucellp->ticketLen = ticketLen;
2383     if (ucellp->ticketp)
2384         free(ucellp->ticketp);  /* Discard old token if any */
2385     ucellp->ticketp = malloc(ticketLen);
2386     memcpy(ucellp->ticketp, ticket, ticketLen);
2387     /*
2388      * Get the session key from the RPC, rather than from the pioctl.
2389      */
2390     /*
2391     memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
2392     */
2393     memcpy(ucellp->sessionKey.data, sessionKey, sizeof(sessionKey));
2394     ucellp->kvno = ct.AuthHandle;
2395     ucellp->expirationTime = ct.EndTimestamp;
2396     ucellp->gen++;
2397 #ifdef QUERY_AFSID
2398     ucellp->uid = ANONYMOUSID;
2399 #endif
2400     if (uname) {
2401         StringCbCopyA(ucellp->userName, MAXKTCNAMELEN, uname);
2402 #ifdef QUERY_AFSID
2403         cm_UsernameToId(uname, ucellp, &ucellp->uid);
2404 #endif
2405     }
2406     ucellp->flags |= CM_UCELLFLAG_RXKAD;
2407     lock_ReleaseMutex(&userp->mx);
2408
2409     if (flags & PIOCTL_LOGON) {
2410         ioctlp->flags |= SMB_IOCTLFLAG_LOGON;
2411     }
2412
2413     cm_ResetACLCache(userp);
2414
2415     if (release_userp)
2416         cm_ReleaseUser(userp);
2417
2418     return 0;
2419 }
2420
2421 long cm_IoctlGetTokenIter(struct smb_ioctl *ioctlp, struct cm_user *userp)
2422 {
2423     char *tp, *cp;
2424     int iterator;
2425     int temp;
2426     cm_ucell_t *ucellp;
2427     struct ClearToken ct;
2428
2429     cm_SkipIoctlPath(ioctlp);
2430
2431     tp = ioctlp->inDatap;
2432     cp = ioctlp->outDatap;
2433
2434     /* iterator */
2435     memcpy(&iterator, tp, sizeof(iterator));
2436     tp += sizeof(iterator);
2437
2438     lock_ObtainMutex(&userp->mx);
2439
2440     /* look for token */
2441     for (;;iterator++) {
2442         ucellp = cm_FindUCell(userp, iterator);
2443         if (!ucellp) {
2444             lock_ReleaseMutex(&userp->mx);
2445             return CM_ERROR_NOMORETOKENS;
2446         }
2447         if (ucellp->flags & CM_UCELLFLAG_RXKAD)
2448             break;
2449     }       
2450
2451     /* new iterator */
2452     temp = ucellp->iterator + 1;
2453     memcpy(cp, &temp, sizeof(temp));
2454     cp += sizeof(temp);
2455
2456     /* ticket length */
2457     memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
2458     cp += sizeof(ucellp->ticketLen);
2459
2460     /* ticket */
2461     memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
2462     cp += ucellp->ticketLen;
2463
2464     /* clear token size */
2465     temp = sizeof(ct);
2466     memcpy(cp, &temp, sizeof(temp));
2467     cp += sizeof(temp);
2468
2469     /* clear token */
2470     ct.AuthHandle = ucellp->kvno;
2471     /*
2472      * Don't give out a real session key here
2473      */
2474     /*
2475     memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
2476     */
2477     memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
2478     ct.ViceId = 37;                     /* XXX */
2479     ct.BeginTimestamp = 0;              /* XXX */
2480     ct.EndTimestamp = ucellp->expirationTime;
2481     memcpy(cp, &ct, sizeof(ct));
2482     cp += sizeof(ct);
2483
2484     /* Primary flag (unused) */
2485     temp = 0;
2486     memcpy(cp, &temp, sizeof(temp));
2487     cp += sizeof(temp);
2488
2489     /* cell name */
2490     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), ucellp->cellp->name);
2491     cp += strlen(cp) + 1;
2492
2493     /* user name */
2494     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), ucellp->userName);
2495     cp += strlen(cp) + 1;
2496
2497     ioctlp->outDatap = cp;
2498
2499     lock_ReleaseMutex(&userp->mx);
2500
2501     return 0;
2502 }
2503
2504 long cm_IoctlGetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
2505 {
2506     char *cp;
2507     int temp;
2508     cm_cell_t *cellp;
2509     cm_ucell_t *ucellp;
2510     struct ClearToken ct;
2511     char *tp;
2512     afs_uuid_t uuid;
2513     cm_SkipIoctlPath(ioctlp);
2514
2515     tp = ioctlp->inDatap;
2516
2517     cp = ioctlp->outDatap;
2518
2519     /* cell name is right here */
2520     cellp = cm_GetCell(tp, 0);
2521     if (!cellp) 
2522         return CM_ERROR_NOSUCHCELL;
2523     tp += strlen(tp) + 1;
2524
2525     /* uuid */
2526     memcpy(&uuid, tp, sizeof(uuid));
2527
2528     lock_ObtainMutex(&userp->mx);
2529
2530     ucellp = cm_GetUCell(userp, cellp);
2531     if (!ucellp || !(ucellp->flags & CM_UCELLFLAG_RXKAD)) {
2532         lock_ReleaseMutex(&userp->mx);
2533         return CM_ERROR_NOMORETOKENS;
2534     }
2535
2536     /* ticket length */
2537     memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
2538     cp += sizeof(ucellp->ticketLen);
2539
2540     /* ticket */
2541     memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
2542     cp += ucellp->ticketLen;
2543
2544     /* clear token size */
2545     temp = sizeof(ct);
2546     memcpy(cp, &temp, sizeof(temp));
2547     cp += sizeof(temp);
2548
2549     /* clear token */
2550     ct.AuthHandle = ucellp->kvno;
2551     /*
2552      * Don't give out a real session key here
2553      */
2554     /*
2555     memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
2556     */
2557     memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
2558     ct.ViceId = 37;                     /* XXX */
2559     ct.BeginTimestamp = 0;              /* XXX */
2560     ct.EndTimestamp = ucellp->expirationTime;
2561     memcpy(cp, &ct, sizeof(ct));
2562     cp += sizeof(ct);
2563
2564     /* Primary flag (unused) */
2565     temp = 0;
2566     memcpy(cp, &temp, sizeof(temp));
2567     cp += sizeof(temp);
2568
2569     /* cell name */
2570     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), ucellp->cellp->name);
2571     cp += strlen(cp) + 1;
2572
2573     /* user name */
2574     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), ucellp->userName);
2575     cp += strlen(cp) + 1;
2576
2577     ioctlp->outDatap = cp;
2578
2579     lock_ReleaseMutex(&userp->mx);
2580
2581     cm_RegisterNewTokenEvent(uuid, ucellp->sessionKey.data);
2582
2583     return 0;
2584 }
2585
2586 long cm_IoctlDelToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
2587 {
2588     char *cp;
2589     cm_cell_t *cellp;
2590     cm_ucell_t *ucellp;
2591
2592     cm_SkipIoctlPath(ioctlp);
2593
2594     cp = ioctlp->outDatap;
2595
2596     /* cell name is right here */
2597     cellp = cm_GetCell(ioctlp->inDatap, 0);
2598     if (!cellp) 
2599         return CM_ERROR_NOSUCHCELL;
2600
2601     lock_ObtainMutex(&userp->mx);
2602
2603     ucellp = cm_GetUCell(userp, cellp);
2604     if (!ucellp) {
2605         lock_ReleaseMutex(&userp->mx);
2606         return CM_ERROR_NOMORETOKENS;
2607     }
2608
2609     osi_Log1(smb_logp,"cm_IoctlDelToken ucellp %lx", ucellp);
2610
2611     if (ucellp->ticketp) {
2612         free(ucellp->ticketp);
2613         ucellp->ticketp = NULL;
2614     }
2615     ucellp->ticketLen = 0;
2616     memset(ucellp->sessionKey.data, 0, 8);
2617     ucellp->kvno = 0;
2618     ucellp->expirationTime = 0;
2619     ucellp->userName[0] = '\0';
2620     ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
2621     ucellp->gen++;
2622
2623     lock_ReleaseMutex(&userp->mx);
2624
2625     cm_ResetACLCache(userp);
2626
2627     return 0;
2628 }
2629
2630 long cm_IoctlDelAllToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
2631 {
2632     cm_ucell_t *ucellp;
2633
2634     lock_ObtainMutex(&userp->mx);
2635
2636     for (ucellp = userp->cellInfop; ucellp; ucellp = ucellp->nextp) {
2637         osi_Log1(smb_logp,"cm_IoctlDelAllToken ucellp %lx", ucellp);
2638
2639         if (ucellp->ticketp) {
2640             free(ucellp->ticketp);
2641             ucellp->ticketp = NULL;
2642         }
2643         ucellp->ticketLen = 0;
2644         memset(ucellp->sessionKey.data, 0, 8);
2645         ucellp->kvno = 0;
2646         ucellp->expirationTime = 0;
2647         ucellp->userName[0] = '\0';
2648         ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
2649         ucellp->gen++;
2650     }
2651
2652     lock_ReleaseMutex(&userp->mx);
2653
2654     cm_ResetACLCache(userp);
2655
2656     return 0;
2657 }
2658
2659 long cm_IoctlMakeSubmount(smb_ioctl_t *ioctlp, cm_user_t *userp)
2660 {
2661     char afspath[MAX_PATH];
2662     char *submountreqp;
2663     int nextAutoSubmount;
2664     HKEY hkSubmounts;
2665     DWORD dwType, dwSize;
2666     DWORD status;
2667     DWORD dwIndex;
2668     DWORD dwSubmounts;
2669
2670     cm_SkipIoctlPath(ioctlp);
2671
2672     /* Serialize this one, to prevent simultaneous mods
2673      * to afsdsbmt.ini
2674      */
2675     lock_ObtainMutex(&cm_Afsdsbmt_Lock);
2676
2677     /* Parse the input parameters--first the required afs path,
2678      * then the requested submount name (which may be "").
2679      */
2680     cm_NormalizeAfsPath (afspath, sizeof(afspath), ioctlp->inDatap);
2681     submountreqp = ioctlp->inDatap + (strlen(ioctlp->inDatap)+1);
2682
2683     /* If the caller supplied a suggested submount name, see if
2684      * that submount name is in use... if so, the submount's path
2685      * has to match our path.
2686      */
2687
2688     RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
2689                     AFSREG_CLT_OPENAFS_SUBKEY "\\Submounts",
2690                     0, 
2691                     "AFS", 
2692                     REG_OPTION_NON_VOLATILE,
2693                     KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
2694                     NULL, 
2695                     &hkSubmounts,
2696                     NULL );
2697
2698     if (submountreqp && *submountreqp) {
2699         char submountPathNormalized[MAX_PATH];
2700         char submountPath[MAX_PATH];
2701
2702         dwSize = sizeof(submountPath);
2703         status = RegQueryValueEx( hkSubmounts, submountreqp, 0,
2704                                   &dwType, submountPath, &dwSize);
2705
2706         if (status != ERROR_SUCCESS) {
2707
2708             /* The suggested submount name isn't in use now--
2709              * so we can safely map the requested submount name
2710              * to the supplied path. Remember not to write the
2711              * leading "/afs" when writing out the submount.
2712              */
2713             RegSetValueEx( hkSubmounts, submountreqp, 0,
2714                            REG_EXPAND_SZ, 
2715                            (strlen(&afspath[strlen(cm_mountRoot)])) ?
2716                            &afspath[strlen(cm_mountRoot)]:"/",
2717                            (strlen(&afspath[strlen(cm_mountRoot)])) ?
2718                            (DWORD)strlen(&afspath[strlen(cm_mountRoot)])+1:2);
2719
2720             RegCloseKey( hkSubmounts );
2721             StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), submountreqp);
2722             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
2723             lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
2724             return 0;
2725         }
2726
2727         /* The suggested submount name is already in use--if the
2728          * supplied path matches the submount's path, we can still
2729          * use the suggested submount name.
2730          */
2731         cm_NormalizeAfsPath (submountPathNormalized, sizeof(submountPathNormalized), submountPath);
2732         if (!strcmp (submountPathNormalized, afspath)) {
2733             StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), submountreqp);
2734             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
2735             RegCloseKey( hkSubmounts );
2736             lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
2737             return 0;
2738         }
2739     }
2740
2741     RegQueryInfoKey( hkSubmounts,
2742                      NULL,  /* lpClass */
2743                      NULL,  /* lpcClass */
2744                      NULL,  /* lpReserved */
2745                      NULL,  /* lpcSubKeys */
2746                      NULL,  /* lpcMaxSubKeyLen */
2747                      NULL,  /* lpcMaxClassLen */
2748                      &dwSubmounts, /* lpcValues */
2749                      NULL,  /* lpcMaxValueNameLen */
2750                      NULL,  /* lpcMaxValueLen */
2751                      NULL,  /* lpcbSecurityDescriptor */
2752                      NULL   /* lpftLastWriteTime */
2753                      );
2754
2755
2756     /* Having obtained a list of all available submounts, start
2757      * searching that list for a path which matches the requested
2758      * AFS path. We'll also keep track of the highest "auto15"/"auto47"
2759      * submount, in case we need to add a new one later.
2760      */
2761
2762     nextAutoSubmount = 1;
2763
2764     for ( dwIndex = 0; dwIndex < dwSubmounts; dwIndex ++ ) {
2765         char submountPathNormalized[MAX_PATH];
2766         char submountPath[MAX_PATH] = "";
2767         DWORD submountPathLen = sizeof(submountPath);
2768         char submountName[MAX_PATH];
2769         DWORD submountNameLen = sizeof(submountName);
2770
2771         dwType = 0;
2772         RegEnumValue( hkSubmounts, dwIndex, submountName, &submountNameLen, NULL,
2773                       &dwType, submountPath, &submountPathLen);
2774         if (dwType == REG_EXPAND_SZ) {
2775             char buf[MAX_PATH];
2776             StringCbCopyA(buf, MAX_PATH, submountPath);
2777             submountPathLen = ExpandEnvironmentStrings(buf, submountPath, MAX_PATH);
2778             if (submountPathLen > MAX_PATH)
2779                 continue;
2780         }
2781
2782         /* If this is an Auto### submount, remember its ### value */
2783         if ((!strnicmp (submountName, "auto", 4)) &&
2784              (isdigit (submountName[strlen("auto")]))) {
2785             int thisAutoSubmount;
2786             thisAutoSubmount = atoi (&submountName[strlen("auto")]);
2787             nextAutoSubmount = max (nextAutoSubmount,
2788                                      thisAutoSubmount+1);
2789         }       
2790
2791         if ((submountPathLen == 0) ||
2792              (submountPathLen == sizeof(submountPath) - 1)) {
2793             continue;
2794         }
2795
2796         /* See if the path for this submount matches the path
2797          * that our caller specified. If so, we can return
2798          * this submount.
2799          */
2800         cm_NormalizeAfsPath (submountPathNormalized, sizeof(submountPathNormalized), submountPath);
2801         if (!strcmp (submountPathNormalized, afspath)) {
2802             StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), submountName);
2803             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
2804             RegCloseKey(hkSubmounts);
2805             lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
2806             return 0;
2807
2808         }
2809     }
2810
2811     /* We've been through the entire list of existing submounts, and
2812      * didn't find any which matched the specified path. So, we'll
2813      * just have to add one. Remember not to write the leading "/afs"
2814      * when writing out the submount.
2815      */
2816
2817     StringCbPrintfA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), "auto%ld", nextAutoSubmount);
2818
2819     RegSetValueEx( hkSubmounts, 
2820                    ioctlp->outDatap,
2821                    0,
2822                    REG_EXPAND_SZ, 
2823                    (strlen(&afspath[strlen(cm_mountRoot)])) ?
2824                    &afspath[strlen(cm_mountRoot)]:"/",
2825                    (strlen(&afspath[strlen(cm_mountRoot)])) ?
2826                    (DWORD)strlen(&afspath[strlen(cm_mountRoot)])+1:2);
2827
2828     ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
2829     RegCloseKey(hkSubmounts);
2830     lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
2831     return 0;
2832 }
2833
2834 long cm_IoctlGetRxkcrypt(smb_ioctl_t *ioctlp, cm_user_t *userp)
2835 {
2836     memcpy(ioctlp->outDatap, &cryptall, sizeof(cryptall));
2837     ioctlp->outDatap += sizeof(cryptall);
2838
2839     return 0;
2840 }
2841
2842 long cm_IoctlSetRxkcrypt(smb_ioctl_t *ioctlp, cm_user_t *userp)
2843 {
2844     afs_int32 c = cryptall;
2845
2846     cm_SkipIoctlPath(ioctlp);
2847
2848     memcpy(&cryptall, ioctlp->inDatap, sizeof(cryptall));
2849
2850     if (c != cryptall) {
2851         if (cryptall)
2852             LogEvent(EVENTLOG_INFORMATION_TYPE, MSG_CRYPT_ON);
2853         else
2854             LogEvent(EVENTLOG_INFORMATION_TYPE, MSG_CRYPT_OFF);
2855     }
2856     return 0;
2857 }
2858
2859 long cm_IoctlRxStatProcess(struct smb_ioctl *ioctlp, struct cm_user *userp)
2860 {
2861     afs_int32 flags;
2862     int code = 0;
2863
2864     cm_SkipIoctlPath(ioctlp);
2865
2866     memcpy((char *)&flags, ioctlp->inDatap, sizeof(afs_int32));
2867     if (!(flags & AFSCALL_RXSTATS_MASK) || (flags & ~AFSCALL_RXSTATS_MASK)) {
2868         return -1;
2869     }
2870     if (flags & AFSCALL_RXSTATS_ENABLE) {
2871         rx_enableProcessRPCStats();
2872     }
2873     if (flags & AFSCALL_RXSTATS_DISABLE) {
2874         rx_disableProcessRPCStats();
2875     }
2876     if (flags & AFSCALL_RXSTATS_CLEAR) {
2877         rx_clearProcessRPCStats(AFS_RX_STATS_CLEAR_ALL);
2878     }
2879     return 0;
2880 }
2881
2882 long cm_IoctlRxStatPeer(struct smb_ioctl *ioctlp, struct cm_user *userp)
2883 {
2884     afs_int32 flags;
2885     int code = 0;
2886
2887     cm_SkipIoctlPath(ioctlp);
2888
2889     memcpy((char *)&flags, ioctlp->inDatap, sizeof(afs_int32));
2890     if (!(flags & AFSCALL_RXSTATS_MASK) || (flags & ~AFSCALL_RXSTATS_MASK)) {
2891         return -1;
2892     }
2893     if (flags & AFSCALL_RXSTATS_ENABLE) {
2894         rx_enablePeerRPCStats();
2895     }
2896     if (flags & AFSCALL_RXSTATS_DISABLE) {
2897         rx_disablePeerRPCStats();
2898     }
2899     if (flags & AFSCALL_RXSTATS_CLEAR) {
2900         rx_clearPeerRPCStats(AFS_RX_STATS_CLEAR_ALL);
2901     }
2902     return 0;
2903 }
2904
2905 long cm_IoctlGetSMBName(smb_ioctl_t *ioctlp, cm_user_t *userp)
2906 {
2907   smb_user_t *uidp = ioctlp->uidp;
2908
2909   if (uidp && uidp->unp) {
2910     memcpy(ioctlp->outDatap, uidp->unp->name, strlen(uidp->unp->name));
2911     ioctlp->outDatap += strlen(uidp->unp->name);
2912   }
2913
2914   return 0;
2915 }
2916
2917 long cm_IoctlUUIDControl(struct smb_ioctl * ioctlp, struct cm_user *userp)
2918 {
2919     long cmd;
2920     afsUUID uuid;
2921
2922     memcpy(&cmd, ioctlp->inDatap, sizeof(long));
2923
2924     if (cmd) {             /* generate a new UUID */
2925         UuidCreate((UUID *) &uuid);
2926         cm_data.Uuid = uuid;
2927         cm_ForceNewConnectionsAllServers();
2928     }
2929
2930     memcpy(ioctlp->outDatap, &cm_data.Uuid, sizeof(cm_data.Uuid));
2931     ioctlp->outDatap += sizeof(cm_data.Uuid);
2932
2933     return 0;
2934 }
2935
2936 /* 
2937  * functions to dump contents of various structures. 
2938  * In debug build (linked with crt debug library) will dump allocated but not freed memory
2939  */
2940 extern int cm_DumpSCache(FILE *outputFile, char *cookie, int lock);
2941 extern int cm_DumpBufHashTable(FILE *outputFile, char *cookie, int lock);
2942 extern int smb_DumpVCP(FILE *outputFile, char *cookie, int lock);
2943
2944 long cm_IoctlMemoryDump(struct smb_ioctl *ioctlp, struct cm_user *userp)
2945 {
2946     long inValue = 0;
2947     HANDLE hLogFile;
2948     char logfileName[MAX_PATH+1];
2949     char *cookie;
2950     DWORD dwSize;
2951   
2952 #ifdef _DEBUG  
2953     static _CrtMemState memstate;
2954 #endif
2955   
2956     cm_SkipIoctlPath(ioctlp);
2957     memcpy(&inValue, ioctlp->inDatap, sizeof(long));
2958   
2959     dwSize = GetEnvironmentVariable("TEMP", logfileName, sizeof(logfileName));
2960     if ( dwSize == 0 || dwSize > sizeof(logfileName) )
2961     {
2962         GetWindowsDirectory(logfileName, sizeof(logfileName));
2963     }
2964     strncat(logfileName, "\\afsd_alloc.log", sizeof(logfileName));
2965
2966     hLogFile = CreateFile(logfileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2967   
2968     if (!hLogFile)
2969     {
2970       /* error */
2971       inValue = -1;
2972       memcpy(ioctlp->outDatap, &inValue, sizeof(long));
2973       ioctlp->outDatap += sizeof(long);
2974       
2975       return 0;               
2976     }
2977   
2978     SetFilePointer(hLogFile, 0, NULL, FILE_END);
2979   
2980     cookie = inValue ? "b" : "e";
2981   
2982 #ifdef _DEBUG  
2983   
2984     if (inValue)
2985     {
2986       _CrtMemCheckpoint(&memstate);           
2987     }
2988     else
2989     {
2990         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2991         _CrtSetReportFile(_CRT_WARN, hLogFile);
2992         _CrtMemDumpAllObjectsSince(&memstate);
2993     }
2994 #endif
2995   
2996     /* dump all interesting data */
2997     cm_MemDumpDirStats(hLogFile, cookie, 1);
2998     cm_MemDumpBPlusStats(hLogFile, cookie, 1);
2999     cm_DumpCells(hLogFile, cookie, 1);
3000     cm_DumpVolumes(hLogFile, cookie, 1);
3001     cm_DumpSCache(hLogFile, cookie, 1);
3002     cm_DumpBufHashTable(hLogFile, cookie, 1);
3003     smb_DumpVCP(hLogFile, cookie, 1);
3004
3005     CloseHandle(hLogFile);                          
3006   
3007     inValue = 0;        /* success */
3008     memcpy(ioctlp->outDatap, &inValue, sizeof(long));
3009     ioctlp->outDatap += sizeof(long);
3010   
3011     return 0;
3012 }
3013
3014
3015 static long 
3016 cm_CheckServersStatus(cm_serverRef_t *serversp)
3017 {
3018     long code = 0;
3019     cm_serverRef_t *tsrp;
3020     cm_server_t *tsp;
3021     int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
3022
3023     if (serversp == NULL) {
3024         osi_Log1(afsd_logp, "cm_CheckServersStatus returning 0x%x", CM_ERROR_ALLDOWN);
3025         return CM_ERROR_ALLDOWN;
3026     }
3027
3028     lock_ObtainRead(&cm_serverLock);
3029     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
3030         if (tsp = tsrp->server) {
3031             cm_GetServerNoLock(tsp);
3032             lock_ReleaseRead(&cm_serverLock);
3033             if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
3034                 allDown = 0;
3035                 if (tsrp->status == srv_busy) {
3036                     allOffline = 0;
3037                     someBusy = 1;
3038                 } else if (tsrp->status == srv_offline) {
3039                     allBusy = 0;
3040                     someOffline = 1;
3041                 } else {
3042                     allOffline = 0;
3043                     allBusy = 0;
3044                     cm_PutServer(tsp);
3045                     goto done;
3046                 }
3047             }
3048             lock_ObtainRead(&cm_serverLock);
3049             cm_PutServerNoLock(tsp);
3050         }
3051     }   
3052     lock_ReleaseRead(&cm_serverLock);
3053
3054     if (allDown) 
3055         code = CM_ERROR_ALLDOWN;
3056     else if (allBusy) 
3057         code = CM_ERROR_ALLBUSY;
3058     else if (allOffline || (someBusy && someOffline))
3059         code = CM_ERROR_ALLOFFLINE;
3060
3061   done:
3062     osi_Log1(afsd_logp, "cm_CheckServersStatus returning 0x%x", code);
3063     return code;
3064 }
3065
3066
3067 long cm_IoctlPathAvailability(struct smb_ioctl *ioctlp, struct cm_user *userp)
3068 {
3069     long code;
3070     cm_scache_t *scp;
3071     cm_cell_t *cellp;
3072     cm_volume_t *tvp;
3073     cm_vol_state_t *statep;
3074     afs_uint32 volume;
3075     cm_req_t req;
3076     cm_ioctlQueryOptions_t *optionsp;
3077     afs_uint32 flags = 0;
3078
3079     cm_InitReq(&req);
3080
3081     optionsp = cm_IoctlGetQueryOptions(ioctlp, userp);
3082     if (optionsp && CM_IOCTL_QOPTS_HAVE_LITERAL(optionsp))
3083         flags |= (optionsp->literal ? CM_PARSE_FLAG_LITERAL : 0);
3084
3085     if (optionsp && CM_IOCTL_QOPTS_HAVE_FID(optionsp)) {
3086         cm_SkipIoctlPath(ioctlp);
3087         code = cm_GetSCache(&optionsp->fid, &scp, userp, &req);
3088     } else {
3089         code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp, flags);
3090     }
3091     if (code) 
3092         return code;
3093         
3094 #ifdef AFS_FREELANCE_CLIENT
3095     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
3096         code = 0;
3097         cm_ReleaseSCache(scp);
3098     } else
3099 #endif
3100     {
3101         volume = scp->fid.volume;
3102
3103         cellp = cm_FindCellByID(scp->fid.cell, 0);
3104
3105         cm_ReleaseSCache(scp);
3106
3107         if (!cellp)
3108             return CM_ERROR_NOSUCHCELL;
3109
3110         code = cm_GetVolumeByID(cellp, volume, userp, &req, CM_GETVOL_FLAG_CREATE, &tvp);
3111         if (code) 
3112             return code;
3113         
3114         if (volume == tvp->rw.ID)
3115             statep = &tvp->rw;
3116         else if (volume == tvp->ro.ID)
3117             statep = &tvp->ro;
3118         else
3119             statep = &tvp->bk;
3120
3121         switch (statep->state) {
3122         case vl_online:
3123         case vl_unknown:
3124             code = 0;
3125             break;
3126         case vl_busy:
3127             code = CM_ERROR_ALLBUSY;
3128             break;
3129         case vl_offline:
3130             code = CM_ERROR_ALLOFFLINE;
3131             break;
3132         case vl_alldown:
3133             code = CM_ERROR_ALLDOWN;
3134             break;
3135         }
3136         cm_PutVolume(tvp);
3137     }
3138     return code;
3139 }       
3140
3141
3142 long cm_IoctlVolStatTest(struct smb_ioctl *ioctlp, struct cm_user *userp)
3143 {
3144     long code;
3145     cm_cell_t *cellp = NULL;
3146     cm_volume_t *volp;
3147     cm_vol_state_t *statep;
3148     struct VolStatTest * testp;
3149     cm_req_t req;
3150     afs_uint32 n;
3151     size_t len;
3152
3153     cm_InitReq(&req);
3154
3155     cm_SkipIoctlPath(ioctlp);   /* we don't care about the path */
3156     testp = (struct VolStatTest *)ioctlp->inDatap;
3157
3158 #ifdef AFS_FREELANCE_CLIENT
3159     if (testp->fid.cell == -1) 
3160         return CM_ERROR_NOACCESS;
3161 #endif
3162
3163     if (testp->flags & VOLSTAT_TEST_CHECK_VOLUME) {
3164         cm_CheckOfflineVolumes();
3165         return 0;
3166     }
3167
3168     if (testp->flags & VOLSTAT_TEST_NETWORK_UP) {
3169         cm_VolStatus_Network_Started(cm_NetbiosName
3170 #ifdef _WIN64
3171                                   , cm_NetbiosName
3172 #endif
3173                                   );
3174         return 0;
3175     }
3176
3177     if (testp->flags & VOLSTAT_TEST_NETWORK_DOWN) {
3178         cm_VolStatus_Network_Stopped(cm_NetbiosName
3179 #ifdef _WIN64
3180                                   , cm_NetbiosName
3181 #endif
3182                                   );
3183         return 0;
3184     }
3185
3186     if (testp->cellname[0]) {
3187         n = atoi(testp->cellname);
3188         if (n)
3189             testp->fid.cell = n;
3190         else
3191             cellp = cm_GetCell(testp->cellname, 0);
3192     }
3193
3194     if (testp->fid.cell > 0) {
3195         cellp = cm_FindCellByID(testp->fid.cell, 0);
3196     }
3197
3198     if (!cellp)
3199         return CM_ERROR_NOSUCHCELL;
3200
3201     if (testp->volname[0]) {
3202         n = atoi(testp->volname);
3203         if (n)
3204             testp->fid.volume = n;
3205         else
3206             code = cm_GetVolumeByName(cellp, testp->volname, userp, &req, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp);
3207     }
3208
3209     if (testp->fid.volume > 0)
3210         code = cm_GetVolumeByID(cellp, testp->fid.volume, userp, &req, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp);
3211
3212     if (code)
3213         return code;
3214         
3215     if (testp->fid.volume) {
3216         if (testp->fid.volume == volp->rw.ID)
3217             statep = &volp->rw;
3218         else if (testp->fid.volume == volp->ro.ID)
3219             statep = &volp->ro;
3220         else
3221             statep = &volp->bk;
3222     } else {
3223         len = strlen(testp->volname);
3224
3225         if (stricmp(".readonly", &testp->volname[len-9]) == 0)
3226             statep = &volp->ro;
3227         else if (stricmp(".backup", &testp->volname[len-7]) == 0)
3228             statep = &volp->bk;
3229         else 
3230             statep = &volp->rw;
3231     }
3232
3233     if (statep) {
3234         statep->state = testp->state;
3235         code = cm_VolStatus_Change_Notification(cellp->cellID, statep->ID, testp->state);
3236     }
3237
3238     cm_PutVolume(volp);
3239
3240     return code;
3241 }       
3242
3243