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