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