2 * Copyright (C) 1998, 1989 Transarc Corporation - All rights reserved
4 * (C) COPYRIGHT IBM CORPORATION 1987, 1988
5 * LICENSED MATERIALS - PROPERTY OF IBM
10 #include <afs/param.h>
24 #include "afsd_init.h"
33 /* Copied from afs_tokens.h */
34 #define PIOCTL_LOGON 0x1
36 osi_mutex_t cm_Afsdsbmt_Lock;
38 void cm_InitIoctl(void)
40 lock_InitializeMutex(&cm_Afsdsbmt_Lock, "AFSDSBMT.INI Access Lock");
43 long cm_FlushFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
47 lock_ObtainWrite(&scp->bufCreateLock);
48 code = buf_FlushCleanPages(scp, userp, reqp);
50 lock_ObtainMutex(&scp->mx);
51 scp->cbServerp = NULL;
53 lock_ReleaseMutex(&scp->mx);
55 lock_ReleaseWrite(&scp->bufCreateLock);
62 * cm_ResetACLCache -- invalidate ACL info for a user that has just
63 * obtained or lost tokens
65 void cm_ResetACLCache(cm_user_t *userp)
70 lock_ObtainWrite(&cm_scacheLock);
71 for (hash=0; hash < cm_hashTableSize; hash++) {
72 for (scp=cm_hashTablep[hash]; scp; scp=scp->nextp) {
74 lock_ReleaseWrite(&cm_scacheLock);
75 lock_ObtainMutex(&scp->mx);
76 cm_InvalidateACLUser(scp, userp);
77 lock_ReleaseMutex(&scp->mx);
78 lock_ObtainWrite(&cm_scacheLock);
82 lock_ReleaseWrite(&cm_scacheLock);
86 * TranslateExtendedChars - This is a fix for TR 54482.
88 * If an extended character (80 - FF) is entered into a file
89 * or directory name in Windows, the character is translated
90 * into the OEM character map before being passed to us. Why
91 * this occurs is unknown. Our pioctl functions must match
92 * this translation for paths given via our own commands (like
93 * fs). If we do not do this, then we will try to perform an
94 * operation on a non-translated path, which we will fail to
95 * find, since the path was created with the translated chars.
96 * This function performs the required translation.
98 void TranslateExtendedChars(char *str)
106 /* parse the passed-in file name and do a namei on it. If we fail,
107 * return an error code, otherwise return the vnode located in *scpp.
109 long cm_ParseIoctlPath(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
113 cm_scache_t *substRootp;
115 /* This is usually the file name, but for StatMountPoint it is the path. */
116 TranslateExtendedChars(ioctlp->inDatap);
118 code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
119 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
120 userp, ioctlp->tidPathp, reqp, &substRootp);
121 if (code) return code;
123 code = cm_NameI(substRootp, ioctlp->inDatap, CM_FLAG_FOLLOW,
124 userp, NULL, reqp, scpp);
125 if (code) return code;
127 /* # of bytes of path */
128 code = strlen(ioctlp->inDatap) + 1;
129 ioctlp->inDatap += code;
131 /* This is usually nothing, but for StatMountPoint it is the file name. */
132 TranslateExtendedChars(ioctlp->inDatap);
134 /* and return success */
138 void cm_SkipIoctlPath(smb_ioctl_t *ioctlp)
142 temp = strlen(ioctlp->inDatap) + 1;
143 ioctlp->inDatap += temp;
147 /* format the specified path to look like "/afs/<cellname>/usr", by
148 * adding "/afs" (if necessary) in front, changing any \'s to /'s, and
149 * removing any trailing "/"'s. One weirdo caveat: "/afs" will be
150 * intentionally returned as "/afs/"--this makes submount manipulation
151 * easier (because we can always jump past the initial "/afs" to find
152 * the AFS path that should be written into afsdsbmt.ini).
154 void cm_NormalizeAfsPath (char *outpathp, char *inpathp)
158 if (!strnicmp (inpathp, "/afs", strlen("/afs")))
159 lstrcpy (outpathp, inpathp);
160 else if (!strnicmp (inpathp, "\\afs", strlen("\\afs")))
161 lstrcpy (outpathp, inpathp);
162 else if ((inpathp[0] == '/') || (inpathp[0] == '\\'))
163 sprintf (outpathp, "/afs%s", inpathp);
164 else // inpathp looks like "<cell>/usr"
165 sprintf (outpathp, "/afs/%s", inpathp);
167 for (cp = outpathp; *cp != 0; ++cp) {
172 if (strlen(outpathp) && (outpathp[strlen(outpathp)-1] == '/')) {
173 outpathp[strlen(outpathp)-1] = 0;
176 if (!strcmpi (outpathp, "/afs")) {
177 strcpy (outpathp, "/afs/");
181 /* parse the passed-in file name and do a namei on its parent. If we fail,
182 * return an error code, otherwise return the vnode located in *scpp.
184 long cm_ParseIoctlParent(smb_ioctl_t *ioctlp, cm_user_t *userp, cm_req_t *reqp,
185 cm_scache_t **scpp, char *leafp)
190 cm_scache_t *substRootp;
192 strcpy(tbuffer, ioctlp->inDatap);
193 tp = strrchr(tbuffer, '\\');
194 jp = strrchr(tbuffer, '/');
197 else if (jp && (tp - tbuffer) < (jp - tbuffer))
200 strcpy(tbuffer, "\\");
201 if (leafp) strcpy(leafp, ioctlp->inDatap);
205 if (leafp) strcpy(leafp, tp+1);
208 code = cm_NameI(cm_rootSCachep, ioctlp->prefix->data,
209 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
210 userp, ioctlp->tidPathp, reqp, &substRootp);
211 if (code) return code;
213 code = cm_NameI(substRootp, tbuffer, CM_FLAG_FOLLOW,
214 userp, NULL, reqp, scpp);
215 if (code) return code;
217 /* # of bytes of path */
218 code = strlen(ioctlp->inDatap) + 1;
219 ioctlp->inDatap += code;
221 /* and return success */
225 long cm_IoctlGetACL(smb_ioctl_t *ioctlp, cm_user_t *userp)
230 AFSFetchStatus fileStatus;
239 code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
240 if (code) return code;
242 /* now make the get acl call */
243 fid.Volume = scp->fid.volume;
244 fid.Vnode = scp->fid.vnode;
245 fid.Unique = scp->fid.unique;
247 acl.AFSOpaque_val = ioctlp->outDatap;
248 acl.AFSOpaque_len = 0;
249 code = cm_Conn(&scp->fid, userp, &req, &connp);
252 code = RXAFS_FetchACL(connp->callp, &fid, &acl, &fileStatus, &volSync);
253 } while (cm_Analyze(connp, userp, &req, &scp->fid,
254 &volSync, NULL, code));
255 code = cm_MapRPCError(code, &req);
256 cm_ReleaseSCache(scp);
258 if (code) return code;
260 /* skip over return data */
261 tlen = strlen(ioctlp->outDatap) + 1;
262 ioctlp->outDatap += tlen;
264 /* and return success */
268 long cm_IoctlGetFileCellName(struct smb_ioctl *ioctlp, struct cm_user *userp)
277 code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
278 if (code) return code;
280 cellp = cm_FindCellByID(scp->fid.cell);
282 strcpy(ioctlp->outDatap, cellp->namep);
283 ioctlp->outDatap += strlen(ioctlp->outDatap) + 1;
286 else code = CM_ERROR_NOSUCHCELL;
288 cm_ReleaseSCache(scp);
292 long cm_IoctlSetACL(struct smb_ioctl *ioctlp, struct cm_user *userp)
297 AFSFetchStatus fileStatus;
305 code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
306 if (code) return code;
308 /* now make the get acl call */
309 fid.Volume = scp->fid.volume;
310 fid.Vnode = scp->fid.vnode;
311 fid.Unique = scp->fid.unique;
313 acl.AFSOpaque_val = ioctlp->inDatap;
314 acl.AFSOpaque_len = strlen(ioctlp->inDatap)+1;
315 code = cm_Conn(&scp->fid, userp, &req, &connp);
318 code = RXAFS_StoreACL(connp->callp, &fid, &acl, &fileStatus, &volSync);
319 } while (cm_Analyze(connp, userp, &req, &scp->fid,
320 &volSync, NULL, code));
321 code = cm_MapRPCError(code, &req);
323 /* invalidate cache info, since we just trashed the ACL cache */
324 lock_ObtainMutex(&scp->mx);
325 cm_DiscardSCache(scp);
326 lock_ReleaseMutex(&scp->mx);
328 cm_ReleaseSCache(scp);
333 long cm_IoctlFlushVolume(struct smb_ioctl *ioctlp, struct cm_user *userp)
337 unsigned long volume;
343 code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
344 if (code) return code;
346 volume = scp->fid.volume;
347 cm_ReleaseSCache(scp);
349 lock_ObtainWrite(&cm_scacheLock);
350 for(i=0; i<cm_hashTableSize; i++) {
351 for(scp = cm_hashTablep[i]; scp; scp = scp->nextp) {
352 if (scp->fid.volume == volume) {
354 lock_ReleaseWrite(&cm_scacheLock);
356 /* now flush the file */
357 cm_FlushFile(scp, userp, &req);
359 lock_ObtainWrite(&cm_scacheLock);
364 lock_ReleaseWrite(&cm_scacheLock);
369 long cm_IoctlFlushFile(struct smb_ioctl *ioctlp, struct cm_user *userp)
377 code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
378 if (code) return code;
380 cm_FlushFile(scp, userp, &req);
381 cm_ReleaseSCache(scp);
386 long cm_IoctlSetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
390 char offLineMsg[256];
394 AFSFetchVolumeStatus volStat;
395 AFSStoreVolumeStatus storeStat;
403 code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
404 if (code) return code;
406 cellp = cm_FindCellByID(scp->fid.cell);
409 if (scp->flags & CM_SCACHEFLAG_RO) {
410 cm_ReleaseSCache(scp);
411 return CM_ERROR_READONLY;
414 code = cm_GetVolumeByID(cellp, scp->fid.volume, userp, &req, &tvp);
416 cm_ReleaseSCache(scp);
420 /* Copy the junk out, using cp as a roving pointer. */
421 cp = ioctlp->inDatap;
422 memcpy((char *)&volStat, cp, sizeof(AFSFetchVolumeStatus));
423 cp += sizeof(AFSFetchVolumeStatus);
425 cp += strlen(volName)+1;
426 strcpy(offLineMsg, cp);
427 cp += strlen(offLineMsg)+1;
430 if (volStat.MinQuota != -1) {
431 storeStat.MinQuota = volStat.MinQuota;
432 storeStat.Mask |= AFS_SETMINQUOTA;
434 if (volStat.MaxQuota != -1) {
435 storeStat.MaxQuota = volStat.MaxQuota;
436 storeStat.Mask |= AFS_SETMAXQUOTA;
440 code = cm_Conn(&scp->fid, userp, &req, &tcp);
443 code = RXAFS_SetVolumeStatus(tcp->callp, scp->fid.volume,
444 &storeStat, volName, offLineMsg, motd);
445 } while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, code));
446 code = cm_MapRPCError(code, &req);
448 /* return on failure */
449 cm_ReleaseSCache(scp);
454 /* we are sending parms back to make compat. with prev system. should
455 * change interface later to not ask for current status, just set
458 cp = ioctlp->outDatap;
459 memcpy(cp, (char *)&volStat, sizeof(VolumeStatus));
460 cp += sizeof(VolumeStatus);
462 cp += strlen(volName)+1;
463 strcpy(cp, offLineMsg);
464 cp += strlen(offLineMsg)+1;
466 cp += strlen(motd)+1;
468 /* now return updated return data pointer */
469 ioctlp->outDatap = cp;
474 long cm_IoctlGetVolumeStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
478 char offLineMsg[256];
482 AFSFetchVolumeStatus volStat;
491 code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
492 if (code) return code;
495 OfflineMsg = offLineMsg;
498 code = cm_Conn(&scp->fid, userp, &req, &tcp);
501 code = RXAFS_GetVolumeStatus(tcp->callp, scp->fid.volume,
502 &volStat, &Name, &OfflineMsg, &MOTD);
503 } while (cm_Analyze(tcp, userp, &req, &scp->fid, NULL, NULL, code));
504 code = cm_MapRPCError(code, &req);
506 cm_ReleaseSCache(scp);
507 if (code) return code;
509 /* Copy all this junk into msg->im_data, keeping track of the lengths. */
510 cp = ioctlp->outDatap;
511 memcpy(cp, (char *)&volStat, sizeof(AFSFetchVolumeStatus));
512 cp += sizeof(AFSFetchVolumeStatus);
514 cp += strlen(volName)+1;
515 strcpy(cp, offLineMsg);
516 cp += strlen(offLineMsg)+1;
518 cp += strlen(motd)+1;
520 /* return new size */
521 ioctlp->outDatap = cp;
526 long cm_IoctlWhereIs(struct smb_ioctl *ioctlp, struct cm_user *userp)
532 cm_serverRef_t *tsrp;
534 unsigned long volume;
540 code = cm_ParseIoctlPath(ioctlp, userp, &req, &scp);
541 if (code) return code;
543 volume = scp->fid.volume;
545 cellp = cm_FindCellByID(scp->fid.cell);
548 cm_ReleaseSCache(scp);
550 code = cm_GetVolumeByID(cellp, volume, userp, &req, &tvp);
551 if (code) return code;
553 cp = ioctlp->outDatap;
555 lock_ObtainMutex(&tvp->mx);
556 tsrp = cm_GetVolServers(tvp, volume);
557 lock_ObtainRead(&cm_serverLock);
560 memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
564 lock_ReleaseRead(&cm_serverLock);
565 lock_ReleaseMutex(&tvp->mx);
567 /* still room for terminating NULL, add it on */
568 volume = 0; /* reuse vbl */
569 memcpy(cp, (char *)&volume, sizeof(long));
572 ioctlp->outDatap = cp;
577 long cm_IoctlStatMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
587 code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
588 if (code) return code;
590 cp = ioctlp->inDatap;
592 code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
593 cm_ReleaseSCache(dscp);
594 if (code) return code;
596 lock_ObtainMutex(&scp->mx);
598 /* now check that this is a real mount point */
599 if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
600 lock_ReleaseMutex(&scp->mx);
601 cm_ReleaseSCache(scp);
602 return CM_ERROR_INVAL;
605 code = cm_ReadMountPoint(scp, userp, &req);
607 cp = ioctlp->outDatap;
608 strcpy(cp, scp->mountPointStringp);
609 cp += strlen(cp) + 1;
610 ioctlp->outDatap = cp;
612 lock_ReleaseMutex(&scp->mx);
613 cm_ReleaseSCache(scp);
618 long cm_IoctlDeleteMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
628 code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
629 if (code) return code;
631 cp = ioctlp->inDatap;
633 code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
635 /* if something went wrong, bail out now */
640 lock_ObtainMutex(&scp->mx);
641 code = cm_SyncOp(scp, NULL, userp, &req, 0,
642 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
644 lock_ReleaseMutex(&scp->mx);
645 cm_ReleaseSCache(scp);
649 /* now check that this is a real mount point */
650 if (scp->fileType != CM_SCACHETYPE_MOUNTPOINT) {
651 lock_ReleaseMutex(&scp->mx);
652 cm_ReleaseSCache(scp);
653 code = CM_ERROR_INVAL;
657 /* time to make the RPC, so drop the lock */
658 lock_ReleaseMutex(&scp->mx);
659 cm_ReleaseSCache(scp);
661 /* easier to do it this way */
662 code = cm_Unlink(dscp, cp, userp, &req);
663 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
664 smb_NotifyChange(FILE_ACTION_REMOVED,
665 FILE_NOTIFY_CHANGE_DIR_NAME,
666 dscp, cp, NULL, TRUE);
669 cm_ReleaseSCache(dscp);
673 long cm_IoctlCheckServers(struct smb_ioctl *ioctlp, struct cm_user *userp)
683 cm_SkipIoctlPath(ioctlp); /* we don't care about the path */
684 tp = ioctlp->inDatap;
687 memcpy(&temp, tp, sizeof(temp));
688 if (temp == 0x12345678) { /* For afs3.3 version */
689 memcpy(&csi, tp, sizeof(csi));
690 if (csi.tinterval >= 0) {
691 cp = ioctlp->outDatap;
692 memcpy(cp, (char *)&cm_daemonCheckInterval, sizeof(long));
693 ioctlp->outDatap += sizeof(long);
694 if (csi.tinterval > 0) {
695 if (!smb_SUser(userp))
696 return CM_ERROR_NOACCESS;
697 cm_daemonCheckInterval = csi.tinterval;
705 } else { /* For pre afs3.3 versions */
706 memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
707 ioctlp->inDatap = cp = ioctlp->inDatap + sizeof(long);
708 if (cp - ioctlp->inAllocp < ioctlp->inCopied) /* still more data available */
713 * 1: fast check, don't contact servers.
714 * 2: local cell only.
717 /* have cell name, too */
718 cellp = cm_GetCell(cp, 0);
719 if (!cellp) return CM_ERROR_NOSUCHCELL;
721 else cellp = (cm_cell_t *) 0;
722 if (!cellp && (temp & 2)) {
724 cellp = cm_FindCellByID(1);
726 if (!(temp & 1)) { /* if not fast, call server checker routine */
727 /* check down servers */
728 cm_CheckServers(CM_FLAG_CHECKDOWNSERVERS | CM_FLAG_CHECKUPSERVERS,
732 /* now return the current down server list */
733 cp = ioctlp->outDatap;
734 lock_ObtainRead(&cm_serverLock);
735 for(tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
736 if (cellp && tsp->cellp != cellp) continue; /* cell spec'd and wrong */
737 if ((tsp->flags & CM_SERVERFLAG_DOWN)
738 && tsp->type == CM_SERVER_FILE) {
739 memcpy(cp, (char *)&tsp->addr.sin_addr.s_addr, sizeof(long));
743 lock_ReleaseRead(&cm_serverLock);
745 ioctlp->outDatap = cp;
749 long cm_IoctlGag(struct smb_ioctl *ioctlp, struct cm_user *userp)
751 /* we don't print anything superfluous, so we don't support the gag call */
752 return CM_ERROR_INVAL;
755 long cm_IoctlCheckVolumes(struct smb_ioctl *ioctlp, struct cm_user *userp)
761 long cm_IoctlSetCacheSize(struct smb_ioctl *ioctlp, struct cm_user *userp)
766 cm_SkipIoctlPath(ioctlp);
768 memcpy(&temp, ioctlp->inDatap, sizeof(temp));
769 if (temp == 0) temp = buf_nOrigBuffers;
771 /* temp is in 1K units, convert to # of buffers */
772 temp = temp / (buf_bufferSize / 1024);
775 /* now adjust the cache size */
776 code = buf_SetNBuffers(temp);
781 long cm_IoctlTraceControl(struct smb_ioctl *ioctlp, struct cm_user *userp)
785 cm_SkipIoctlPath(ioctlp);
787 memcpy(&inValue, ioctlp->inDatap, sizeof(long));
791 afsd_ForceTrace(FALSE);
795 /* set tracing value to low order bit */
796 if ((inValue & 1) == 0) {
797 /* disable tracing */
798 osi_LogDisable(afsd_logp);
802 osi_LogEnable(afsd_logp);
806 /* see if we're supposed to do a reset, too */
808 osi_LogReset(afsd_logp);
811 /* and copy out tracing flag */
812 inValue = afsd_logp->enabled; /* use as a temp vbl */
813 memcpy(ioctlp->outDatap, &inValue, sizeof(long));
814 ioctlp->outDatap += sizeof(long);
818 long cm_IoctlGetCacheParms(struct smb_ioctl *ioctlp, struct cm_user *userp)
820 cm_cacheParms_t parms;
822 memset(&parms, 0, sizeof(parms));
824 /* first we get, in 1K units, the cache size */
825 parms.parms[0] = buf_nbuffers * (buf_bufferSize / 1024);
827 /* and then the actual # of buffers in use (not in the free list, I guess,
828 * will be what we do).
830 parms.parms[1] = (buf_nbuffers - buf_CountFreeList()) * (buf_bufferSize / 1024);
832 memcpy(ioctlp->outDatap, &parms, sizeof(parms));
833 ioctlp->outDatap += sizeof(parms);
838 long cm_IoctlGetCell(struct smb_ioctl *ioctlp, struct cm_user *userp)
843 cm_serverRef_t *serverRefp;
844 cm_server_t *serverp;
850 cm_SkipIoctlPath(ioctlp);
852 tp = ioctlp->inDatap;
854 memcpy((char *)&whichCell, tp, sizeof(long));
857 /* see if more than one long passed in, ignoring the null pathname (the -1) */
858 if (ioctlp->inCopied-1 > sizeof(long)) {
859 memcpy((char *)&magic, tp, sizeof(long));
862 lock_ObtainRead(&cm_cellLock);
863 for(tcellp = cm_allCellsp; tcellp; tcellp = tcellp->nextp) {
864 if (whichCell == 0) break;
867 lock_ReleaseRead(&cm_cellLock);
871 cp = ioctlp->outDatap;
873 if (magic == 0x12345678) {
874 memcpy(cp, (char *)&magic, sizeof(long));
877 memset(cp, 0, max * sizeof(long));
879 lock_ObtainRead(&cm_serverLock); /* for going down server list */
880 serverRefp = tcellp->vlServersp;
881 for(i=0; i<max; i++) {
882 if (!serverRefp) break;
883 serverp = serverRefp->server;
884 memcpy(cp, &serverp->addr.sin_addr.s_addr, sizeof(long));
886 serverRefp = serverRefp->next;
888 lock_ReleaseRead(&cm_serverLock);
889 cp = basep + max * sizeof(afs_int32);
890 strcpy(cp, tcellp->namep);
891 cp += strlen(tcellp->namep)+1;
892 ioctlp->outDatap = cp;
895 if (tcellp) return 0;
896 else return CM_ERROR_NOMORETOKENS; /* mapped to EDOM */
899 long cm_IoctlNewCell(struct smb_ioctl *ioctlp, struct cm_user *userp)
901 /* don't need to do, since NT cache manager will re-read afsdcell.ini
902 * on every access to a new cell.
904 return CM_ERROR_INVAL;
907 long cm_IoctlGetWsCell(smb_ioctl_t *ioctlp, cm_user_t *userp)
909 /* if we don't know our default cell, return failure */
910 if (cm_rootCellp == NULL) {
911 return CM_ERROR_NOSUCHCELL;
914 /* return the default cellname to the caller */
915 strcpy(ioctlp->outDatap, cm_rootCellp->namep);
916 ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
922 long cm_IoctlSysName(struct smb_ioctl *ioctlp, struct cm_user *userp)
927 cm_SkipIoctlPath(ioctlp);
929 memcpy(&setSysName, ioctlp->inDatap, sizeof(long));
930 ioctlp->inDatap += sizeof(long);
933 strcpy(cm_sysName, ioctlp->inDatap);
936 /* return the sysname to the caller */
937 setSysName = 1; /* really means "found sys name */
938 cp = ioctlp->outDatap;
939 memcpy(cp, &setSysName, sizeof(long));
940 cp += sizeof(long); /* skip found flag */
941 strcpy(cp, cm_sysName);
942 cp += strlen(cp) + 1; /* skip name and terminating null char */
943 ioctlp->outDatap = cp;
950 long cm_IoctlGetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
955 cm_SkipIoctlPath(ioctlp);
957 cellp = cm_GetCell(ioctlp->inDatap, 0);
958 if (!cellp) return CM_ERROR_NOSUCHCELL;
961 lock_ObtainMutex(&cellp->mx);
962 if (cellp->flags & CM_CELLFLAG_SUID)
963 temp |= CM_SETCELLFLAG_SUID;
964 lock_ReleaseMutex(&cellp->mx);
966 /* now copy out parm */
967 memcpy(ioctlp->outDatap, &temp, sizeof(long));
968 ioctlp->outDatap += sizeof(long);
973 long cm_IoctlSetCellStatus(struct smb_ioctl *ioctlp, struct cm_user *userp)
978 cm_SkipIoctlPath(ioctlp);
980 cellp = cm_GetCell(ioctlp->inDatap + 2*sizeof(long), 0);
981 if (!cellp) return CM_ERROR_NOSUCHCELL;
983 memcpy((char *)&temp, ioctlp->inDatap, sizeof(long));
985 lock_ObtainMutex(&cellp->mx);
986 if (temp & CM_SETCELLFLAG_SUID)
987 cellp->flags |= CM_CELLFLAG_SUID;
989 cellp->flags &= ~CM_CELLFLAG_SUID;
990 lock_ReleaseMutex(&cellp->mx);
995 long cm_IoctlSetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
997 cm_SSetPref_t *spin; /* input */
998 cm_SPref_t *srvin; /* one input component */
1000 int i, vlonly, noServers, type;
1001 struct sockaddr_in tmp;
1002 unsigned short rank;
1004 cm_SkipIoctlPath(ioctlp); /* we don't care about the path */
1006 spin = (cm_SSetPref_t *)ioctlp->inDatap;
1007 noServers = spin->num_servers;
1008 vlonly = spin->flags;
1010 type = CM_SERVER_VLDB;
1011 else type = CM_SERVER_FILE;
1013 for ( i=0; i < noServers; i++)
1015 srvin = &(spin->servers[i]);
1016 rank = srvin->rank + (rand() & 0x000f);
1017 tmp.sin_addr = srvin->host;
1018 tmp.sin_family = AF_INET;
1020 tsp = cm_FindServer(&tmp, type);
1021 if ( tsp ) /* an existing server */
1023 tsp->ipRank = rank; /* no need to protect by mutex*/
1025 if ( type == CM_SERVER_FILE) /* fileserver */
1027 /* find volumes which might have RO copy
1028 /* on server and change the ordering of
1030 cm_ChangeRankVolume(tsp);
1034 /* set preferences for an existing vlserver */
1035 cm_ChangeRankCellVLServer(tsp);
1038 else /* add a new server without a cell*/
1040 tsp = cm_NewServer(&tmp, type, NULL);
1048 long cm_IoctlGetSPrefs(struct smb_ioctl *ioctlp, struct cm_user *userp)
1050 cm_SPrefRequest_t *spin; /* input */
1051 cm_SPrefInfo_t *spout; /* output */
1052 cm_SPref_t *srvout; /* one output component */
1054 int i, vlonly, noServers;
1056 cm_SkipIoctlPath(ioctlp); /* we don't care about the path */
1058 spin = (cm_SPrefRequest_t *)ioctlp->inDatap;
1059 spout = (cm_SPrefInfo_t *) ioctlp->outDatap;
1060 srvout = spout->servers;
1061 noServers = spin->num_servers;
1062 vlonly = spin->flags & CM_SPREF_VLONLY;
1063 spout->num_servers = 0;
1065 lock_ObtainRead(&cm_serverLock); /* get server lock */
1067 for(tsp=cm_allServersp, i=0; tsp && noServers; tsp=tsp->allNextp,i++){
1068 if (spin->offset > i) {
1069 continue; /* catch up to where we left off */
1072 if ( vlonly && (tsp->type == CM_SERVER_FILE) )
1073 continue; /* ignore fileserver for -vlserver option*/
1074 if ( !vlonly && (tsp->type == CM_SERVER_VLDB) )
1075 continue; /* ignore vlservers */
1077 srvout->host = tsp->addr.sin_addr;
1078 srvout->rank = tsp->ipRank;
1080 spout->num_servers++;
1083 lock_ReleaseRead(&cm_serverLock); /* release server lock */
1085 if ( tsp ) /* we ran out of space in the output buffer */
1086 spout->next_offset = i;
1088 spout->next_offset = 0;
1089 ioctlp->outDatap += sizeof(cm_SPrefInfo_t) +
1090 (spout->num_servers -1 ) * sizeof(cm_SPref_t) ;
1094 long cm_IoctlStoreBehind(struct smb_ioctl *ioctlp, struct cm_user *userp)
1096 /* we ignore default asynchrony since we only have one way
1097 * of doing this today.
1102 long cm_IoctlCreateMountPoint(struct smb_ioctl *ioctlp, struct cm_user *userp)
1117 code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
1118 if (code) return code;
1120 /* Translate chars for the mount point name */
1121 TranslateExtendedChars(leaf);
1124 * The fs command allows the user to specify partial cell names on NT. These must
1125 * be expanded to the full cell name for mount points so that the mount points will
1126 * work on UNIX clients.
1129 /* Extract the possibly partial cell name */
1130 strcpy(cell, ioctlp->inDatap + 1); /* Skip the mp type character */
1132 if (cp = strchr(cell, ':')) {
1133 /* Extract the volume name */
1135 strcpy(volume, cp + 1);
1137 /* Get the full name for this cell */
1138 code = cm_SearchCellFile(cell, fullCell, 0, 0);
1140 return CM_ERROR_NOSUCHCELL;
1142 sprintf(mpInfo, "%c%s:%s", *ioctlp->inDatap, fullCell, volume);
1144 /* No cell name specified */
1145 strcpy(mpInfo, ioctlp->inDatap);
1148 /* create the symlink with mode 644. The lack of X bits tells
1149 * us that it is a mount point.
1151 tattr.mask = CM_ATTRMASK_UNIXMODEBITS | CM_ATTRMASK_CLIENTMODTIME;
1152 tattr.unixModeBits = 0644;
1153 tattr.clientModTime = time(NULL);
1155 code = cm_SymLink(dscp, leaf, mpInfo, 0, &tattr, userp, &req);
1156 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1157 smb_NotifyChange(FILE_ACTION_ADDED,
1158 FILE_NOTIFY_CHANGE_DIR_NAME,
1159 dscp, leaf, NULL, TRUE);
1161 cm_ReleaseSCache(dscp);
1166 long cm_IoctlSymlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
1177 code = cm_ParseIoctlParent(ioctlp, userp, &req, &dscp, leaf);
1178 if (code) return code;
1180 /* Translate chars for the link name */
1181 TranslateExtendedChars(leaf);
1183 /* Translate chars for the linked to name */
1184 TranslateExtendedChars(ioctlp->inDatap);
1186 cp = ioctlp->inDatap; /* contents of link */
1188 /* Create symlink with mode 0755. */
1189 tattr.mask = CM_ATTRMASK_UNIXMODEBITS;
1190 tattr.unixModeBits = 0755;
1192 code = cm_SymLink(dscp, leaf, cp, 0, &tattr, userp, &req);
1193 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1194 smb_NotifyChange(FILE_ACTION_ADDED,
1195 FILE_NOTIFY_CHANGE_FILE_NAME
1196 | FILE_NOTIFY_CHANGE_DIR_NAME,
1197 dscp, leaf, NULL, TRUE);
1199 cm_ReleaseSCache(dscp);
1204 extern long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1205 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1206 cm_user_t *userp, cm_req_t *reqp);
1208 long cm_IoctlListlink(struct smb_ioctl *ioctlp, struct cm_user *userp)
1215 cm_scache_t *newRootScp;
1220 code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
1221 if (code) return code;
1223 cp = ioctlp->inDatap;
1225 code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
1226 cm_ReleaseSCache(dscp);
1227 if (code) return code;
1229 /* Check that it's a real symlink */
1230 if (scp->fileType != CM_SCACHETYPE_SYMLINK){
1231 cm_ReleaseSCache(scp);
1232 return CM_ERROR_INVAL;
1235 code = cm_AssembleLink(scp, "", &newRootScp, &spacep, userp, &req);
1236 cm_ReleaseSCache(scp);
1238 cp = ioctlp->outDatap;
1239 if (newRootScp != NULL) {
1240 strcpy(cp, "/afs/");
1243 strcpy(cp, spacep->data);
1244 cp += strlen(cp) + 1;
1245 ioctlp->outDatap = cp;
1246 cm_FreeSpace(spacep);
1247 if (newRootScp != NULL)
1248 cm_ReleaseSCache(newRootScp);
1254 long cm_IoctlDeletelink(struct smb_ioctl *ioctlp, struct cm_user *userp)
1264 code = cm_ParseIoctlPath(ioctlp, userp, &req, &dscp);
1265 if (code) return code;
1267 cp = ioctlp->inDatap;
1269 code = cm_Lookup(dscp, cp, CM_FLAG_NOMOUNTCHASE, userp, &req, &scp);
1271 /* if something went wrong, bail out now */
1276 lock_ObtainMutex(&scp->mx);
1277 code = cm_SyncOp(scp, NULL, userp, &req, 0,
1278 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1280 lock_ReleaseMutex(&scp->mx);
1281 cm_ReleaseSCache(scp);
1285 /* now check that this is a real symlink */
1286 if (scp->fileType != CM_SCACHETYPE_SYMLINK) {
1287 lock_ReleaseMutex(&scp->mx);
1288 cm_ReleaseSCache(scp);
1289 code = CM_ERROR_INVAL;
1293 /* time to make the RPC, so drop the lock */
1294 lock_ReleaseMutex(&scp->mx);
1295 cm_ReleaseSCache(scp);
1297 /* easier to do it this way */
1298 code = cm_Unlink(dscp, cp, userp, &req);
1299 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1300 smb_NotifyChange(FILE_ACTION_REMOVED,
1301 FILE_NOTIFY_CHANGE_FILE_NAME
1302 | FILE_NOTIFY_CHANGE_DIR_NAME,
1303 dscp, cp, NULL, TRUE);
1306 cm_ReleaseSCache(dscp);
1310 long cm_IoctlSetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
1317 struct ClearToken ct;
1325 saveDataPtr = ioctlp->inDatap;
1327 cm_SkipIoctlPath(ioctlp);
1329 tp = ioctlp->inDatap;
1332 memcpy(&ticketLen, tp, sizeof(ticketLen));
1333 tp += sizeof(ticketLen);
1334 if (ticketLen < MINKTCTICKETLEN || ticketLen > MAXKTCTICKETLEN)
1335 return CM_ERROR_INVAL;
1337 /* remember ticket and skip over it for now */
1341 /* clear token size */
1342 memcpy(&ctSize, tp, sizeof(ctSize));
1343 tp += sizeof(ctSize);
1344 if (ctSize != sizeof(struct ClearToken))
1345 return CM_ERROR_INVAL;
1348 memcpy(&ct, tp, ctSize);
1350 if (ct.AuthHandle == -1)
1351 ct.AuthHandle = 999; /* more rxvab compat stuff */
1353 /* more stuff, if any */
1354 if (ioctlp->inCopied > tp - saveDataPtr) {
1355 /* flags: logon flag */
1356 memcpy(&flags, tp, sizeof(int));
1360 cellp = cm_GetCell(tp, CM_FLAG_CREATE);
1361 if (!cellp) return CM_ERROR_NOSUCHCELL;
1362 tp += strlen(tp) + 1;
1366 tp += strlen(tp) + 1;
1369 memcpy(&uuid, tp, sizeof(uuid));
1370 if (!cm_FindTokenEvent(uuid, sessionKey))
1371 return CM_ERROR_INVAL;
1373 cellp = cm_rootCellp;
1375 /* store the token */
1376 lock_ObtainMutex(&userp->mx);
1377 ucellp = cm_GetUCell(userp, cellp);
1378 ucellp->ticketLen = ticketLen;
1379 if (ucellp->ticketp)
1380 free(ucellp->ticketp); /* Discard old token if any */
1381 ucellp->ticketp = malloc(ticketLen);
1382 memcpy(ucellp->ticketp, ticket, ticketLen);
1384 * Get the session key from the RPC, rather than from the pioctl.
1387 memcpy(&ucellp->sessionKey, ct.HandShakeKey, sizeof(ct.HandShakeKey));
1389 memcpy(ucellp->sessionKey.data, sessionKey, sizeof(sessionKey));
1390 ucellp->kvno = ct.AuthHandle;
1391 ucellp->expirationTime = ct.EndTimestamp;
1393 if (uname) strcpy(ucellp->userName, uname);
1394 ucellp->flags |= CM_UCELLFLAG_RXKAD;
1395 lock_ReleaseMutex(&userp->mx);
1397 if (flags & PIOCTL_LOGON) {
1398 ioctlp->flags |= SMB_IOCTLFLAG_LOGON;
1401 cm_ResetACLCache(userp);
1406 long cm_IoctlGetTokenIter(struct smb_ioctl *ioctlp, struct cm_user *userp)
1412 struct ClearToken ct;
1414 cm_SkipIoctlPath(ioctlp);
1416 tp = ioctlp->inDatap;
1417 cp = ioctlp->outDatap;
1420 memcpy(&iterator, tp, sizeof(iterator));
1421 tp += sizeof(iterator);
1423 lock_ObtainMutex(&userp->mx);
1425 /* look for token */
1426 for (;;iterator++) {
1427 ucellp = cm_FindUCell(userp, iterator);
1429 lock_ReleaseMutex(&userp->mx);
1430 return CM_ERROR_NOMORETOKENS;
1432 if (ucellp->flags & CM_UCELLFLAG_RXKAD)
1437 temp = ucellp->iterator + 1;
1438 memcpy(cp, &temp, sizeof(temp));
1442 memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
1443 cp += sizeof(ucellp->ticketLen);
1446 memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
1447 cp += ucellp->ticketLen;
1449 /* clear token size */
1451 memcpy(cp, &temp, sizeof(temp));
1455 ct.AuthHandle = ucellp->kvno;
1457 * Don't give out a real session key here
1460 memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
1462 memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
1463 ct.ViceId = 37; /* XXX */
1464 ct.BeginTimestamp = 0; /* XXX */
1465 ct.EndTimestamp = ucellp->expirationTime;
1466 memcpy(cp, &ct, sizeof(ct));
1469 /* Primary flag (unused) */
1471 memcpy(cp, &temp, sizeof(temp));
1475 strcpy(cp, ucellp->cellp->namep);
1476 cp += strlen(cp) + 1;
1479 strcpy(cp, ucellp->userName);
1480 cp += strlen(cp) + 1;
1482 ioctlp->outDatap = cp;
1484 lock_ReleaseMutex(&userp->mx);
1489 long cm_IoctlGetToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
1495 struct ClearToken ct;
1499 cm_SkipIoctlPath(ioctlp);
1501 tp = ioctlp->inDatap;
1503 cp = ioctlp->outDatap;
1505 /* cell name is right here */
1506 cellp = cm_GetCell(tp, 0);
1507 if (!cellp) return CM_ERROR_NOSUCHCELL;
1508 tp += strlen(tp) + 1;
1511 memcpy(&uuid, tp, sizeof(uuid));
1513 lock_ObtainMutex(&userp->mx);
1515 ucellp = cm_GetUCell(userp, cellp);
1516 if (!ucellp || !(ucellp->flags & CM_UCELLFLAG_RXKAD)) {
1517 lock_ReleaseMutex(&userp->mx);
1518 return CM_ERROR_NOMORETOKENS;
1522 memcpy(cp, &ucellp->ticketLen, sizeof(ucellp->ticketLen));
1523 cp += sizeof(ucellp->ticketLen);
1526 memcpy(cp, ucellp->ticketp, ucellp->ticketLen);
1527 cp += ucellp->ticketLen;
1529 /* clear token size */
1531 memcpy(cp, &temp, sizeof(temp));
1535 ct.AuthHandle = ucellp->kvno;
1537 * Don't give out a real session key here
1540 memcpy(ct.HandShakeKey, &ucellp->sessionKey, sizeof(ct.HandShakeKey));
1542 memset(ct.HandShakeKey, 0, sizeof(ct.HandShakeKey));
1543 ct.ViceId = 37; /* XXX */
1544 ct.BeginTimestamp = 0; /* XXX */
1545 ct.EndTimestamp = ucellp->expirationTime;
1546 memcpy(cp, &ct, sizeof(ct));
1549 /* Primary flag (unused) */
1551 memcpy(cp, &temp, sizeof(temp));
1555 strcpy(cp, ucellp->cellp->namep);
1556 cp += strlen(cp) + 1;
1559 strcpy(cp, ucellp->userName);
1560 cp += strlen(cp) + 1;
1562 ioctlp->outDatap = cp;
1564 lock_ReleaseMutex(&userp->mx);
1566 cm_RegisterNewTokenEvent(uuid, ucellp->sessionKey.data);
1571 long cm_IoctlDelToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
1577 cm_SkipIoctlPath(ioctlp);
1579 cp = ioctlp->outDatap;
1581 /* cell name is right here */
1582 cellp = cm_GetCell(ioctlp->inDatap, 0);
1583 if (!cellp) return CM_ERROR_NOSUCHCELL;
1585 lock_ObtainMutex(&userp->mx);
1587 ucellp = cm_GetUCell(userp, cellp);
1589 lock_ReleaseMutex(&userp->mx);
1590 return CM_ERROR_NOMORETOKENS;
1593 if (ucellp->ticketp) {
1594 free(ucellp->ticketp);
1595 ucellp->ticketp = NULL;
1597 ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
1600 lock_ReleaseMutex(&userp->mx);
1602 cm_ResetACLCache(userp);
1607 long cm_IoctlDelAllToken(struct smb_ioctl *ioctlp, struct cm_user *userp)
1611 lock_ObtainMutex(&userp->mx);
1613 for (ucellp = userp->cellInfop; ucellp; ucellp = ucellp->nextp) {
1614 ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
1618 lock_ReleaseMutex(&userp->mx);
1620 cm_ResetACLCache(userp);
1625 long cm_IoctlMakeSubmount(smb_ioctl_t *ioctlp, cm_user_t *userp)
1627 char afspath[MAX_PATH];
1630 int submountDataSize;
1633 int nextAutoSubmount;
1635 cm_SkipIoctlPath(ioctlp);
1637 /* Serialize this one, to prevent simultaneous mods
1640 lock_ObtainMutex(&cm_Afsdsbmt_Lock);
1642 /* Parse the input parameters--first the required afs path,
1643 * then the requested submount name (which may be "").
1645 cm_NormalizeAfsPath (afspath, ioctlp->inDatap);
1646 submountreqp = ioctlp->inDatap + (strlen(ioctlp->inDatap)+1);
1648 /* If the caller supplied a suggested submount name, see if
1649 * that submount name is in use... if so, the submount's path
1650 * has to match our path.
1652 if (submountreqp && *submountreqp) {
1653 char submountPathNormalized[MAX_PATH];
1654 char submountPath[MAX_PATH];
1655 int submountPathLen;
1657 submountPathLen = GetPrivateProfileString("AFS Submounts",
1658 submountreqp, "", submountPath,
1659 sizeof(submountPath), "afsdsbmt.ini");
1661 if ((submountPathLen == 0) ||
1662 (submountPathLen == sizeof(submountPath) - 1)) {
1664 /* The suggested submount name isn't in use now--
1665 * so we can safely map the requested submount name
1666 * to the supplied path. Remember not to write the
1667 * leading "/afs" when writing out the submount.
1669 WritePrivateProfileString("AFS Submounts",
1670 submountreqp, &afspath[strlen("/afs")],
1673 strcpy(ioctlp->outDatap, submountreqp);
1674 ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1675 lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
1679 /* The suggested submount name is already in use--if the
1680 * supplied path matches the submount's path, we can still
1681 * use the suggested submount name.
1683 cm_NormalizeAfsPath (submountPathNormalized, submountPath);
1684 if (!strcmp (submountPathNormalized, afspath)) {
1685 strcpy(ioctlp->outDatap, submountreqp);
1686 ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1687 lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
1692 /* At this point, the user either didn't request a particular
1693 * submount name, or that submount name couldn't be used.
1694 * Look through afsdsbmt.ini to see if there are any submounts
1695 * already associated with the specified path. The first
1696 * step in doing that search is to load the AFS Submounts
1697 * section of afsdsbmt.ini into memory.
1700 submountDataSize = 1024;
1701 submountData = malloc (submountDataSize);
1703 for (iteration = 0; iteration < 5; ++iteration) {
1706 sectionSize = GetPrivateProfileString("AFS Submounts",
1707 NULL, "", submountData,
1708 submountDataSize, "afsdsbmt.ini");
1709 if (sectionSize < submountDataSize-2)
1712 free (submountData);
1713 submountDataSize *= 2;
1714 submountData = malloc (submountDataSize);
1717 /* Having obtained a list of all available submounts, start
1718 * searching that list for a path which matches the requested
1719 * AFS path. We'll also keep track of the highest "auto15"/"auto47"
1720 * submount, in case we need to add a new one later.
1723 nextAutoSubmount = 1;
1725 for (submountName = submountData;
1726 submountName && *submountName;
1727 submountName += 1+strlen(submountName)) {
1729 char submountPathNormalized[MAX_PATH];
1730 char submountPath[MAX_PATH] = "";
1731 int submountPathLen;
1733 /* If this is an Auto### submount, remember its ### value */
1735 if ((!strnicmp (submountName, "auto", 4)) &&
1736 (isdigit (submountName[strlen("auto")]))) {
1737 int thisAutoSubmount;
1738 thisAutoSubmount = atoi (&submountName[strlen("auto")]);
1739 nextAutoSubmount = max (nextAutoSubmount,
1740 thisAutoSubmount+1);
1743 /* We have the name of a submount in the AFS Submounts
1744 * section; read that entry to find out what path it
1747 submountPathLen = GetPrivateProfileString("AFS Submounts",
1748 submountName, "", submountPath,
1749 sizeof(submountPath), "afsdsbmt.ini");
1751 if ((submountPathLen == 0) ||
1752 (submountPathLen == sizeof(submountPath) - 1)) {
1756 /* See if the path for this submount matches the path
1757 * that our caller specified. If so, we can return
1760 cm_NormalizeAfsPath (submountPathNormalized, submountPath);
1761 if (!strcmp (submountPathNormalized, afspath)) {
1763 strcpy(ioctlp->outDatap, submountName);
1764 ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1765 free (submountData);
1766 lock_ReleaseMutex(&cm_Afsdsbmt_Lock);
1772 free (submountData);
1774 /* We've been through the entire list of existing submounts, and
1775 * didn't find any which matched the specified path. So, we'll
1776 * just have to add one. Remember not to write the leading "/afs"
1777 * when writing out the submount.
1780 sprintf(ioctlp->outDatap, "auto%ld", nextAutoSubmount);
1782 WritePrivateProfileString("AFS Submounts", ioctlp->outDatap,
1783 &afspath[lstrlen("/afs")],
1786 ioctlp->outDatap += strlen(ioctlp->outDatap) +1;
1787 lock_ReleaseMutex(&cm_Afsdsbmt_Lock);