windows-reformatting-20081021
[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_SearchCellFile(cp->name, cp->name, 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, 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     tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
2161     code = afsconf_GetCellInfo(tdir, ucellp->cellp->name, "afsprot", &info);
2162     afsconf_Close(tdir);
2163
2164     sc[0] = 0;
2165     sc[1] = 0;
2166     sc[2] = 0;
2167
2168     /* we have the token that was given to us in the settoken 
2169      * call.   we just have to use it. 
2170      */
2171     scIndex = 2;        /* kerberos ticket */
2172     sc[2] = rxkad_NewClientSecurityObject(rxkad_clear, &ucellp->sessionKey,
2173                                           ucellp->kvno, ucellp->ticketLen,
2174                                           ucellp->ticketp);
2175
2176     memset(serverconns, 0, sizeof(serverconns));        /* terminate list!!! */
2177     for (i = 0; i < info.numServers; i++)
2178         serverconns[i] =
2179             rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
2180                              info.hostAddr[i].sin_port, PRSRV, sc[scIndex],
2181                              scIndex);
2182
2183     code = ubik_ClientInit(serverconns, &pruclient);
2184     if (code) {
2185         return code;
2186     }
2187
2188     code = rxs_Release(sc[scIndex]);
2189
2190     lids.idlist_len = 0;
2191     lids.idlist_val = 0;
2192     lnames.namelist_len = 1;
2193     lnames.namelist_val = (prname *) malloc(PR_MAXNAMELEN);
2194     strncpy(lnames.namelist_val[0], uname, PR_MAXNAMELEN);
2195     lnames.namelist_val[0][PR_MAXNAMELEN-1] = '\0';
2196     for ( p=lnames.namelist_val[0], r=NULL; *p; p++ ) {
2197         if (isupper(*p))
2198             *p = tolower(*p);
2199         if (*p == '@')
2200             r = p;
2201     }
2202     if (r && !cm_stricmp_utf8(r+1,ucellp->cellp->name))
2203         *r = '\0';
2204
2205     code = ubik_PR_NameToID(pruclient, 0, &lnames, &lids);
2206     if (lids.idlist_val) {
2207         *uid = *lids.idlist_val;
2208         free(lids.idlist_val);
2209     }
2210     if (lnames.namelist_val)
2211         free(lnames.namelist_val);
2212
2213     if ( pruclient ) {
2214         ubik_ClientDestroy(pruclient);
2215         pruclient = NULL;
2216     }
2217
2218     return 0;
2219 }
2220 #endif /* QUERY_AFSID */
2221
2222 #if 0
2223 /* This has been copied to smb_IoctlSetToken in its entirety.
2224  * An equivalent version will need to be produced for the 
2225  * redirector and some extensive refactoring might be required.
2226  */
2227 afs_int32
2228 cm_IoctlSetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
2229 {
2230     char *saveDataPtr;
2231     char *tp;
2232     int ticketLen;
2233     char *ticket;
2234     int ctSize;
2235     struct ClearToken ct;
2236     cm_cell_t *cellp;
2237     cm_ucell_t *ucellp;
2238     char *uname = NULL;
2239     afs_uuid_t uuid;
2240     int flags;
2241     char sessionKey[8];
2242     char *smbname;
2243     int release_userp = 0;
2244     char * wdir = NULL;
2245
2246     saveDataPtr = ioctlp->inDatap;
2247
2248     cm_SkipIoctlPath(ioctlp);
2249
2250     tp = ioctlp->inDatap;
2251
2252     /* ticket length */
2253     memcpy(&ticketLen, tp, sizeof(ticketLen));
2254     tp += sizeof(ticketLen);
2255     if (ticketLen < MINKTCTICKETLEN || ticketLen > MAXKTCTICKETLEN)
2256         return CM_ERROR_INVAL;
2257
2258     /* remember ticket and skip over it for now */
2259     ticket = tp;
2260     tp += ticketLen;
2261
2262     /* clear token size */
2263     memcpy(&ctSize, tp, sizeof(ctSize));
2264     tp += sizeof(ctSize);
2265     if (ctSize != sizeof(struct ClearToken))
2266         return CM_ERROR_INVAL;
2267
2268     /* clear token */
2269     memcpy(&ct, tp, ctSize);
2270     tp += ctSize;
2271     if (ct.AuthHandle == -1)
2272         ct.AuthHandle = 999;    /* more rxvab compat stuff */
2273
2274     /* more stuff, if any */
2275     if (ioctlp->inCopied > tp - saveDataPtr) {
2276         /* flags:  logon flag */
2277         memcpy(&flags, tp, sizeof(int));
2278         tp += sizeof(int);
2279
2280         /* cell name */
2281         cellp = cm_GetCell(tp, CM_FLAG_CREATE | CM_FLAG_NOPROBE);
2282         if (!cellp) 
2283             return CM_ERROR_NOSUCHCELL;
2284         tp += strlen(tp) + 1;
2285
2286         /* user name */
2287         uname = tp;
2288         tp += strlen(tp) + 1;
2289
2290         if (flags & PIOCTL_LOGON) {
2291             /* SMB user name with which to associate tokens */
2292             smbname = tp;
2293             osi_Log2(smb_logp,"cm_IoctlSetToken for user [%s] smbname [%s]",
2294                      osi_LogSaveString(smb_logp,uname), osi_LogSaveString(smb_logp,smbname));
2295             fprintf(stderr, "SMB name = %s\n", smbname);
2296             tp += strlen(tp) + 1;
2297         } else {
2298             osi_Log1(smb_logp,"cm_IoctlSetToken for user [%s]",
2299                      osi_LogSaveString(smb_logp, uname));
2300         }
2301
2302                 /* uuid */
2303         memcpy(&uuid, tp, sizeof(uuid));
2304         if (!cm_FindTokenEvent(uuid, sessionKey))
2305             return CM_ERROR_INVAL;
2306     } else {
2307         cellp = cm_data.rootCellp;
2308         osi_Log0(smb_logp,"cm_IoctlSetToken - no name specified");
2309     }
2310
2311     if (flags & PIOCTL_LOGON) {
2312         userp = smb_FindCMUserByName(smbname, ioctlp->fidp->vcp->rname,
2313                                      SMB_FLAG_CREATE|SMB_FLAG_AFSLOGON);
2314         release_userp = 1;
2315     }
2316
2317     /* store the token */
2318     lock_ObtainMutex(&userp->mx);
2319     ucellp = cm_GetUCell(userp, cellp);
2320     osi_Log1(smb_logp,"cm_IoctlSetToken ucellp %lx", ucellp);
2321     ucellp->ticketLen = ticketLen;
2322     if (ucellp->ticketp)
2323         free(ucellp->ticketp);  /* Discard old token if any */
2324     ucellp->ticketp = malloc(ticketLen);
2325     memcpy(ucellp->ticketp, ticket, ticketLen);
2326     /*
2327      * Get the session key from the RPC, rather than from the pioctl.
2328      */
2329     /*
2330     memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
2331     */
2332     memcpy(ucellp->sessionKey.data, sessionKey, sizeof(sessionKey));
2333     ucellp->kvno = ct.AuthHandle;
2334     ucellp->expirationTime = ct.EndTimestamp;
2335     ucellp->gen++;
2336 #ifdef QUERY_AFSID
2337     ucellp->uid = ANONYMOUSID;
2338 #endif
2339     if (uname) {
2340         StringCbCopyA(ucellp->userName, MAXKTCNAMELEN, uname);
2341 #ifdef QUERY_AFSID
2342         cm_UsernameToId(uname, ucellp, &ucellp->uid);
2343 #endif
2344     }
2345     ucellp->flags |= CM_UCELLFLAG_RXKAD;
2346     lock_ReleaseMutex(&userp->mx);
2347
2348     if (flags & PIOCTL_LOGON) {
2349         ioctlp->flags |= CM_IOCTLFLAG_LOGON;
2350     }
2351
2352     cm_ResetACLCache(userp);
2353
2354     if (release_userp)
2355         cm_ReleaseUser(userp);
2356
2357     return 0;
2358 }
2359 #endif
2360
2361 /* 
2362  * VIOC_GETTOK internals.
2363  * 
2364  * Assumes that pioctl path has been parsed or skipped.
2365  */
2366 afs_int32
2367 cm_IoctlGetTokenIter(struct cm_ioctl *ioctlp, struct cm_user *userp)
2368 {
2369     char *tp, *cp;
2370     int iterator;
2371     int temp;
2372     cm_ucell_t *ucellp;
2373     struct ClearToken ct;
2374
2375     tp = ioctlp->inDatap;
2376     cp = ioctlp->outDatap;
2377
2378     /* iterator */
2379     memcpy(&iterator, tp, sizeof(iterator));
2380     tp += sizeof(iterator);
2381
2382     lock_ObtainMutex(&userp->mx);
2383
2384     /* look for token */
2385     for (;;iterator++) {
2386         ucellp = cm_FindUCell(userp, iterator);
2387         if (!ucellp) {
2388             lock_ReleaseMutex(&userp->mx);
2389             return CM_ERROR_NOMORETOKENS;
2390         }
2391         if (ucellp->flags & CM_UCELLFLAG_RXKAD)
2392             break;
2393     }       
2394
2395     /* new iterator */
2396     temp = ucellp->iterator + 1;
2397     memcpy(cp, &temp, sizeof(temp));
2398     cp += sizeof(temp);
2399
2400     /* ticket length */
2401     memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
2402     cp += sizeof(ucellp->ticketLen);
2403
2404     /* ticket */
2405     memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
2406     cp += ucellp->ticketLen;
2407
2408     /* clear token size */
2409     temp = sizeof(ct);
2410     memcpy(cp, &temp, sizeof(temp));
2411     cp += sizeof(temp);
2412
2413     /* clear token */
2414     ct.AuthHandle = ucellp->kvno;
2415
2416     /*
2417      * This field is supposed to hold the session key
2418      * but we don't want to make it easier for someone 
2419      * to attack the cache.  The user gave us the session
2420      * key in the first place.
2421      */
2422     memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
2423     ct.ViceId = 37;                     /* XXX */
2424     ct.BeginTimestamp = 0;              /* XXX */
2425     ct.EndTimestamp = ucellp->expirationTime;
2426     memcpy(cp, &ct, sizeof(ct));
2427     cp += sizeof(ct);
2428
2429     /* Primary flag (unused) */
2430     temp = 0;
2431     memcpy(cp, &temp, sizeof(temp));
2432     cp += sizeof(temp);
2433
2434     /* cell name */
2435     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), ucellp->cellp->name);
2436     cp += strlen(cp) + 1;
2437
2438     /* user name */
2439     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), ucellp->userName);
2440     cp += strlen(cp) + 1;
2441
2442     ioctlp->outDatap = cp;
2443
2444     lock_ReleaseMutex(&userp->mx);
2445
2446     return 0;
2447 }
2448
2449 /* 
2450  * VIOC_NEWGETTOK internals.
2451  * 
2452  * Assumes that pioctl path has been parsed or skipped.
2453  */
2454 afs_int32
2455 cm_IoctlGetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
2456 {
2457     char *cp;
2458     int temp;
2459     cm_cell_t *cellp;
2460     cm_ucell_t *ucellp;
2461     struct ClearToken ct;
2462     char *tp;
2463     afs_uuid_t uuid;
2464
2465     tp = ioctlp->inDatap;
2466
2467     cp = ioctlp->outDatap;
2468
2469     /* cell name is right here */
2470     cellp = cm_GetCell(tp, 0);
2471     if (!cellp) 
2472         return CM_ERROR_NOSUCHCELL;
2473     tp += strlen(tp) + 1;
2474
2475     /* uuid */
2476     memcpy(&uuid, tp, sizeof(uuid));
2477
2478     lock_ObtainMutex(&userp->mx);
2479
2480     ucellp = cm_GetUCell(userp, cellp);
2481     if (!ucellp || !(ucellp->flags & CM_UCELLFLAG_RXKAD)) {
2482         lock_ReleaseMutex(&userp->mx);
2483         return CM_ERROR_NOMORETOKENS;
2484     }
2485
2486     /* ticket length */
2487     memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
2488     cp += sizeof(ucellp->ticketLen);
2489
2490     /* ticket */
2491     memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
2492     cp += ucellp->ticketLen;
2493
2494     /* clear token size */
2495     temp = sizeof(ct);
2496     memcpy(cp, &temp, sizeof(temp));
2497     cp += sizeof(temp);
2498
2499     /* clear token */
2500     ct.AuthHandle = ucellp->kvno;
2501
2502     /* do not give out the session key */
2503     memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
2504     ct.ViceId = 37;                     /* XXX */
2505     ct.BeginTimestamp = 0;              /* XXX */
2506     ct.EndTimestamp = ucellp->expirationTime;
2507     memcpy(cp, &ct, sizeof(ct));
2508     cp += sizeof(ct);
2509
2510     /* Primary flag (unused) */
2511     temp = 0;
2512     memcpy(cp, &temp, sizeof(temp));
2513     cp += sizeof(temp);
2514
2515     /* cell name */
2516     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), ucellp->cellp->name);
2517     cp += strlen(cp) + 1;
2518
2519     /* user name */
2520     StringCbCopyA(cp, SMB_IOCTL_MAXDATA - (cp - ioctlp->outAllocp), ucellp->userName);
2521     cp += strlen(cp) + 1;
2522
2523     ioctlp->outDatap = cp;
2524
2525     lock_ReleaseMutex(&userp->mx);
2526
2527     cm_RegisterNewTokenEvent(uuid, ucellp->sessionKey.data);
2528
2529     return 0;
2530 }
2531
2532 /* 
2533  * VIOCDELTOK internals.
2534  * 
2535  * Assumes that pioctl path has been parsed or skipped.
2536  */
2537 afs_int32
2538 cm_IoctlDelToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
2539 {
2540     char *cp;
2541     cm_cell_t *cellp;
2542     cm_ucell_t *ucellp;
2543
2544     cp = ioctlp->outDatap;
2545
2546     /* cell name is right here */
2547     cellp = cm_GetCell(ioctlp->inDatap, 0);
2548     if (!cellp) 
2549         return CM_ERROR_NOSUCHCELL;
2550
2551     lock_ObtainMutex(&userp->mx);
2552
2553     ucellp = cm_GetUCell(userp, cellp);
2554     if (!ucellp) {
2555         lock_ReleaseMutex(&userp->mx);
2556         return CM_ERROR_NOMORETOKENS;
2557     }
2558
2559     osi_Log1(smb_logp,"cm_IoctlDelToken ucellp %lx", ucellp);
2560
2561     if (ucellp->ticketp) {
2562         free(ucellp->ticketp);
2563         ucellp->ticketp = NULL;
2564     }
2565     ucellp->ticketLen = 0;
2566     memset(ucellp->sessionKey.data, 0, 8);
2567     ucellp->kvno = 0;
2568     ucellp->expirationTime = 0;
2569     ucellp->userName[0] = '\0';
2570     ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
2571     ucellp->gen++;
2572
2573     lock_ReleaseMutex(&userp->mx);
2574
2575     cm_ResetACLCache(userp);
2576
2577     return 0;
2578 }
2579
2580 /* 
2581  * VIOCDELALLTOK internals.
2582  * 
2583  * Assumes that pioctl path has been parsed or skipped.
2584  */
2585 afs_int32
2586 cm_IoctlDelAllToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
2587 {
2588     cm_ucell_t *ucellp;
2589
2590     lock_ObtainMutex(&userp->mx);
2591
2592     for (ucellp = userp->cellInfop; ucellp; ucellp = ucellp->nextp) {
2593         osi_Log1(smb_logp,"cm_IoctlDelAllToken ucellp %lx", ucellp);
2594
2595         if (ucellp->ticketp) {
2596             free(ucellp->ticketp);
2597             ucellp->ticketp = NULL;
2598         }
2599         ucellp->ticketLen = 0;
2600         memset(ucellp->sessionKey.data, 0, 8);
2601         ucellp->kvno = 0;
2602         ucellp->expirationTime = 0;
2603         ucellp->userName[0] = '\0';
2604         ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
2605         ucellp->gen++;
2606     }
2607
2608     lock_ReleaseMutex(&userp->mx);
2609
2610     cm_ResetACLCache(userp);
2611
2612     return 0;
2613 }
2614
2615 /* 
2616  * VIOC_MAKESUBMOUNT internals.  (This function should be deprecated)
2617  * 
2618  * Assumes that pioctl path has been parsed or skipped.
2619  */
2620 afs_int32
2621 cm_IoctlMakeSubmount(cm_ioctl_t *ioctlp, cm_user_t *userp)
2622 {
2623     char afspath[MAX_PATH];
2624     char *submountreqp;
2625     int nextAutoSubmount;
2626     HKEY hkSubmounts;
2627     DWORD dwType, dwSize;
2628     DWORD status;
2629     DWORD dwIndex;
2630     DWORD dwSubmounts;
2631
2632     /* Serialize this one, to prevent simultaneous mods
2633      * to afsdsbmt.ini
2634      */
2635     lock_ObtainMutex(&cm_Afsdsbmt_Lock);
2636
2637     /* Parse the input parameters--first the required afs path,
2638      * then the requested submount name (which may be "").
2639      */
2640     cm_NormalizeAfsPathAscii(afspath, sizeof(afspath), ioctlp->inDatap);
2641     submountreqp = ioctlp->inDatap + (strlen(ioctlp->inDatap)+1);
2642
2643     /* If the caller supplied a suggested submount name, see if
2644      * that submount name is in use... if so, the submount's path
2645      * has to match our path.
2646      */
2647
2648     RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
2649                     AFSREG_CLT_OPENAFS_SUBKEY "\\Submounts",
2650                     0, 
2651                     "AFS", 
2652                     REG_OPTION_NON_VOLATILE,
2653                     KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
2654                     NULL, 
2655                     &hkSubmounts,
2656                     NULL );
2657
2658     if (submountreqp && *submountreqp) {
2659         char submountPath[MAX_PATH];
2660
2661         dwSize = sizeof(submountPath);
2662         status = RegQueryValueEx( hkSubmounts, submountreqp, 0,
2663                                   &dwType, submountPath, &dwSize);
2664
2665         if (status != ERROR_SUCCESS) {
2666
2667             /* The suggested submount name isn't in use now--
2668              * so we can safely map the requested submount name
2669              * to the supplied path. Remember not to write the
2670              * leading "/afs" when writing out the submount.
2671              */
2672             RegSetValueEx( hkSubmounts, submountreqp, 0,
2673                            REG_EXPAND_SZ, 
2674                            (strlen(&afspath[strlen(cm_mountRoot)])) ?
2675                            &afspath[strlen(cm_mountRoot)]:"/",
2676                            (strlen(&afspath[strlen(cm_mountRoot)])) ?
2677                            (DWORD)strlen(&afspath[strlen(cm_mountRoot)])+1:2);
2678
2679             RegCloseKey( hkSubmounts );
2680             StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), submountreqp);
2681             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
2682             lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
2683             return 0;
2684         }
2685
2686         /* The suggested submount name is already in use--if the
2687          * supplied path matches the submount's path, we can still
2688          * use the suggested submount name.
2689          */
2690         if (!strcmp (submountPath, afspath)) {
2691             StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), submountreqp);
2692             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
2693             RegCloseKey( hkSubmounts );
2694             lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
2695             return 0;
2696         }
2697     }
2698
2699     RegQueryInfoKey( hkSubmounts,
2700                      NULL,  /* lpClass */
2701                      NULL,  /* lpcClass */
2702                      NULL,  /* lpReserved */
2703                      NULL,  /* lpcSubKeys */
2704                      NULL,  /* lpcMaxSubKeyLen */
2705                      NULL,  /* lpcMaxClassLen */
2706                      &dwSubmounts, /* lpcValues */
2707                      NULL,  /* lpcMaxValueNameLen */
2708                      NULL,  /* lpcMaxValueLen */
2709                      NULL,  /* lpcbSecurityDescriptor */
2710                      NULL   /* lpftLastWriteTime */
2711                      );
2712
2713
2714     /* Having obtained a list of all available submounts, start
2715      * searching that list for a path which matches the requested
2716      * AFS path. We'll also keep track of the highest "auto15"/"auto47"
2717      * submount, in case we need to add a new one later.
2718      */
2719
2720     nextAutoSubmount = 1;
2721
2722     for ( dwIndex = 0; dwIndex < dwSubmounts; dwIndex ++ ) {
2723         char submountPath[MAX_PATH] = "";
2724         DWORD submountPathLen = sizeof(submountPath);
2725         char submountName[MAX_PATH];
2726         DWORD submountNameLen = sizeof(submountName);
2727
2728         dwType = 0;
2729         RegEnumValue( hkSubmounts, dwIndex, submountName, &submountNameLen, NULL,
2730                       &dwType, submountPath, &submountPathLen);
2731         if (dwType == REG_EXPAND_SZ) {
2732             char buf[MAX_PATH];
2733             StringCbCopyA(buf, MAX_PATH, submountPath);
2734             submountPathLen = ExpandEnvironmentStrings(buf, submountPath, MAX_PATH);
2735             if (submountPathLen > MAX_PATH)
2736                 continue;
2737         }
2738
2739         /* If this is an Auto### submount, remember its ### value */
2740         if ((!strnicmp (submountName, "auto", 4)) &&
2741              (isdigit (submountName[strlen("auto")]))) {
2742             int thisAutoSubmount;
2743             thisAutoSubmount = atoi (&submountName[strlen("auto")]);
2744             nextAutoSubmount = max (nextAutoSubmount,
2745                                      thisAutoSubmount+1);
2746         }       
2747
2748         if ((submountPathLen == 0) ||
2749              (submountPathLen == sizeof(submountPath) - 1)) {
2750             continue;
2751         }
2752
2753         /* See if the path for this submount matches the path
2754          * that our caller specified. If so, we can return
2755          * this submount.
2756          */
2757         if (!strcmp (submountPath, afspath)) {
2758             StringCbCopyA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), submountName);
2759             ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
2760             RegCloseKey(hkSubmounts);
2761             lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
2762             return 0;
2763
2764         }
2765     }
2766
2767     /* We've been through the entire list of existing submounts, and
2768      * didn't find any which matched the specified path. So, we'll
2769      * just have to add one. Remember not to write the leading "/afs"
2770      * when writing out the submount.
2771      */
2772
2773     StringCbPrintfA(ioctlp->outDatap, SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp), "auto%ld", nextAutoSubmount);
2774
2775     RegSetValueEx( hkSubmounts, 
2776                    ioctlp->outDatap,
2777                    0,
2778                    REG_EXPAND_SZ, 
2779                    (strlen(&afspath[strlen(cm_mountRoot)])) ?
2780                    &afspath[strlen(cm_mountRoot)]:"/",
2781                    (strlen(&afspath[strlen(cm_mountRoot)])) ?
2782                    (DWORD)strlen(&afspath[strlen(cm_mountRoot)])+1:2);
2783
2784     ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
2785     RegCloseKey(hkSubmounts);
2786     lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
2787     return 0;
2788 }
2789
2790 /* 
2791  * VIOC_GETRXKCRYPT internals.
2792  * 
2793  * Assumes that pioctl path has been parsed or skipped.
2794  */
2795 afs_int32
2796 cm_IoctlGetRxkcrypt(cm_ioctl_t *ioctlp, cm_user_t *userp)
2797 {
2798     memcpy(ioctlp->outDatap, &cryptall, sizeof(cryptall));
2799     ioctlp->outDatap += sizeof(cryptall);
2800
2801     return 0;
2802 }
2803
2804 /* 
2805  * VIOC_SETRXKCRYPT internals.
2806  * 
2807  * Assumes that pioctl path has been parsed or skipped.
2808  */
2809 afs_int32
2810 cm_IoctlSetRxkcrypt(cm_ioctl_t *ioctlp, cm_user_t *userp)
2811 {
2812     afs_int32 c = cryptall;
2813
2814     memcpy(&cryptall, ioctlp->inDatap, sizeof(cryptall));
2815
2816     if (c != cryptall) {
2817         if (cryptall == 1)
2818             LogEvent(EVENTLOG_INFORMATION_TYPE, MSG_CRYPT_ON);
2819         else if (cryptall == 2)
2820             LogEvent(EVENTLOG_INFORMATION_TYPE, MSG_CRYPT_AUTH);
2821         else
2822             LogEvent(EVENTLOG_INFORMATION_TYPE, MSG_CRYPT_OFF);
2823     }
2824     return 0;
2825 }
2826
2827 /* 
2828  * VIOC_RXSTAT_PROC internals.
2829  * 
2830  * Assumes that pioctl path has been parsed or skipped.
2831  */
2832 afs_int32
2833 cm_IoctlRxStatProcess(struct cm_ioctl *ioctlp, struct cm_user *userp)
2834 {
2835     afs_int32 flags;
2836     int code = 0;
2837
2838     memcpy((char *)&flags, ioctlp->inDatap, sizeof(afs_int32));
2839     if (!(flags & AFSCALL_RXSTATS_MASK) || (flags & ~AFSCALL_RXSTATS_MASK)) {
2840         return -1;
2841     }
2842     if (flags & AFSCALL_RXSTATS_ENABLE) {
2843         rx_enableProcessRPCStats();
2844     }
2845     if (flags & AFSCALL_RXSTATS_DISABLE) {
2846         rx_disableProcessRPCStats();
2847     }
2848     if (flags & AFSCALL_RXSTATS_CLEAR) {
2849         rx_clearProcessRPCStats(AFS_RX_STATS_CLEAR_ALL);
2850     }
2851     return 0;
2852 }
2853
2854 /* 
2855  * VIOC_RXSTAT_PEER internals.
2856  * 
2857  * Assumes that pioctl path has been parsed or skipped.
2858  */
2859 afs_int32
2860 cm_IoctlRxStatPeer(struct cm_ioctl *ioctlp, struct cm_user *userp)
2861 {
2862     afs_int32 flags;
2863     int code = 0;
2864
2865     memcpy((char *)&flags, ioctlp->inDatap, sizeof(afs_int32));
2866     if (!(flags & AFSCALL_RXSTATS_MASK) || (flags & ~AFSCALL_RXSTATS_MASK)) {
2867         return -1;
2868     }
2869     if (flags & AFSCALL_RXSTATS_ENABLE) {
2870         rx_enablePeerRPCStats();
2871     }
2872     if (flags & AFSCALL_RXSTATS_DISABLE) {
2873         rx_disablePeerRPCStats();
2874     }
2875     if (flags & AFSCALL_RXSTATS_CLEAR) {
2876         rx_clearPeerRPCStats(AFS_RX_STATS_CLEAR_ALL);
2877     }
2878     return 0;
2879 }
2880
2881 /* 
2882  * VIOC_UNICODECTL internals.
2883  * 
2884  * Assumes that pioctl path has been parsed or skipped.
2885  */
2886 afs_int32
2887 cm_IoctlUnicodeControl(struct cm_ioctl *ioctlp, struct cm_user * userp)
2888 {
2889     afs_int32 result = 0;
2890 #ifdef SMB_UNICODE
2891     afs_uint32 cmd;
2892
2893     memcpy(&cmd, ioctlp->inDatap, sizeof(afs_uint32));
2894
2895     if (cmd & 2) {
2896         /* Setting the Unicode flag */
2897         LONG newflag;
2898
2899         newflag = ((cmd & 1) == 1);
2900
2901         InterlockedExchange(&smb_UseUnicode, newflag);
2902     }
2903
2904     result = smb_UseUnicode;
2905 #else
2906     result = 2;
2907 #endif
2908
2909     memcpy(ioctlp->outDatap, &result, sizeof(result));
2910     ioctlp->outDatap += sizeof(result);
2911
2912     return 0;
2913 }
2914
2915 /* 
2916  * VIOC_UUIDCTL internals.
2917  * 
2918  * Assumes that pioctl path has been parsed or skipped.
2919  */
2920 afs_int32
2921 cm_IoctlUUIDControl(struct cm_ioctl * ioctlp, struct cm_user *userp)
2922 {
2923     afs_uint32 cmd;
2924     afsUUID uuid;
2925
2926     memcpy(&cmd, ioctlp->inDatap, sizeof(afs_uint32));
2927
2928     if (cmd) {             /* generate a new UUID */
2929         UuidCreate((UUID *) &uuid);
2930         cm_data.Uuid = uuid;
2931         cm_ForceNewConnectionsAllServers();
2932     }
2933
2934     memcpy(ioctlp->outDatap, &cm_data.Uuid, sizeof(cm_data.Uuid));
2935     ioctlp->outDatap += sizeof(cm_data.Uuid);
2936
2937     return 0;
2938 }
2939
2940
2941
2942 /* 
2943  * functions to dump contents of various structures. 
2944  * In debug build (linked with crt debug library) will dump allocated but not freed memory
2945  */
2946 extern int cm_DumpSCache(FILE *outputFile, char *cookie, int lock);
2947 extern int cm_DumpBufHashTable(FILE *outputFile, char *cookie, int lock);
2948
2949 /* 
2950  * VIOC_TRACEMEMDUMP internals.
2951  * 
2952  * Assumes that pioctl path has been parsed or skipped.
2953  * dscp is held but not locked.
2954  */
2955 afs_int32
2956 cm_IoctlMemoryDump(struct cm_ioctl *ioctlp, struct cm_user *userp)
2957 {
2958     afs_int32 inValue = 0;
2959     HANDLE hLogFile;
2960     char logfileName[MAX_PATH+1];
2961     char *cookie;
2962     DWORD dwSize;
2963   
2964 #ifdef _DEBUG  
2965     static _CrtMemState memstate;
2966 #endif
2967   
2968     memcpy(&inValue, ioctlp->inDatap, sizeof(afs_int32));
2969   
2970     dwSize = GetEnvironmentVariable("TEMP", logfileName, sizeof(logfileName));
2971     if ( dwSize == 0 || dwSize > sizeof(logfileName) )
2972     {
2973         GetWindowsDirectory(logfileName, sizeof(logfileName));
2974     }
2975     strncat(logfileName, "\\afsd_alloc.log", sizeof(logfileName));
2976
2977     hLogFile = CreateFile(logfileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2978   
2979     if (!hLogFile)
2980     {
2981       /* error */
2982       inValue = -1;
2983       memcpy(ioctlp->outDatap, &inValue, sizeof(afs_int32));
2984       ioctlp->outDatap += sizeof(afs_int32);
2985       
2986       return 0;               
2987     }
2988   
2989     SetFilePointer(hLogFile, 0, NULL, FILE_END);
2990   
2991     cookie = inValue ? "b" : "e";
2992   
2993 #ifdef _DEBUG  
2994   
2995     if (inValue)
2996     {
2997       _CrtMemCheckpoint(&memstate);           
2998     }
2999     else
3000     {
3001         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
3002         _CrtSetReportFile(_CRT_WARN, hLogFile);
3003         _CrtMemDumpAllObjectsSince(&memstate);
3004     }
3005 #endif
3006   
3007     /* dump all interesting data */
3008     cm_MemDumpDirStats(hLogFile, cookie, 1);
3009     cm_MemDumpBPlusStats(hLogFile, cookie, 1);
3010     cm_DumpCells(hLogFile, cookie, 1);
3011     cm_DumpVolumes(hLogFile, cookie, 1);
3012     cm_DumpSCache(hLogFile, cookie, 1);
3013     cm_DumpBufHashTable(hLogFile, cookie, 1);
3014     smb_DumpVCP(hLogFile, cookie, 1);
3015
3016     CloseHandle(hLogFile);                          
3017   
3018     inValue = 0;        /* success */
3019     memcpy(ioctlp->outDatap, &inValue, sizeof(long));
3020     ioctlp->outDatap += sizeof(long);
3021   
3022     return 0;
3023 }
3024
3025 /* Utility functon.  Not currently used. */
3026 static afs_int32
3027 cm_CheckServersStatus(cm_serverRef_t *serversp)
3028 {
3029     afs_int32 code = 0;
3030     cm_serverRef_t *tsrp;
3031     cm_server_t *tsp;
3032     int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
3033
3034     if (serversp == NULL) {
3035         osi_Log1(afsd_logp, "cm_CheckServersStatus returning 0x%x", CM_ERROR_ALLDOWN);
3036         return CM_ERROR_ALLDOWN;
3037     }
3038
3039     lock_ObtainRead(&cm_serverLock);
3040     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
3041         if (tsrp->status == srv_deleted)
3042             continue;
3043         if (tsp = tsrp->server) {
3044             cm_GetServerNoLock(tsp);
3045             lock_ReleaseRead(&cm_serverLock);
3046             if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
3047                 allDown = 0;
3048                 if (tsrp->status == srv_busy) {
3049                     allOffline = 0;
3050                     someBusy = 1;
3051                 } else if (tsrp->status == srv_offline) {
3052                     allBusy = 0;
3053                     someOffline = 1;
3054                 } else {
3055                     allOffline = 0;
3056                     allBusy = 0;
3057                     cm_PutServer(tsp);
3058                     goto done;
3059                 }
3060             }
3061             lock_ObtainRead(&cm_serverLock);
3062             cm_PutServerNoLock(tsp);
3063         }
3064     }   
3065     lock_ReleaseRead(&cm_serverLock);
3066
3067     if (allDown) 
3068         code = CM_ERROR_ALLDOWN;
3069     else if (allBusy) 
3070         code = CM_ERROR_ALLBUSY;
3071     else if (allOffline || (someBusy && someOffline))
3072         code = CM_ERROR_ALLOFFLINE;
3073
3074   done:
3075     osi_Log1(afsd_logp, "cm_CheckServersStatus returning 0x%x", code);
3076     return code;
3077 }
3078
3079 /* 
3080  * VIOC_PATH_AVAILABILITY internals.
3081  * 
3082  * Assumes that pioctl path has been parsed or skipped.
3083  * scp is held but not locked.
3084  */
3085 afs_int32
3086 cm_IoctlPathAvailability(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp, cm_req_t *reqp)
3087 {
3088     afs_int32 code;
3089     cm_cell_t *cellp;
3090     cm_volume_t *tvp;
3091     cm_vol_state_t *statep;
3092     afs_uint32 volume;
3093         
3094 #ifdef AFS_FREELANCE_CLIENT
3095     if ( scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID ) {
3096         code = 0;
3097     } else
3098 #endif
3099     {
3100         volume = scp->fid.volume;
3101
3102         cellp = cm_FindCellByID(scp->fid.cell, 0);
3103
3104         if (!cellp)
3105             return CM_ERROR_NOSUCHCELL;
3106
3107         code = cm_FindVolumeByID(cellp, volume, userp, reqp, CM_GETVOL_FLAG_CREATE, &tvp);
3108         if (code) 
3109             return code;
3110         
3111         statep = cm_VolumeStateByID(tvp, volume);
3112         switch (statep->state) {
3113         case vl_online:
3114         case vl_unknown:
3115             code = 0;
3116             break;
3117         case vl_busy:
3118             code = CM_ERROR_ALLBUSY;
3119             break;
3120         case vl_offline:
3121             code = CM_ERROR_ALLOFFLINE;
3122             break;
3123         case vl_alldown:
3124             code = CM_ERROR_ALLDOWN;
3125             break;
3126         }
3127         cm_PutVolume(tvp);
3128     }
3129     return code;
3130 }       
3131
3132 /* 
3133  * VIOC_VOLSTAT_TEST internals.
3134  * 
3135  * Assumes that pioctl path has been parsed or skipped.
3136  */
3137 afs_int32
3138 cm_IoctlVolStatTest(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_req_t *reqp)
3139 {
3140     afs_int32 code;
3141     cm_cell_t *cellp = NULL;
3142     cm_volume_t *volp;
3143     cm_vol_state_t *statep;
3144     struct VolStatTest * testp;
3145     afs_uint32 n;
3146
3147     testp = (struct VolStatTest *)ioctlp->inDatap;
3148
3149 #ifdef AFS_FREELANCE_CLIENT
3150     if (testp->fid.cell == -1) 
3151         return CM_ERROR_NOACCESS;
3152 #endif
3153
3154     if (testp->flags & VOLSTAT_TEST_CHECK_VOLUME) {
3155         cm_CheckOfflineVolumes();
3156         return 0;
3157     }
3158
3159     if (testp->flags & VOLSTAT_TEST_NETWORK_UP) {
3160         cm_VolStatus_Network_Started(cm_NetbiosName
3161 #ifdef _WIN64
3162                                   , cm_NetbiosName
3163 #endif
3164                                   );
3165         return 0;
3166     }
3167
3168     if (testp->flags & VOLSTAT_TEST_NETWORK_DOWN) {
3169         cm_VolStatus_Network_Stopped(cm_NetbiosName
3170 #ifdef _WIN64
3171                                   , cm_NetbiosName
3172 #endif
3173                                   );
3174         return 0;
3175     }
3176
3177     if (testp->cellname[0]) {
3178         n = atoi(testp->cellname);
3179         if (n)
3180             testp->fid.cell = n;
3181         else
3182             cellp = cm_GetCell(testp->cellname, 0);
3183     }
3184
3185     if (testp->fid.cell > 0) {
3186         cellp = cm_FindCellByID(testp->fid.cell, 0);
3187     }
3188
3189     if (!cellp)
3190         return CM_ERROR_NOSUCHCELL;
3191
3192     if (testp->volname[0]) {
3193         n = atoi(testp->volname);
3194         if (n)
3195             testp->fid.volume = n;
3196         else
3197             code = cm_FindVolumeByName(cellp, testp->volname, userp, reqp, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp);
3198     }
3199
3200     if (testp->fid.volume > 0)
3201         code = cm_FindVolumeByID(cellp, testp->fid.volume, userp, reqp, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp);
3202
3203     if (code)
3204         return code;
3205         
3206     if (testp->fid.volume)
3207         statep = cm_VolumeStateByID(volp, testp->fid.volume);
3208     else
3209         statep = cm_VolumeStateByName(volp, testp->volname);
3210
3211     if (statep) {
3212         statep->state = testp->state;
3213         code = cm_VolStatus_Change_Notification(cellp->cellID, statep->ID, testp->state);
3214     }
3215
3216     cm_PutVolume(volp);
3217
3218     return code;
3219 }