2 ** Apple Macintosh Developer Technical Support
4 ** A collection of useful high-level File Manager routines.
6 ** by Jim Luther, Apple Developer Technical Support Emeritus
8 ** File: MoreFilesExtras.c
10 ** Copyright © 1992-1998 Apple Computer, Inc.
11 ** All rights reserved.
13 ** You may incorporate this sample code into your applications without
14 ** restriction, though the sample code has been provided "AS IS" and the
15 ** responsibility for its operation is 100% yours. However, what you are
16 ** not permitted to do is to redistribute the source as "DSC Sample Code"
17 ** after having made changes. If you're going to re-distribute the source,
18 ** we require that you make it clear in the source that the code was
19 ** descended from Apple Sample Code, but that you've made changes.
33 #include <TextUtils.h>
38 #define __COMPILINGMOREFILES
45 /*****************************************************************************/
47 /* local data structures */
49 /* The DeleteEnumGlobals structure is used to minimize the amount of
50 ** stack space used when recursively calling DeleteLevel and to hold
51 ** global information that might be needed at any time. */
53 #if PRAGMA_ALIGN_SUPPORTED
54 #pragma options align=mac68k
56 struct DeleteEnumGlobals
58 OSErr error
; /* temporary holder of results - saves 2 bytes of stack each level */
59 Str63 itemName
; /* the name of the current item */
60 UniversalFMPB myPB
; /* the parameter block used for PBGetCatInfo calls */
62 #if PRAGMA_ALIGN_SUPPORTED
63 #pragma options align=reset
66 typedef struct DeleteEnumGlobals DeleteEnumGlobals
;
67 typedef DeleteEnumGlobals
*DeleteEnumGlobalsPtr
;
69 /*****************************************************************************/
71 pascal void TruncPString(StringPtr destination
,
72 ConstStr255Param source
,
77 if ( source
!= NULL
&& destination
!= NULL
) /* don't do anything stupid */
79 if ( source
[0] > maxLength
)
81 /* Make sure the string isn't truncated in the middle of */
82 /* a multi-byte character. */
83 while (maxLength
!= 0)
85 charType
= CharByte((Ptr
)&source
[1], maxLength
);
86 if ( (charType
== smSingleByte
) || (charType
== smLastByte
) )
87 break; /* source[maxLength] is now a valid last character */
93 maxLength
= source
[0];
95 /* Set the destination string length */
96 destination
[0] = maxLength
;
97 /* and copy maxLength characters (if needed) */
98 if ( source
!= destination
)
100 while ( maxLength
!= 0 )
102 destination
[maxLength
] = source
[maxLength
];
109 /*****************************************************************************/
111 pascal Ptr
GetTempBuffer(long buffReqSize
,
116 kSlopMemory
= 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */
120 /* Make request a multiple of 1024 bytes */
121 buffReqSize
= buffReqSize
& 0xfffffc00;
123 if ( buffReqSize
< 0x00000400 )
125 /* Request was smaller than 1024 bytes - make it 1024 */
126 buffReqSize
= 0x00000400;
129 /* Attempt to allocate the memory */
130 tempPtr
= NewPtr(buffReqSize
);
132 /* If request failed, go to backup plan */
133 if ( (tempPtr
== NULL
) && (buffReqSize
> 0x00000400) )
136 ** Try to get largest 1024-byte block available
137 ** leaving some slop for the toolbox if possible
139 long freeMemory
= (FreeMem() - kSlopMemory
) & 0xfffffc00;
141 buffReqSize
= MaxBlock() & 0xfffffc00;
143 if ( buffReqSize
> freeMemory
)
145 buffReqSize
= freeMemory
;
148 if ( buffReqSize
== 0 )
150 buffReqSize
= 0x00000400;
153 tempPtr
= NewPtr(buffReqSize
);
156 /* Return bytes allocated */
157 if ( tempPtr
!= NULL
)
159 *buffActSize
= buffReqSize
;
169 /*****************************************************************************/
172 ** GetVolumeInfoNoName uses pathname and vRefNum to call PBHGetVInfoSync
173 ** in cases where the returned volume name is not needed by the caller.
174 ** The pathname and vRefNum parameters are not touched, and the pb
175 ** parameter is initialized by PBHGetVInfoSync except that ioNamePtr in
176 ** the parameter block is always returned as NULL (since it might point
177 ** to the local tempPathname).
179 ** I noticed using this code in several places, so here it is once.
180 ** This reduces the code size of MoreFiles.
182 pascal OSErr
GetVolumeInfoNoName(ConstStr255Param pathname
,
189 /* Make sure pb parameter is not NULL */
192 pb
->volumeParam
.ioVRefNum
= vRefNum
;
193 if ( pathname
== NULL
)
195 pb
->volumeParam
.ioNamePtr
= NULL
;
196 pb
->volumeParam
.ioVolIndex
= 0; /* use ioVRefNum only */
200 BlockMoveData(pathname
, tempPathname
, pathname
[0] + 1); /* make a copy of the string and */
201 pb
->volumeParam
.ioNamePtr
= (StringPtr
)tempPathname
; /* use the copy so original isn't trashed */
202 pb
->volumeParam
.ioVolIndex
= -1; /* use ioNamePtr/ioVRefNum combination */
204 error
= PBHGetVInfoSync(pb
);
205 pb
->volumeParam
.ioNamePtr
= NULL
; /* ioNamePtr may point to local tempPathname, so don't return it */
214 /*****************************************************************************/
217 ** XGetVolumeInfoNoName uses pathname and vRefNum to call PBXGetVolInfoSync
218 ** in cases where the returned volume name is not needed by the caller.
219 ** The pathname and vRefNum parameters are not touched, and the pb
220 ** parameter is initialized by PBXGetVolInfoSync except that ioNamePtr in
221 ** the parameter block is always returned as NULL (since it might point
222 ** to the local tempPathname).
224 pascal OSErr
XGetVolumeInfoNoName(ConstStr255Param pathname
,
232 /* Make sure pb parameter is not NULL */
235 pb
->ioVRefNum
= vRefNum
;
236 pb
->ioXVersion
= 0; /* this XVolumeParam version (0) */
237 if ( pathname
== NULL
)
239 pb
->ioNamePtr
= NULL
;
240 pb
->ioVolIndex
= 0; /* use ioVRefNum only */
244 BlockMoveData(pathname
, tempPathname
, pathname
[0] + 1); /* make a copy of the string and */
245 pb
->ioNamePtr
= (StringPtr
)tempPathname
; /* use the copy so original isn't trashed */
246 pb
->ioVolIndex
= -1; /* use ioNamePtr/ioVRefNum combination */
248 #if !__MACOSSEVENFIVEONEORLATER
249 /* Is PBXGetVolInfo available? */
250 if ( ( Gestalt(gestaltFSAttr
, &response
) != noErr
) || ((response
& (1L << gestaltFSSupports2TBVols
)) == 0) )
252 /* No, fall back on PBHGetVInfo */
253 error
= PBHGetVInfoSync((HParmBlkPtr
)pb
);
254 if ( error
== noErr
)
256 /* calculate the ioVTotalBytes and ioVFreeBytes fields */
257 pb
->ioVTotalBytes
.hi
= 0;
258 pb
->ioVTotalBytes
.lo
= pb
->ioVNmAlBlks
* pb
->ioVAlBlkSiz
; /* calculated total number of bytes on volume */
259 pb
->ioVFreeBytes
.hi
= 0;
260 pb
->ioVFreeBytes
.lo
= pb
->ioVFrBlk
* pb
->ioVAlBlkSiz
; /* calculated number of free bytes on volume */
264 #endif // !__MACOSSEVENFIVEONEORLATER
267 error
= PBXGetVolInfoSync(pb
);
269 pb
->ioNamePtr
= NULL
; /* ioNamePtr may point to local tempPathname, so don't return it */
278 /*****************************************************************************/
280 pascal OSErr
GetCatInfoNoName(short vRefNum
,
282 ConstStr255Param name
,
288 /* Protection against File Sharing problem */
289 if ( (name
== NULL
) || (name
[0] == 0) )
292 pb
->dirInfo
.ioNamePtr
= tempName
;
293 pb
->dirInfo
.ioFDirIndex
= -1; /* use ioDirID */
297 pb
->dirInfo
.ioNamePtr
= (StringPtr
)name
;
298 pb
->dirInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
300 pb
->dirInfo
.ioVRefNum
= vRefNum
;
301 pb
->dirInfo
.ioDrDirID
= dirID
;
302 error
= PBGetCatInfoSync(pb
);
303 pb
->dirInfo
.ioNamePtr
= NULL
;
307 /*****************************************************************************/
309 pascal OSErr
DetermineVRefNum(ConstStr255Param pathname
,
316 error
= GetVolumeInfoNoName(pathname
,vRefNum
, &pb
);
317 if ( error
== noErr
)
319 *realVRefNum
= pb
.volumeParam
.ioVRefNum
;
324 /*****************************************************************************/
326 pascal OSErr
HGetVInfo(short volReference
,
329 unsigned long *freeBytes
,
330 unsigned long *totalBytes
)
333 unsigned long allocationBlockSize
;
334 unsigned short numAllocationBlocks
;
335 unsigned short numFreeBlocks
;
340 /* Use the File Manager to get the real vRefNum */
341 pb
.volumeParam
.ioVRefNum
= volReference
;
342 pb
.volumeParam
.ioNamePtr
= volName
;
343 pb
.volumeParam
.ioVolIndex
= 0; /* use ioVRefNum only, return volume name */
344 result
= PBHGetVInfoSync(&pb
);
346 if ( result
== noErr
)
348 /* The volume name was returned in volName (if not NULL) and */
349 /* we have the volume's vRefNum and allocation block size */
350 *vRefNum
= pb
.volumeParam
.ioVRefNum
;
351 allocationBlockSize
= (unsigned long)pb
.volumeParam
.ioVAlBlkSiz
;
353 /* System 7.5 (and beyond) pins the number of allocation blocks and */
354 /* the number of free allocation blocks returned by PBHGetVInfo to */
355 /* a value so that when multiplied by the allocation block size, */
356 /* the volume will look like it has $7fffffff bytes or less. This */
357 /* was done so older applications that use signed math or that use */
358 /* the GetVInfo function (which uses signed math) will continue to work. */
359 /* However, the unpinned numbers (which we want) are always available */
360 /* in the volume's VCB so we'll get those values from the VCB if possible. */
362 /* Find the volume's VCB */
364 theVCB
= (VCB
*)(GetVCBQHdr()->qHead
);
365 while ( (theVCB
!= NULL
) && !vcbFound
)
367 /* Check VCB signature before using VCB. Don't have to check for */
368 /* MFS (0xd2d7) because they can't get big enough to be pinned */
369 if ( theVCB
->vcbSigWord
== 0x4244 )
371 if ( theVCB
->vcbVRefNum
== *vRefNum
)
379 theVCB
= (VCB
*)(theVCB
->qLink
);
383 if ( theVCB
!= NULL
)
385 /* Found a VCB we can use. Get the un-pinned number of allocation blocks */
386 /* and the number of free blocks from the VCB. */
387 numAllocationBlocks
= (unsigned short)theVCB
->vcbNmAlBlks
;
388 numFreeBlocks
= (unsigned short)theVCB
->vcbFreeBks
;
392 /* Didn't find a VCB we can use. Return the number of allocation blocks */
393 /* and the number of free blocks returned by PBHGetVInfoSync. */
394 numAllocationBlocks
= (unsigned short)pb
.volumeParam
.ioVNmAlBlks
;
395 numFreeBlocks
= (unsigned short)pb
.volumeParam
.ioVFrBlk
;
398 /* Now, calculate freeBytes and totalBytes using unsigned values */
399 *freeBytes
= numFreeBlocks
* allocationBlockSize
;
400 *totalBytes
= numAllocationBlocks
* allocationBlockSize
;
406 /*****************************************************************************/
409 ** PBXGetVolInfoSync is the glue code needed to make PBXGetVolInfoSync
410 ** File Manager requests from CFM-based programs. At some point, Apple
411 ** will get around to adding this to the standard libraries you link with
412 ** and you'll get a duplicate symbol link error. At that time, just delete
413 ** this code (or comment it out).
415 ** Non-CFM 68K programs don't needs this glue (and won't get it) because
416 ** they instead use the inline assembly glue found in the Files.h interface
420 #if __WANTPASCALELIMINATION
425 pascal OSErr
PBXGetVolInfoSync(XVolumeParamPtr paramBlock
)
429 kXGetVolInfoSelector
= 0x0012, /* Selector for XGetVolInfo */
431 uppFSDispatchProcInfo
= kRegisterBased
432 | REGISTER_RESULT_LOCATION(kRegisterD0
)
433 | RESULT_SIZE(SIZE_CODE(sizeof(OSErr
)))
434 | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1
, SIZE_CODE(sizeof(long))) /* trap word */
435 | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0
, SIZE_CODE(sizeof(long))) /* selector */
436 | REGISTER_ROUTINE_PARAMETER(3, kRegisterA0
, SIZE_CODE(sizeof(XVolumeParamPtr
)))
439 return ( CallOSTrapUniversalProc(NGetTrapAddress(_FSDispatch
, OSTrap
),
440 uppFSDispatchProcInfo
,
442 kXGetVolInfoSelector
,
447 #if __WANTPASCALELIMINATION
451 /*****************************************************************************/
453 pascal OSErr
XGetVInfo(short volReference
,
456 UnsignedWide
*freeBytes
,
457 UnsignedWide
*totalBytes
)
463 /* See if large volume support is available */
464 if ( ( Gestalt(gestaltFSAttr
, &response
) == noErr
) && ((response
& (1L << gestaltFSSupports2TBVols
)) != 0) )
466 /* Large volume support is available */
467 pb
.ioVRefNum
= volReference
;
468 pb
.ioNamePtr
= volName
;
469 pb
.ioXVersion
= 0; /* this XVolumeParam version (0) */
470 pb
.ioVolIndex
= 0; /* use ioVRefNum only, return volume name */
471 result
= PBXGetVolInfoSync(&pb
);
472 if ( result
== noErr
)
474 /* The volume name was returned in volName (if not NULL) and */
475 /* we have the volume's vRefNum and allocation block size */
476 *vRefNum
= pb
.ioVRefNum
;
478 /* return the freeBytes and totalBytes */
479 *totalBytes
= pb
.ioVTotalBytes
;
480 *freeBytes
= pb
.ioVFreeBytes
;
485 /* No large volume support */
487 /* Use HGetVInfo to get the results */
488 result
= HGetVInfo(volReference
, volName
, vRefNum
, &freeBytes
->lo
, &totalBytes
->lo
);
489 if ( result
== noErr
)
491 /* zero the high longs of totalBytes and freeBytes */
499 /*****************************************************************************/
501 pascal OSErr
CheckVolLock(ConstStr255Param pathname
,
507 error
= GetVolumeInfoNoName(pathname
,vRefNum
, &pb
);
508 if ( error
== noErr
)
510 if ( (pb
.volumeParam
.ioVAtrb
& 0x0080) != 0 )
512 error
= wPrErr
; /* volume locked by hardware */
514 else if ( (pb
.volumeParam
.ioVAtrb
& 0x8000) != 0 )
516 error
= vLckdErr
; /* volume locked by software */
523 /*****************************************************************************/
525 pascal OSErr
GetDriverName(short driverRefNum
,
530 DRVRHeaderPtr dHeaderPtr
;
532 theDctl
= GetDCtlEntry(driverRefNum
);
533 if ( theDctl
!= NULL
)
535 if ( (**theDctl
).dCtlFlags
& 0x40 )
537 /* dctlDriver is handle - dereference */
538 dHeaderPtr
= *((DRVRHeaderHandle
)(**theDctl
).dCtlDriver
);
542 /* dctlDriver is pointer */
543 dHeaderPtr
= (DRVRHeaderPtr
)(**theDctl
).dCtlDriver
;
545 BlockMoveData((*dHeaderPtr
).drvrName
, driverName
, (*dHeaderPtr
).drvrName
[0] + 1);
551 result
= badUnitErr
; /* bad reference number */
557 /*****************************************************************************/
559 pascal OSErr
FindDrive(ConstStr255Param pathname
,
561 DrvQElPtr
*driveQElementPtr
)
567 *driveQElementPtr
= NULL
;
569 /* First, use GetVolumeInfoNoName to determine the volume */
570 result
= GetVolumeInfoNoName(pathname
, vRefNum
, &hPB
);
571 if ( result
== noErr
)
574 ** The volume can be either online, offline, or ejected. What we find in
575 ** ioVDrvInfo and ioVDRefNum will tell us which it is.
576 ** See Inside Macintosh: Files page 2-80 and the Technical Note
577 ** "FL 34 - VCBs and Drive Numbers : The Real Story"
578 ** Where we get the drive number depends on the state of the volume.
580 if ( hPB
.volumeParam
.ioVDrvInfo
!= 0 )
582 /* The volume is online and not ejected */
583 /* Get the drive number */
584 driveNumber
= hPB
.volumeParam
.ioVDrvInfo
;
588 /* The volume's is either offline or ejected */
589 /* in either case, the volume is NOT online */
591 /* Is it ejected or just offline? */
592 if ( hPB
.volumeParam
.ioVDRefNum
> 0 )
594 /* It's ejected, the drive number is ioVDRefNum */
595 driveNumber
= hPB
.volumeParam
.ioVDRefNum
;
599 /* It's offline, the drive number is the negative of ioVDRefNum */
600 driveNumber
= (short)-hPB
.volumeParam
.ioVDRefNum
;
604 /* Get pointer to first element in drive queue */
605 *driveQElementPtr
= (DrvQElPtr
)(GetDrvQHdr()->qHead
);
607 /* Search for a matching drive number */
608 while ( (*driveQElementPtr
!= NULL
) && ((*driveQElementPtr
)->dQDrive
!= driveNumber
) )
610 *driveQElementPtr
= (DrvQElPtr
)(*driveQElementPtr
)->qLink
;
613 if ( *driveQElementPtr
== NULL
)
615 /* This should never happen since every volume must have a drive, but... */
623 /*****************************************************************************/
625 pascal OSErr
GetDiskBlocks(ConstStr255Param pathname
,
627 unsigned long *numBlocks
)
629 /* Various constants for GetDiskBlocks() */
632 /* return format list status code */
635 /* reference number of .SONY driver */
636 kSonyRefNum
= 0xfffb,
638 /* values returned by DriveStatus in DrvSts.twoSideFmt */
641 kSingleSidedSize
= 800, /* 400K */
642 kDoubleSidedSize
= 1600, /* 800K */
644 /* values in DrvQEl.qType */
648 /* more than enough formatListRecords */
649 kMaxFormatListRecs
= 16
652 DrvQElPtr driveQElementPtr
;
653 unsigned long blocks
;
655 FormatListRec formatListRecords
[kMaxFormatListRecs
];
657 short formatListRecIndex
;
662 /* Find the drive queue element for this volume */
663 result
= FindDrive(pathname
, vRefNum
, &driveQElementPtr
);
666 ** Make sure this is a real driver (dQRefNum < 0).
667 ** AOCE's Mail Enclosures volume uses 0 for dQRefNum which will cause
668 ** problems if you try to use it as a driver refNum.
670 if ( (result
== noErr
) && (driveQElementPtr
->dQRefNum
>= 0) )
676 /* Attempt to get the drive's format list. */
677 /* (see the Technical Note "What Your Sony Drives For You") */
679 pb
.cntrlParam
.ioVRefNum
= driveQElementPtr
->dQDrive
;
680 pb
.cntrlParam
.ioCRefNum
= driveQElementPtr
->dQRefNum
;
681 pb
.cntrlParam
.csCode
= kFmtLstCode
;
682 pb
.cntrlParam
.csParam
[0] = kMaxFormatListRecs
;
683 *(long *)&pb
.cntrlParam
.csParam
[1] = (long)&formatListRecords
[0];
685 result
= PBStatusSync(&pb
);
687 if ( result
== noErr
)
689 /* The drive supports ReturnFormatList status call. */
691 /* Get the current disk's size. */
692 for( formatListRecIndex
= 0;
693 formatListRecIndex
< pb
.cntrlParam
.csParam
[0];
694 ++formatListRecIndex
)
696 if ( (formatListRecords
[formatListRecIndex
].formatFlags
&
697 diCIFmtFlagsCurrentMask
) != 0 )
699 blocks
= formatListRecords
[formatListRecIndex
].volSize
;
704 /* This should never happen */
708 else if ( driveQElementPtr
->dQRefNum
== (short)kSonyRefNum
)
710 /* The drive is a non-SuperDrive floppy which only supports 400K and 800K disks */
712 result
= DriveStatus(driveQElementPtr
->dQDrive
, &status
);
713 if ( result
== noErr
)
715 switch ( status
.twoSideFmt
)
718 blocks
= kSingleSidedSize
;
721 blocks
= kDoubleSidedSize
;
724 /* This should never happen */
732 /* The drive is not a floppy and it doesn't support ReturnFormatList */
733 /* so use the dQDrvSz field(s) */
735 result
= noErr
; /* reset result */
736 switch ( driveQElementPtr
->qType
)
739 blocks
= driveQElementPtr
->dQDrvSz
;
742 blocks
= ((unsigned long)driveQElementPtr
->dQDrvSz2
<< 16) +
743 driveQElementPtr
->dQDrvSz
;
746 /* This should never happen */
753 if ( result
== noErr
)
761 /*****************************************************************************/
763 pascal OSErr
GetVolFileSystemID(ConstStr255Param pathname
,
770 error
= GetVolumeInfoNoName(pathname
,vRefNum
, &pb
);
771 if ( error
== noErr
)
773 *fileSystemID
= pb
.volumeParam
.ioVFSID
;
779 /*****************************************************************************/
781 pascal OSErr
GetVolState(ConstStr255Param pathname
,
783 Boolean
*volumeOnline
,
784 Boolean
*volumeEjected
,
785 Boolean
*driveEjectable
,
786 Boolean
*driverWantsEject
)
792 error
= GetVolumeInfoNoName(pathname
,vRefNum
, &pb
);
793 if ( error
== noErr
)
795 if ( pb
.volumeParam
.ioVDrvInfo
!= 0 )
797 /* the volume is online and not ejected */
798 *volumeOnline
= true;
799 *volumeEjected
= false;
801 /* Get the drive number */
802 driveNumber
= pb
.volumeParam
.ioVDrvInfo
;
806 /* the volume's is either offline or ejected */
807 /* in either case, the volume is NOT online */
808 *volumeOnline
= false;
811 *volumeEjected
= pb
.volumeParam
.ioVDRefNum
> 0;
813 if ( *volumeEjected
)
815 /* If ejected, the drive number is ioVDRefNum */
816 driveNumber
= pb
.volumeParam
.ioVDRefNum
;
820 /* If offline, the drive number is the negative of ioVDRefNum */
821 driveNumber
= (short)-pb
.volumeParam
.ioVDRefNum
;
828 /* Find the drive queue element by searching the drive queue */
829 drvQElem
= (DrvQElPtr
)(GetDrvQHdr()->qHead
);
830 while ( (drvQElem
!= NULL
) && (drvQElem
->dQDrive
!= driveNumber
) )
832 drvQElem
= (DrvQElPtr
)drvQElem
->qLink
;
835 if ( drvQElem
!= NULL
)
838 ** Each drive queue element is preceded by 4 flag bytes.
839 ** Byte 1 (the second flag byte) has bits that tell us if a
840 ** drive is ejectable and if its driver wants an eject call.
841 ** See Inside Macintosh: Files, page 2-85.
846 /* point to byte 1 of the flag bytes */
847 flagBytePtr
= (Ptr
)drvQElem
;
851 ** The drive is ejectable if flag byte 1 does not contain
852 ** 0x08 (nonejectable) or 0x48 (nonejectable, but wants eject call).
855 *driveEjectable
= (*flagBytePtr
!= 0x08) && (*flagBytePtr
!= 0x48);
858 ** The driver wants an eject call if flag byte 1 does not contain
859 ** 0x08 (nonejectable). This may seem like a minor point, but some
860 ** disk drivers use the Eject request to flush their caches to disk
861 ** and you wouldn't want to skip that step after unmounting a volume.
864 *driverWantsEject
= (*flagBytePtr
!= 0x08);
869 /* Didn't find the drive (this should never happen) */
870 *driveEjectable
= false;
871 *driverWantsEject
= false;
879 /*****************************************************************************/
881 pascal OSErr
UnmountAndEject(ConstStr255Param pathname
,
886 Boolean ejected
, wantsEject
;
890 error
= GetVolumeInfoNoName(pathname
, vRefNum
, &pb
);
891 if ( error
== noErr
)
893 if ( pb
.volumeParam
.ioVDrvInfo
!= 0 )
895 /* the volume is online and not ejected */
898 /* Get the drive number */
899 driveNum
= pb
.volumeParam
.ioVDrvInfo
;
903 /* the volume is ejected or offline */
906 ejected
= pb
.volumeParam
.ioVDRefNum
> 0;
910 /* If ejected, the drive number is ioVDRefNum */
911 driveNum
= pb
.volumeParam
.ioVDRefNum
;
915 /* If offline, the drive number is the negative of ioVDRefNum */
916 driveNum
= (short)-pb
.volumeParam
.ioVDRefNum
;
920 /* find the drive queue element */
921 drvQElem
= (DrvQElPtr
)(GetDrvQHdr()->qHead
);
922 while ( (drvQElem
!= NULL
) && (drvQElem
->dQDrive
!= driveNum
) )
924 drvQElem
= (DrvQElPtr
)drvQElem
->qLink
;
927 if ( drvQElem
!= NULL
)
929 /* does the drive want an eject call */
930 wantsEject
= (*((Ptr
)((Ptr
)drvQElem
- 3)) != 8);
934 /* didn't find the drive!! */
938 /* unmount the volume */
939 pb
.volumeParam
.ioNamePtr
= NULL
;
940 /* ioVRefNum is already filled in from PBHGetVInfo */
941 error
= PBUnmountVol((ParmBlkPtr
)&pb
);
942 if ( error
== noErr
)
944 if ( wantsEject
&& !ejected
)
946 /* eject the media from the drive if needed */
947 pb
.volumeParam
.ioVRefNum
= driveNum
;
948 error
= PBEject((ParmBlkPtr
)&pb
);
956 /*****************************************************************************/
958 pascal OSErr
OnLine(FSSpecPtr volumes
,
970 for ( endVolArray
= volumes
+ reqVolCount
; (volumes
< endVolArray
) && (error
== noErr
); ++volumes
)
972 pb
.volumeParam
.ioNamePtr
= (StringPtr
) & volumes
->name
;
973 pb
.volumeParam
.ioVolIndex
= *volIndex
;
974 error
= PBHGetVInfoSync(&pb
);
975 if ( error
== noErr
)
977 volumes
->parID
= fsRtParID
; /* the root directory's parent is 1 */
978 volumes
->vRefNum
= pb
.volumeParam
.ioVRefNum
;
992 /*****************************************************************************/
994 pascal OSErr
SetDefault(short newVRefNum
,
1001 /* Get the current default volume/directory. */
1002 error
= HGetVol(NULL
, oldVRefNum
, oldDirID
);
1003 if ( error
== noErr
)
1005 /* Set the new default volume/directory */
1006 error
= HSetVol(NULL
, newVRefNum
, newDirID
);
1012 /*****************************************************************************/
1014 pascal OSErr
RestoreDefault(short oldVRefNum
,
1018 short defaultVRefNum
;
1022 /* Determine if the default volume was a wdRefNum. */
1023 error
= GetWDInfo(oldVRefNum
, &defaultVRefNum
, &defaultDirID
, &defaultProcID
);
1024 if ( error
== noErr
)
1026 /* Restore the old default volume/directory, one way or the other. */
1027 if ( defaultDirID
!= fsRtDirID
)
1029 /* oldVRefNum was a wdRefNum - use SetVol */
1030 error
= SetVol(NULL
, oldVRefNum
);
1034 /* oldVRefNum was a real vRefNum - use HSetVol */
1035 error
= HSetVol(NULL
, oldVRefNum
, oldDirID
);
1042 /*****************************************************************************/
1044 pascal OSErr
GetDInfo(short vRefNum
,
1046 ConstStr255Param name
,
1052 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1053 if ( error
== noErr
)
1055 if ( (pb
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1057 /* it's a directory, return the DInfo */
1058 *fndrInfo
= pb
.dirInfo
.ioDrUsrWds
;
1062 /* oops, a file was passed */
1070 /*****************************************************************************/
1072 pascal OSErr
FSpGetDInfo(const FSSpec
*spec
,
1075 return ( GetDInfo(spec
->vRefNum
, spec
->parID
, spec
->name
, fndrInfo
) );
1078 /*****************************************************************************/
1080 pascal OSErr
SetDInfo(short vRefNum
,
1082 ConstStr255Param name
,
1083 const DInfo
*fndrInfo
)
1089 /* Protection against File Sharing problem */
1090 if ( (name
== NULL
) || (name
[0] == 0) )
1093 pb
.dirInfo
.ioNamePtr
= tempName
;
1094 pb
.dirInfo
.ioFDirIndex
= -1; /* use ioDirID */
1098 pb
.dirInfo
.ioNamePtr
= (StringPtr
)name
;
1099 pb
.dirInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1101 pb
.dirInfo
.ioVRefNum
= vRefNum
;
1102 pb
.dirInfo
.ioDrDirID
= dirID
;
1103 error
= PBGetCatInfoSync(&pb
);
1104 if ( error
== noErr
)
1106 if ( (pb
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1108 /* it's a directory, set the DInfo */
1109 if ( pb
.dirInfo
.ioNamePtr
== tempName
)
1111 pb
.dirInfo
.ioDrDirID
= pb
.dirInfo
.ioDrParID
;
1115 pb
.dirInfo
.ioDrDirID
= dirID
;
1117 pb
.dirInfo
.ioDrUsrWds
= *fndrInfo
;
1118 error
= PBSetCatInfoSync(&pb
);
1122 /* oops, a file was passed */
1130 /*****************************************************************************/
1132 pascal OSErr
FSpSetDInfo(const FSSpec
*spec
,
1133 const DInfo
*fndrInfo
)
1135 return ( SetDInfo(spec
->vRefNum
, spec
->parID
, spec
->name
, fndrInfo
) );
1138 /*****************************************************************************/
1140 pascal OSErr
GetDirectoryID(short vRefNum
,
1142 ConstStr255Param name
,
1144 Boolean
*isDirectory
)
1149 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1150 if ( error
== noErr
)
1152 *isDirectory
= (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0;
1155 *theDirID
= pb
.dirInfo
.ioDrDirID
;
1159 *theDirID
= pb
.hFileInfo
.ioFlParID
;
1166 /*****************************************************************************/
1168 pascal OSErr
FSpGetDirectoryID(const FSSpec
*spec
,
1170 Boolean
*isDirectory
)
1172 return ( GetDirectoryID(spec
->vRefNum
, spec
->parID
, spec
->name
,
1173 theDirID
, isDirectory
) );
1176 /*****************************************************************************/
1178 pascal OSErr
GetDirName(short vRefNum
,
1187 pb
.dirInfo
.ioNamePtr
= name
;
1188 pb
.dirInfo
.ioVRefNum
= vRefNum
;
1189 pb
.dirInfo
.ioDrDirID
= dirID
;
1190 pb
.dirInfo
.ioFDirIndex
= -1; /* get information about ioDirID */
1191 error
= PBGetCatInfoSync(&pb
);
1201 /*****************************************************************************/
1203 pascal OSErr
GetIOACUser(short vRefNum
,
1205 ConstStr255Param name
,
1211 /* Clear ioACUser before calling PBGetCatInfo since some file systems
1212 ** don't bother to set or clear this field. If ioACUser isn't set by the
1213 ** file system, then you'll get the zero value back (full access) which
1214 ** is the access you have on volumes that don't support ioACUser.
1216 pb
.dirInfo
.ioACUser
= 0; /* ioACUser used to be filler2 */
1217 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1218 if ( error
== noErr
)
1220 if ( (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) == 0 )
1222 /* oops, a file was passed */
1227 *ioACUser
= pb
.dirInfo
.ioACUser
;
1234 /*****************************************************************************/
1236 pascal OSErr
FSpGetIOACUser(const FSSpec
*spec
,
1239 return ( GetIOACUser(spec
->vRefNum
, spec
->parID
, spec
->name
, ioACUser
) );
1242 /*****************************************************************************/
1244 pascal OSErr
GetParentID(short vRefNum
,
1246 ConstStr255Param name
,
1254 /* Protection against File Sharing problem */
1255 if ( (name
== NULL
) || (name
[0] == 0) )
1258 pb
.hFileInfo
.ioNamePtr
= tempName
;
1259 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1263 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)name
;
1264 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1266 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1267 pb
.hFileInfo
.ioDirID
= dirID
;
1268 error
= PBGetCatInfoSync(&pb
);
1269 if ( error
== noErr
)
1272 ** There's a bug in HFS where the wrong parent dir ID can be
1273 ** returned if multiple separators are used at the end of a
1274 ** pathname. For example, if the pathname:
1275 ** 'volumeName:System Folder:Extensions::'
1276 ** is passed, the directory ID of the Extensions folder is
1277 ** returned in the ioFlParID field instead of fsRtDirID. Since
1278 ** multiple separators at the end of a pathname always specifies
1279 ** a directory, we only need to work-around cases where the
1280 ** object is a directory and there are multiple separators at
1281 ** the end of the name parameter.
1283 if ( (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1285 /* Its a directory */
1287 /* is there a pathname? */
1288 if ( pb
.hFileInfo
.ioNamePtr
== name
)
1290 /* could it contain multiple separators? */
1293 /* does it contain multiple separators at the end? */
1294 if ( (name
[name
[0]] == ':') && (name
[name
[0] - 1] == ':') )
1296 /* OK, then do the extra stuff to get the correct parID */
1298 /* Get the real vRefNum (this should not fail) */
1299 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
1300 if ( error
== noErr
)
1302 /* we don't need the parent's name, but add protect against File Sharing problem */
1304 pb
.dirInfo
.ioNamePtr
= tempName
;
1305 pb
.dirInfo
.ioVRefNum
= realVRefNum
;
1306 /* pb.dirInfo.ioDrDirID already contains the */
1307 /* dirID of the directory object */
1308 pb
.dirInfo
.ioFDirIndex
= -1; /* get information about ioDirID */
1309 error
= PBGetCatInfoSync(&pb
);
1310 /* now, pb.dirInfo.ioDrParID contains the correct parID */
1317 if ( error
== noErr
)
1319 /* if no errors, then pb.hFileInfo.ioFlParID (pb.dirInfo.ioDrParID) */
1320 /* contains the parent ID */
1321 *parID
= pb
.hFileInfo
.ioFlParID
;
1328 /*****************************************************************************/
1330 pascal OSErr
GetFilenameFromPathname(ConstStr255Param pathname
,
1337 /* default to no filename */
1340 /* check for no pathname */
1341 if ( pathname
!= NULL
)
1343 /* get string length */
1344 index
= pathname
[0];
1346 /* check for empty string */
1349 /* skip over last trailing colon (if any) */
1350 if ( pathname
[index
] == ':' )
1355 /* save the end of the string */
1358 /* if pathname ends with multiple colons, then this pathname refers */
1359 /* to a directory, not a file */
1360 if ( pathname
[index
] != ':' )
1362 /* parse backwards until we find a colon or hit the beginning of the pathname */
1363 while ( (index
!= 0) && (pathname
[index
] != ':') )
1368 /* if we parsed to the beginning of the pathname and the pathname ended */
1369 /* with a colon, then pathname is a full pathname to a volume, not a file */
1370 if ( (index
!= 0) || (pathname
[pathname
[0]] != ':') )
1372 /* get the filename and return noErr */
1373 filename
[0] = (char)(nameEnd
- index
);
1374 BlockMoveData(&pathname
[index
+1], &filename
[1], nameEnd
- index
);
1379 /* pathname to a volume, not a file */
1380 error
= notAFileErr
;
1385 /* directory, not a file */
1386 error
= notAFileErr
;
1391 /* empty string isn't a file */
1392 error
= notAFileErr
;
1397 /* NULL pathname isn't a file */
1398 error
= notAFileErr
;
1404 /*****************************************************************************/
1406 pascal OSErr
GetObjectLocation(short vRefNum
,
1408 ConstStr255Param pathname
,
1412 Boolean
*isDirectory
)
1416 Str255 tempPathname
;
1424 ** Get the real vRefNum
1426 error
= DetermineVRefNum(pathname
, vRefNum
, realVRefNum
);
1427 if ( error
== noErr
)
1430 ** Determine if the object already exists and if so,
1431 ** get the real parent directory ID if it's a file
1434 /* Protection against File Sharing problem */
1435 if ( (pathname
== NULL
) || (pathname
[0] == 0) )
1437 tempPathname
[0] = 0;
1438 pb
.hFileInfo
.ioNamePtr
= tempPathname
;
1439 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1443 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)pathname
;
1444 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1446 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1447 pb
.hFileInfo
.ioDirID
= dirID
;
1448 error
= PBGetCatInfoSync(&pb
);
1449 if ( error
== noErr
)
1452 ** The file system object is present and we have the file's real parID
1455 /* Is it a directory or a file? */
1456 *isDirectory
= (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0;
1460 ** It's a directory, get its name and parent dirID, and then we're done
1463 pb
.dirInfo
.ioNamePtr
= realName
;
1464 pb
.dirInfo
.ioVRefNum
= *realVRefNum
;
1465 /* pb.dirInfo.ioDrDirID already contains the dirID of the directory object */
1466 pb
.dirInfo
.ioFDirIndex
= -1; /* get information about ioDirID */
1467 error
= PBGetCatInfoSync(&pb
);
1469 /* get the parent ID here, because the file system can return the */
1470 /* wrong parent ID from the last call. */
1471 *realParID
= pb
.dirInfo
.ioDrParID
;
1476 ** It's a file - use the parent directory ID from the last call
1477 ** to GetCatInfoparse, get the file name, and then we're done
1479 *realParID
= pb
.hFileInfo
.ioFlParID
;
1480 error
= GetFilenameFromPathname(pathname
, realName
);
1483 else if ( error
== fnfErr
)
1486 ** The file system object is not present - see if its parent is present
1490 ** Parse to get the object name from end of pathname
1492 error
= GetFilenameFromPathname(pathname
, realName
);
1494 /* if we can't get the object name from the end, we can't continue */
1495 if ( error
== noErr
)
1498 ** What we want now is the pathname minus the object name
1500 ** if pathname is 'vol:dir:file' tempPathname becomes 'vol:dir:'
1501 ** if pathname is 'vol:dir:file:' tempPathname becomes 'vol:dir:'
1502 ** if pathname is ':dir:file' tempPathname becomes ':dir:'
1503 ** if pathname is ':dir:file:' tempPathname becomes ':dir:'
1504 ** if pathname is ':file' tempPathname becomes ':'
1505 ** if pathname is 'file or file:' tempPathname becomes ''
1508 /* get a copy of the pathname */
1509 BlockMoveData(pathname
, tempPathname
, pathname
[0] + 1);
1511 /* remove the object name */
1512 tempPathname
[0] -= realName
[0];
1513 /* and the trailing colon (if any) */
1514 if ( pathname
[pathname
[0]] == ':' )
1519 /* OK, now get the parent's directory ID */
1521 /* Protection against File Sharing problem */
1522 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)tempPathname
;
1523 if ( tempPathname
[0] != 0 )
1525 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1529 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1531 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1532 pb
.hFileInfo
.ioDirID
= dirID
;
1533 error
= PBGetCatInfoSync(&pb
);
1534 *realParID
= pb
.dirInfo
.ioDrDirID
;
1536 *isDirectory
= false; /* we don't know what the object is really going to be */
1539 if ( error
!= noErr
)
1541 error
= dirNFErr
; /* couldn't find parent directory */
1545 error
= fnfErr
; /* we found the parent, but not the file */
1553 /*****************************************************************************/
1555 pascal OSErr
GetDirItems(short vRefNum
,
1557 ConstStr255Param name
,
1559 Boolean getDirectories
,
1562 short *actItemCount
,
1563 short *itemIndex
) /* start with 1, then use what's returned */
1568 Boolean isDirectory
;
1569 FSSpec
*endItemsArray
;
1571 if ( *itemIndex
> 0 )
1573 /* NOTE: If I could be sure that the caller passed a real vRefNum and real directory */
1574 /* to this routine, I could rip out calls to DetermineVRefNum and GetDirectoryID and this */
1575 /* routine would be much faster because of the overhead of DetermineVRefNum and */
1576 /* GetDirectoryID and because GetDirectoryID blows away the directory index hint the Macintosh */
1577 /* file system keeps for indexed calls. I can't be sure, so for maximum throughput, */
1578 /* pass a big array of FSSpecs so you can get the directory's contents with few calls */
1579 /* to this routine. */
1581 /* get the real volume reference number */
1582 error
= DetermineVRefNum(name
, vRefNum
, &pb
.hFileInfo
.ioVRefNum
);
1583 if ( error
== noErr
)
1585 /* and the real directory ID of this directory (and make sure it IS a directory) */
1586 error
= GetDirectoryID(vRefNum
, dirID
, name
, &theDirID
, &isDirectory
);
1587 if ( error
== noErr
)
1592 endItemsArray
= items
+ reqItemCount
;
1593 while ( (items
< endItemsArray
) && (error
== noErr
) )
1595 pb
.hFileInfo
.ioNamePtr
= (StringPtr
) &items
->name
;
1596 pb
.hFileInfo
.ioDirID
= theDirID
;
1597 pb
.hFileInfo
.ioFDirIndex
= *itemIndex
;
1598 error
= PBGetCatInfoSync(&pb
);
1599 if ( error
== noErr
)
1601 items
->parID
= pb
.hFileInfo
.ioFlParID
; /* return item's parID */
1602 items
->vRefNum
= pb
.hFileInfo
.ioVRefNum
; /* return item's vRefNum */
1603 ++*itemIndex
; /* prepare to get next item in directory */
1605 if ( (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1607 if ( getDirectories
)
1609 ++*actItemCount
; /* keep this item */
1610 ++items
; /* point to next item */
1617 ++*actItemCount
; /* keep this item */
1618 ++items
; /* point to next item */
1626 /* it wasn't a directory */
1641 /*****************************************************************************/
1643 static void DeleteLevel(long dirToDelete
,
1644 DeleteEnumGlobalsPtr theGlobals
)
1650 /* prepare to delete directory */
1651 theGlobals
->myPB
.ciPB
.dirInfo
.ioNamePtr
= (StringPtr
)&theGlobals
->itemName
;
1652 theGlobals
->myPB
.ciPB
.dirInfo
.ioFDirIndex
= 1; /* get first item */
1653 theGlobals
->myPB
.ciPB
.dirInfo
.ioDrDirID
= dirToDelete
; /* in this directory */
1654 theGlobals
->error
= PBGetCatInfoSync(&(theGlobals
->myPB
.ciPB
));
1655 if ( theGlobals
->error
== noErr
)
1657 savedDir
= dirToDelete
;
1658 /* We have an item. Is it a file or directory? */
1659 if ( (theGlobals
->myPB
.ciPB
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1661 /* it's a directory */
1662 savedDir
= theGlobals
->myPB
.ciPB
.dirInfo
.ioDrDirID
; /* save dirID of directory instead */
1663 DeleteLevel(theGlobals
->myPB
.ciPB
.dirInfo
.ioDrDirID
, theGlobals
); /* Delete its contents */
1664 theGlobals
->myPB
.ciPB
.dirInfo
.ioNamePtr
= NULL
; /* prepare to delete directory */
1666 if ( theGlobals
->error
== noErr
)
1668 theGlobals
->myPB
.ciPB
.dirInfo
.ioDrDirID
= savedDir
; /* restore dirID */
1669 theGlobals
->myPB
.hPB
.fileParam
.ioFVersNum
= 0; /* just in case it's used on an MFS volume... */
1670 theGlobals
->error
= PBHDeleteSync(&(theGlobals
->myPB
.hPB
)); /* delete this item */
1671 if ( theGlobals
->error
== fLckdErr
)
1673 (void) PBHRstFLockSync(&(theGlobals
->myPB
.hPB
)); /* unlock it */
1674 theGlobals
->error
= PBHDeleteSync(&(theGlobals
->myPB
.hPB
)); /* and try again */
1678 } while ( theGlobals
->error
== noErr
);
1680 if ( theGlobals
->error
== fnfErr
)
1682 theGlobals
->error
= noErr
;
1686 /*****************************************************************************/
1688 pascal OSErr
DeleteDirectoryContents(short vRefNum
,
1690 ConstStr255Param name
)
1692 DeleteEnumGlobals theGlobals
;
1693 Boolean isDirectory
;
1696 /* Get the real dirID and make sure it is a directory. */
1697 error
= GetDirectoryID(vRefNum
, dirID
, name
, &dirID
, &isDirectory
);
1698 if ( error
== noErr
)
1702 /* Get the real vRefNum */
1703 error
= DetermineVRefNum(name
, vRefNum
, &vRefNum
);
1704 if ( error
== noErr
)
1706 /* Set up the globals we need to access from the recursive routine. */
1707 theGlobals
.myPB
.ciPB
.dirInfo
.ioVRefNum
= vRefNum
;
1709 /* Here we go into recursion land... */
1710 DeleteLevel(dirID
, &theGlobals
);
1711 error
= theGlobals
.error
;
1723 /*****************************************************************************/
1725 pascal OSErr
DeleteDirectory(short vRefNum
,
1727 ConstStr255Param name
)
1731 /* Make sure a directory was specified and then delete its contents */
1732 error
= DeleteDirectoryContents(vRefNum
, dirID
, name
);
1733 if ( error
== noErr
)
1735 error
= HDelete(vRefNum
, dirID
, name
);
1736 if ( error
== fLckdErr
)
1738 (void) HRstFLock(vRefNum
, dirID
, name
); /* unlock the directory locked by AppleShare */
1739 error
= HDelete(vRefNum
, dirID
, name
); /* and try again */
1746 /*****************************************************************************/
1748 pascal OSErr
CheckObjectLock(short vRefNum
,
1750 ConstStr255Param name
)
1755 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1756 if ( error
== noErr
)
1758 /* check locked bit */
1759 if ( (pb
.hFileInfo
.ioFlAttrib
& 0x01) != 0 )
1768 /*****************************************************************************/
1770 pascal OSErr
FSpCheckObjectLock(const FSSpec
*spec
)
1772 return ( CheckObjectLock(spec
->vRefNum
, spec
->parID
, spec
->name
) );
1775 /*****************************************************************************/
1777 pascal OSErr
GetFileSize(short vRefNum
,
1779 ConstStr255Param fileName
,
1786 pb
.fileParam
.ioNamePtr
= (StringPtr
)fileName
;
1787 pb
.fileParam
.ioVRefNum
= vRefNum
;
1788 pb
.fileParam
.ioFVersNum
= 0;
1789 pb
.fileParam
.ioDirID
= dirID
;
1790 pb
.fileParam
.ioFDirIndex
= 0;
1791 error
= PBHGetFInfoSync(&pb
);
1792 if ( error
== noErr
)
1794 *dataSize
= pb
.fileParam
.ioFlLgLen
;
1795 *rsrcSize
= pb
.fileParam
.ioFlRLgLen
;
1801 /*****************************************************************************/
1803 pascal OSErr
FSpGetFileSize(const FSSpec
*spec
,
1807 return ( GetFileSize(spec
->vRefNum
, spec
->parID
, spec
->name
, dataSize
, rsrcSize
) );
1810 /*****************************************************************************/
1812 pascal OSErr
BumpDate(short vRefNum
,
1814 ConstStr255Param name
)
1815 /* Given a file or directory, change its modification date to the current date/time. */
1822 /* Protection against File Sharing problem */
1823 if ( (name
== NULL
) || (name
[0] == 0) )
1826 pb
.hFileInfo
.ioNamePtr
= tempName
;
1827 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1831 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)name
;
1832 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1834 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1835 pb
.hFileInfo
.ioDirID
= dirID
;
1836 error
= PBGetCatInfoSync(&pb
);
1837 if ( error
== noErr
)
1840 /* set mod date to current date, or one second into the future
1841 if mod date = current date */
1842 pb
.hFileInfo
.ioFlMdDat
= (secs
== pb
.hFileInfo
.ioFlMdDat
) ? (++secs
) : (secs
);
1843 if ( pb
.dirInfo
.ioNamePtr
== tempName
)
1845 pb
.hFileInfo
.ioDirID
= pb
.hFileInfo
.ioFlParID
;
1849 pb
.hFileInfo
.ioDirID
= dirID
;
1851 error
= PBSetCatInfoSync(&pb
);
1857 /*****************************************************************************/
1859 pascal OSErr
FSpBumpDate(const FSSpec
*spec
)
1861 return ( BumpDate(spec
->vRefNum
, spec
->parID
, spec
->name
) );
1864 /*****************************************************************************/
1866 pascal OSErr
ChangeCreatorType(short vRefNum
,
1868 ConstStr255Param name
,
1877 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)name
;
1878 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1879 pb
.hFileInfo
.ioDirID
= dirID
;
1880 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1881 error
= PBGetCatInfoSync(&pb
);
1882 if ( error
== noErr
)
1884 if ( (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) == 0 ) /* if file */
1886 parID
= pb
.hFileInfo
.ioFlParID
; /* save parent dirID for BumpDate call */
1888 /* If creator not 0x00000000, change creator */
1889 if ( creator
!= (OSType
)0x00000000 )
1891 pb
.hFileInfo
.ioFlFndrInfo
.fdCreator
= creator
;
1894 /* If fileType not 0x00000000, change fileType */
1895 if ( fileType
!= (OSType
)0x00000000 )
1897 pb
.hFileInfo
.ioFlFndrInfo
.fdType
= fileType
;
1900 pb
.hFileInfo
.ioDirID
= dirID
;
1901 error
= PBSetCatInfoSync(&pb
); /* now, save the new information back to disk */
1903 if ( (error
== noErr
) && (parID
!= fsRtParID
) ) /* can't bump fsRtParID */
1905 /* get the real vRefNum in case a full pathname was passed */
1906 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
1907 if ( error
== noErr
)
1909 error
= BumpDate(realVRefNum
, parID
, NULL
);
1910 /* and bump the parent directory's mod date to wake up the Finder */
1911 /* to the change we just made */
1917 /* it was a directory, not a file */
1918 error
= notAFileErr
;
1925 /*****************************************************************************/
1927 pascal OSErr
FSpChangeCreatorType(const FSSpec
*spec
,
1931 return ( ChangeCreatorType(spec
->vRefNum
, spec
->parID
, spec
->name
, creator
, fileType
) );
1934 /*****************************************************************************/
1936 pascal OSErr
ChangeFDFlags(short vRefNum
,
1938 ConstStr255Param name
,
1940 unsigned short flagBits
)
1948 /* Protection against File Sharing problem */
1949 if ( (name
== NULL
) || (name
[0] == 0) )
1952 pb
.hFileInfo
.ioNamePtr
= tempName
;
1953 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1957 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)name
;
1958 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1960 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1961 pb
.hFileInfo
.ioDirID
= dirID
;
1962 error
= PBGetCatInfoSync(&pb
);
1963 if ( error
== noErr
)
1965 parID
= pb
.hFileInfo
.ioFlParID
; /* save parent dirID for BumpDate call */
1967 /* set or clear the appropriate bits in the Finder flags */
1970 /* OR in the bits */
1971 pb
.hFileInfo
.ioFlFndrInfo
.fdFlags
|= flagBits
;
1975 /* AND out the bits */
1976 pb
.hFileInfo
.ioFlFndrInfo
.fdFlags
&= ~flagBits
;
1979 if ( pb
.dirInfo
.ioNamePtr
== tempName
)
1981 pb
.hFileInfo
.ioDirID
= pb
.hFileInfo
.ioFlParID
;
1985 pb
.hFileInfo
.ioDirID
= dirID
;
1988 error
= PBSetCatInfoSync(&pb
); /* now, save the new information back to disk */
1990 if ( (error
== noErr
) && (parID
!= fsRtParID
) ) /* can't bump fsRtParID */
1992 /* get the real vRefNum in case a full pathname was passed */
1993 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
1994 if ( error
== noErr
)
1996 error
= BumpDate(realVRefNum
, parID
, NULL
);
1997 /* and bump the parent directory's mod date to wake up the Finder */
1998 /* to the change we just made */
2006 /*****************************************************************************/
2008 pascal OSErr
FSpChangeFDFlags(const FSSpec
*spec
,
2010 unsigned short flagBits
)
2012 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, setBits
, flagBits
) );
2015 /*****************************************************************************/
2017 pascal OSErr
SetIsInvisible(short vRefNum
,
2019 ConstStr255Param name
)
2020 /* Given a file or directory, make it invisible. */
2022 return ( ChangeFDFlags(vRefNum
, dirID
, name
, true, kIsInvisible
) );
2025 /*****************************************************************************/
2027 pascal OSErr
FSpSetIsInvisible(const FSSpec
*spec
)
2028 /* Given a file or directory, make it invisible. */
2030 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, true, kIsInvisible
) );
2033 /*****************************************************************************/
2035 pascal OSErr
ClearIsInvisible(short vRefNum
,
2037 ConstStr255Param name
)
2038 /* Given a file or directory, make it visible. */
2040 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kIsInvisible
) );
2043 /*****************************************************************************/
2045 pascal OSErr
FSpClearIsInvisible(const FSSpec
*spec
)
2046 /* Given a file or directory, make it visible. */
2048 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kIsInvisible
) );
2051 /*****************************************************************************/
2053 pascal OSErr
SetNameLocked(short vRefNum
,
2055 ConstStr255Param name
)
2056 /* Given a file or directory, lock its name. */
2058 return ( ChangeFDFlags(vRefNum
, dirID
, name
, true, kNameLocked
) );
2061 /*****************************************************************************/
2063 pascal OSErr
FSpSetNameLocked(const FSSpec
*spec
)
2064 /* Given a file or directory, lock its name. */
2066 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, true, kNameLocked
) );
2069 /*****************************************************************************/
2071 pascal OSErr
ClearNameLocked(short vRefNum
,
2073 ConstStr255Param name
)
2074 /* Given a file or directory, unlock its name. */
2076 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kNameLocked
) );
2079 /*****************************************************************************/
2081 pascal OSErr
FSpClearNameLocked(const FSSpec
*spec
)
2082 /* Given a file or directory, unlock its name. */
2084 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kNameLocked
) );
2087 /*****************************************************************************/
2089 pascal OSErr
SetIsStationery(short vRefNum
,
2091 ConstStr255Param name
)
2092 /* Given a file, make it a stationery pad. */
2094 return ( ChangeFDFlags(vRefNum
, dirID
, name
, true, kIsStationery
) );
2097 /*****************************************************************************/
2099 pascal OSErr
FSpSetIsStationery(const FSSpec
*spec
)
2100 /* Given a file, make it a stationery pad. */
2102 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, true, kIsStationery
) );
2105 /*****************************************************************************/
2107 pascal OSErr
ClearIsStationery(short vRefNum
,
2109 ConstStr255Param name
)
2110 /* Given a file, clear the stationery bit. */
2112 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kIsStationery
) );
2115 /*****************************************************************************/
2117 pascal OSErr
FSpClearIsStationery(const FSSpec
*spec
)
2118 /* Given a file, clear the stationery bit. */
2120 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kIsStationery
) );
2123 /*****************************************************************************/
2125 pascal OSErr
SetHasCustomIcon(short vRefNum
,
2127 ConstStr255Param name
)
2128 /* Given a file or directory, indicate that it has a custom icon. */
2130 return ( ChangeFDFlags(vRefNum
, dirID
, name
, true, kHasCustomIcon
) );
2133 /*****************************************************************************/
2135 pascal OSErr
FSpSetHasCustomIcon(const FSSpec
*spec
)
2136 /* Given a file or directory, indicate that it has a custom icon. */
2138 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, true, kHasCustomIcon
) );
2141 /*****************************************************************************/
2143 pascal OSErr
ClearHasCustomIcon(short vRefNum
,
2145 ConstStr255Param name
)
2146 /* Given a file or directory, indicate that it does not have a custom icon. */
2148 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kHasCustomIcon
) );
2151 /*****************************************************************************/
2153 pascal OSErr
FSpClearHasCustomIcon(const FSSpec
*spec
)
2154 /* Given a file or directory, indicate that it does not have a custom icon. */
2156 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kHasCustomIcon
) );
2159 /*****************************************************************************/
2161 pascal OSErr
ClearHasBeenInited(short vRefNum
,
2163 ConstStr255Param name
)
2164 /* Given a file, clear its "has been inited" bit. */
2166 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kHasBeenInited
) );
2169 /*****************************************************************************/
2171 pascal OSErr
FSpClearHasBeenInited(const FSSpec
*spec
)
2172 /* Given a file, clear its "has been inited" bit. */
2174 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kHasBeenInited
) );
2177 /*****************************************************************************/
2179 pascal OSErr
CopyFileMgrAttributes(short srcVRefNum
,
2181 ConstStr255Param srcName
,
2184 ConstStr255Param dstName
,
2185 Boolean copyLockBit
)
2190 Boolean objectIsDirectory
;
2192 pb
.ciPB
.hFileInfo
.ioVRefNum
= srcVRefNum
;
2193 pb
.ciPB
.hFileInfo
.ioDirID
= srcDirID
;
2195 /* Protection against File Sharing problem */
2196 if ( (srcName
== NULL
) || (srcName
[0] == 0) )
2199 pb
.ciPB
.hFileInfo
.ioNamePtr
= tempName
;
2200 pb
.ciPB
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
2204 pb
.ciPB
.hFileInfo
.ioNamePtr
= (StringPtr
)srcName
;
2205 pb
.ciPB
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
2207 error
= PBGetCatInfoSync(&pb
.ciPB
);
2208 if ( error
== noErr
)
2210 objectIsDirectory
= ( (pb
.ciPB
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 );
2211 pb
.ciPB
.hFileInfo
.ioVRefNum
= dstVRefNum
;
2212 pb
.ciPB
.hFileInfo
.ioDirID
= dstDirID
;
2213 if ( (dstName
!= NULL
) && (dstName
[0] == 0) )
2215 pb
.ciPB
.hFileInfo
.ioNamePtr
= NULL
;
2219 pb
.ciPB
.hFileInfo
.ioNamePtr
= (StringPtr
)dstName
;
2221 /* don't copy the hasBeenInited bit */
2222 pb
.ciPB
.hFileInfo
.ioFlFndrInfo
.fdFlags
= ( pb
.ciPB
.hFileInfo
.ioFlFndrInfo
.fdFlags
& 0xfeff );
2223 error
= PBSetCatInfoSync(&pb
.ciPB
);
2224 if ( (error
== noErr
) && (copyLockBit
) && ((pb
.ciPB
.hFileInfo
.ioFlAttrib
& 0x01) != 0) )
2226 pb
.hPB
.fileParam
.ioFVersNum
= 0;
2227 error
= PBHSetFLockSync(&pb
.hPB
);
2228 if ( (error
!= noErr
) && (objectIsDirectory
) )
2230 error
= noErr
; /* ignore lock errors if destination is directory */
2237 /*****************************************************************************/
2239 pascal OSErr
FSpCopyFileMgrAttributes(const FSSpec
*srcSpec
,
2240 const FSSpec
*dstSpec
,
2241 Boolean copyLockBit
)
2243 return ( CopyFileMgrAttributes(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
,
2244 dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
,
2248 /*****************************************************************************/
2250 pascal OSErr
HOpenAware(short vRefNum
,
2252 ConstStr255Param fileName
,
2258 GetVolParmsInfoBuffer volParmsInfo
;
2259 long infoSize
= sizeof(GetVolParmsInfoBuffer
);
2261 pb
.ioParam
.ioMisc
= NULL
;
2262 pb
.fileParam
.ioFVersNum
= 0;
2263 pb
.fileParam
.ioNamePtr
= (StringPtr
)fileName
;
2264 pb
.fileParam
.ioVRefNum
= vRefNum
;
2265 pb
.fileParam
.ioDirID
= dirID
;
2267 /* get volume attributes */
2268 /* this preflighting is needed because Foreign File Access based file systems don't */
2269 /* return the correct error result to the OpenDeny call */
2270 error
= HGetVolParms(fileName
, vRefNum
, &volParmsInfo
, &infoSize
);
2271 if ( (error
== noErr
) && hasOpenDeny(volParmsInfo
) )
2273 /* if volume supports OpenDeny, use it and return */
2274 pb
.accessParam
.ioDenyModes
= denyModes
;
2275 error
= PBHOpenDenySync(&pb
);
2276 *refNum
= pb
.ioParam
.ioRefNum
;
2278 else if ( (error
== noErr
) || (error
== paramErr
) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2280 /* OpenDeny isn't supported, so try File Manager Open functions */
2282 /* If request includes write permission, then see if the volume is */
2283 /* locked by hardware or software. The HFS file system doesn't check */
2284 /* for this when a file is opened - you only find out later when you */
2285 /* try to write and the write fails with a wPrErr or a vLckdErr. */
2287 if ( (denyModes
& dmWr
) != 0 )
2289 error
= CheckVolLock(fileName
, vRefNum
);
2296 if ( error
== noErr
)
2298 /* Set File Manager permissions to closest thing possible */
2299 if ( (denyModes
== dmWr
) || (denyModes
== dmRdWr
) )
2301 pb
.ioParam
.ioPermssn
= fsRdWrShPerm
;
2305 pb
.ioParam
.ioPermssn
= denyModes
% 4;
2308 error
= PBHOpenDFSync(&pb
); /* Try OpenDF */
2309 if ( error
== paramErr
)
2311 error
= PBHOpenSync(&pb
); /* OpenDF not supported, so try Open */
2313 *refNum
= pb
.ioParam
.ioRefNum
;
2320 /*****************************************************************************/
2322 pascal OSErr
FSpOpenAware(const FSSpec
*spec
,
2326 return ( HOpenAware(spec
->vRefNum
, spec
->parID
, spec
->name
, denyModes
, refNum
) );
2329 /*****************************************************************************/
2331 pascal OSErr
HOpenRFAware(short vRefNum
,
2333 ConstStr255Param fileName
,
2339 GetVolParmsInfoBuffer volParmsInfo
;
2340 long infoSize
= sizeof(GetVolParmsInfoBuffer
);
2342 pb
.ioParam
.ioMisc
= NULL
;
2343 pb
.fileParam
.ioFVersNum
= 0;
2344 pb
.fileParam
.ioNamePtr
= (StringPtr
)fileName
;
2345 pb
.fileParam
.ioVRefNum
= vRefNum
;
2346 pb
.fileParam
.ioDirID
= dirID
;
2348 /* get volume attributes */
2349 /* this preflighting is needed because Foreign File Access based file systems don't */
2350 /* return the correct error result to the OpenRFDeny call */
2351 error
= HGetVolParms(fileName
, vRefNum
, &volParmsInfo
, &infoSize
);
2352 if ( (error
== noErr
) && hasOpenDeny(volParmsInfo
) )
2354 /* if volume supports OpenRFDeny, use it and return */
2355 if ( hasOpenDeny(volParmsInfo
) )
2357 pb
.accessParam
.ioDenyModes
= denyModes
;
2358 error
= PBHOpenRFDenySync(&pb
);
2359 *refNum
= pb
.ioParam
.ioRefNum
;
2362 else if ( (error
== noErr
) || (error
== paramErr
) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2364 /* OpenRFDeny isn't supported, so try File Manager OpenRF function */
2366 /* If request includes write permission, then see if the volume is */
2367 /* locked by hardware or software. The HFS file system doesn't check */
2368 /* for this when a file is opened - you only find out later when you */
2369 /* try to write and the write fails with a wPrErr or a vLckdErr. */
2371 if ( (denyModes
& dmWr
) != 0 )
2373 error
= CheckVolLock(fileName
, vRefNum
);
2380 if ( error
== noErr
)
2382 /* Set File Manager permissions to closest thing possible */
2383 if ( (denyModes
== dmWr
) || (denyModes
== dmRdWr
) )
2385 pb
.ioParam
.ioPermssn
= fsRdWrShPerm
;
2389 pb
.ioParam
.ioPermssn
= denyModes
% 4;
2392 error
= PBHOpenRFSync(&pb
);
2393 *refNum
= pb
.ioParam
.ioRefNum
;
2400 /*****************************************************************************/
2402 pascal OSErr
FSpOpenRFAware(const FSSpec
*spec
,
2406 return ( HOpenRFAware(spec
->vRefNum
, spec
->parID
, spec
->name
, denyModes
, refNum
) );
2409 /*****************************************************************************/
2411 pascal OSErr
FSReadNoCache(short refNum
,
2418 pb
.ioParam
.ioRefNum
= refNum
;
2419 pb
.ioParam
.ioBuffer
= (Ptr
)buffPtr
;
2420 pb
.ioParam
.ioReqCount
= *count
;
2421 pb
.ioParam
.ioPosMode
= fsAtMark
+ 0x0020; /* fsAtMark + noCacheBit */
2422 pb
.ioParam
.ioPosOffset
= 0;
2423 error
= PBReadSync(&pb
);
2424 *count
= pb
.ioParam
.ioActCount
; /* always return count */
2428 /*****************************************************************************/
2430 pascal OSErr
FSWriteNoCache(short refNum
,
2432 const void *buffPtr
)
2437 pb
.ioParam
.ioRefNum
= refNum
;
2438 pb
.ioParam
.ioBuffer
= (Ptr
)buffPtr
;
2439 pb
.ioParam
.ioReqCount
= *count
;
2440 pb
.ioParam
.ioPosMode
= fsAtMark
+ 0x0020; /* fsAtMark + noCacheBit */
2441 pb
.ioParam
.ioPosOffset
= 0;
2442 error
= PBWriteSync(&pb
);
2443 *count
= pb
.ioParam
.ioActCount
; /* always return count */
2447 /*****************************************************************************/
2450 ** See if numBytes bytes of buffer1 are equal to buffer2.
2452 static Boolean
EqualMemory(const void *buffer1
, const void *buffer2
, unsigned long numBytes
)
2454 register unsigned char *b1
= (unsigned char *)buffer1
;
2455 register unsigned char *b2
= (unsigned char *)buffer2
;
2457 if ( b1
!= b2
) /* if buffer pointers are same, then they are equal */
2459 while ( numBytes
> 0 )
2461 /* compare the bytes and then increment the pointers */
2462 if ( (*b1
++ - *b2
++) != 0 )
2473 /*****************************************************************************/
2476 ** Read any number of bytes from an open file using read-verify mode.
2477 ** The FSReadVerify function reads any number of bytes from an open file
2478 ** and verifies them against the data in the buffer pointed to by buffPtr.
2480 ** Because of a bug in the HFS file system, only non-block aligned parts of
2481 ** the read are verified against the buffer data and the rest is *copied*
2482 ** into the buffer. Thus, you shouldn't verify against your original data;
2483 ** instead, you should verify against a copy of the original data and then
2484 ** compare the read-verified copy against the original data after calling
2485 ** FSReadVerify. That's why this function isn't exported - it needs the
2486 ** wrapper provided by FSWriteVerify.
2488 static OSErr
FSReadVerify(short refNum
,
2495 pb
.ioParam
.ioRefNum
= refNum
;
2496 pb
.ioParam
.ioBuffer
= (Ptr
)buffPtr
;
2497 pb
.ioParam
.ioReqCount
= *count
;
2498 pb
.ioParam
.ioPosMode
= fsAtMark
+ rdVerify
;
2499 pb
.ioParam
.ioPosOffset
= 0;
2500 result
= PBReadSync(&pb
);
2501 *count
= pb
.ioParam
.ioActCount
; /* always return count */
2505 /*****************************************************************************/
2507 pascal OSErr
FSWriteVerify(short refNum
,
2509 const void *buffPtr
)
2520 ** Allocate the verify buffer
2521 ** Try to get get a large enough buffer to verify in one pass.
2522 ** If that fails, use GetTempBuffer to get a buffer.
2524 bufferSize
= *count
;
2525 verifyBuffer
= NewPtr(bufferSize
);
2526 if ( verifyBuffer
== NULL
)
2528 verifyBuffer
= GetTempBuffer(bufferSize
, &bufferSize
);
2530 if ( verifyBuffer
!= NULL
)
2532 /* Save the current position */
2533 result
= GetFPos(refNum
, &position
);
2534 if ( result
== noErr
)
2536 /* Write the data */
2537 result
= FSWrite(refNum
, count
, buffPtr
);
2538 if ( result
== noErr
)
2540 /* Restore the original position */
2541 result
= SetFPos(refNum
, fsFromStart
, position
);
2542 if ( result
== noErr
)
2545 ** *count = total number of bytes to verify
2546 ** bufferSize = the size of the verify buffer
2547 ** bytesVerified = number of bytes verified
2548 ** byteCount = number of bytes to verify this pass
2549 ** startVerify = position in buffPtr
2552 startVerify
= (Ptr
)buffPtr
;
2553 while ( (bytesVerified
< *count
) && ( result
== noErr
) )
2555 if ( (*count
- bytesVerified
) > bufferSize
)
2557 byteCount
= bufferSize
;
2561 byteCount
= *count
- bytesVerified
;
2564 ** Copy the write buffer into the verify buffer.
2565 ** This step is needed because the File Manager
2566 ** compares the data in any non-block aligned
2567 ** data at the beginning and end of the read-verify
2568 ** request back into the file system's cache
2569 ** to the data in verify Buffer. However, the
2570 ** File Manager does not compare any full blocks
2571 ** and instead copies them into the verify buffer
2572 ** so we still have to compare the buffers again
2573 ** after the read-verify request completes.
2575 BlockMoveData(startVerify
, verifyBuffer
, byteCount
);
2577 /* Read-verify the data back into the verify buffer */
2578 result
= FSReadVerify(refNum
, &byteCount
, verifyBuffer
);
2579 if ( result
== noErr
)
2581 /* See if the buffers are the same */
2582 if ( !EqualMemory(verifyBuffer
, startVerify
, byteCount
) )
2586 startVerify
+= byteCount
;
2587 bytesVerified
+= byteCount
;
2593 DisposePtr(verifyBuffer
);
2597 result
= memFullErr
;
2602 /*****************************************************************************/
2604 pascal OSErr
CopyFork(short srcRefNum
,
2606 void *copyBufferPtr
,
2607 long copyBufferSize
)
2609 ParamBlockRec srcPB
;
2610 ParamBlockRec dstPB
;
2614 if ( (copyBufferPtr
== NULL
) || (copyBufferSize
== 0) )
2615 return ( paramErr
);
2617 srcPB
.ioParam
.ioRefNum
= srcRefNum
;
2618 dstPB
.ioParam
.ioRefNum
= dstRefNum
;
2620 /* preallocate the destination fork and */
2621 /* ensure the destination fork's EOF is correct after the copy */
2622 srcError
= PBGetEOFSync(&srcPB
);
2623 if ( srcError
!= noErr
)
2624 return ( srcError
);
2625 dstPB
.ioParam
.ioMisc
= srcPB
.ioParam
.ioMisc
;
2626 dstError
= PBSetEOFSync(&dstPB
);
2627 if ( dstError
!= noErr
)
2628 return ( dstError
);
2630 /* reset source fork's mark */
2631 srcPB
.ioParam
.ioPosMode
= fsFromStart
;
2632 srcPB
.ioParam
.ioPosOffset
= 0;
2633 srcError
= PBSetFPosSync(&srcPB
);
2634 if ( srcError
!= noErr
)
2635 return ( srcError
);
2637 /* reset destination fork's mark */
2638 dstPB
.ioParam
.ioPosMode
= fsFromStart
;
2639 dstPB
.ioParam
.ioPosOffset
= 0;
2640 dstError
= PBSetFPosSync(&dstPB
);
2641 if ( dstError
!= noErr
)
2642 return ( dstError
);
2644 /* set up fields that won't change in the loop */
2645 srcPB
.ioParam
.ioBuffer
= (Ptr
)copyBufferPtr
;
2646 srcPB
.ioParam
.ioPosMode
= fsAtMark
+ 0x0020;/* fsAtMark + noCacheBit */
2647 /* If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes */
2648 /* This will make writes on local volumes faster */
2649 if ( (copyBufferSize
>= 512) && ((copyBufferSize
& 0x1ff) != 0) )
2651 srcPB
.ioParam
.ioReqCount
= copyBufferSize
& 0xfffffe00;
2655 srcPB
.ioParam
.ioReqCount
= copyBufferSize
;
2657 dstPB
.ioParam
.ioBuffer
= (Ptr
)copyBufferPtr
;
2658 dstPB
.ioParam
.ioPosMode
= fsAtMark
+ 0x0020;/* fsAtMark + noCacheBit */
2660 while ( (srcError
== noErr
) && (dstError
== noErr
) )
2662 srcError
= PBReadSync(&srcPB
);
2663 dstPB
.ioParam
.ioReqCount
= srcPB
.ioParam
.ioActCount
;
2664 dstError
= PBWriteSync(&dstPB
);
2667 /* make sure there were no errors at the destination */
2668 if ( dstError
!= noErr
)
2669 return ( dstError
);
2671 /* make sure the only error at the source was eofErr */
2672 if ( srcError
!= eofErr
)
2673 return ( srcError
);
2678 /*****************************************************************************/
2680 pascal OSErr
GetFileLocation(short refNum
,
2688 pb
.ioNamePtr
= fileName
;
2690 pb
.ioRefNum
= refNum
;
2692 error
= PBGetFCBInfoSync(&pb
);
2693 if ( error
== noErr
)
2695 *vRefNum
= pb
.ioFCBVRefNum
;
2696 *dirID
= pb
.ioFCBParID
;
2701 /*****************************************************************************/
2703 pascal OSErr
FSpGetFileLocation(short refNum
,
2706 return ( GetFileLocation(refNum
, &(spec
->vRefNum
), &(spec
->parID
), spec
->name
) );
2709 /*****************************************************************************/
2711 pascal OSErr
CopyDirectoryAccess(short srcVRefNum
,
2713 ConstStr255Param srcName
,
2716 ConstStr255Param dstName
)
2719 GetVolParmsInfoBuffer infoBuffer
; /* Where PBGetVolParms dumps its info */
2720 long dstServerAdr
; /* AppleTalk server address of destination (if any) */
2721 long ownerID
, groupID
, accessRights
;
2724 /* See if destination supports directory access control */
2725 tempLong
= sizeof(infoBuffer
);
2726 error
= HGetVolParms(dstName
, dstVRefNum
, &infoBuffer
, &tempLong
);
2727 if ( (error
== noErr
) && hasAccessCntl(infoBuffer
) )
2729 if ( hasAccessCntl(infoBuffer
) )
2731 dstServerAdr
= infoBuffer
.vMServerAdr
;
2733 /* See if source supports directory access control and is on same server */
2734 tempLong
= sizeof(infoBuffer
);
2735 error
= HGetVolParms(srcName
, srcVRefNum
, &infoBuffer
, &tempLong
);
2736 if ( error
== noErr
)
2738 if ( hasAccessCntl(infoBuffer
) && (dstServerAdr
== infoBuffer
.vMServerAdr
) )
2740 /* both volumes support directory access control and they are */
2741 /* on same server, so copy the access information */
2742 error
= HGetDirAccess(srcVRefNum
, srcDirID
, srcName
, &ownerID
, &groupID
, &accessRights
);
2743 if ( error
== noErr
)
2745 error
= HSetDirAccess(dstVRefNum
, dstDirID
, dstName
, ownerID
, groupID
, accessRights
);
2750 /* destination doesn't support directory access control or */
2751 /* they volumes aren't on the same server */
2758 /* destination doesn't support directory access control */
2766 /*****************************************************************************/
2768 pascal OSErr
FSpCopyDirectoryAccess(const FSSpec
*srcSpec
,
2769 const FSSpec
*dstSpec
)
2771 return ( CopyDirectoryAccess(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
,
2772 dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
) );
2775 /*****************************************************************************/
2777 pascal OSErr
HMoveRenameCompat(short vRefNum
,
2779 ConstStr255Param srcName
,
2781 ConstStr255Param dstpathName
,
2782 ConstStr255Param copyName
)
2785 GetVolParmsInfoBuffer volParmsInfo
;
2790 Boolean isDirectory
;
2791 long tempItemsDirID
;
2792 long uniqueTempDirID
;
2793 Str31 uniqueTempDirName
;
2794 unsigned short uniqueNameoverflow
;
2796 /* Get volume attributes */
2797 infoSize
= sizeof(GetVolParmsInfoBuffer
);
2798 error
= HGetVolParms((StringPtr
)srcName
, vRefNum
, &volParmsInfo
, &infoSize
);
2799 if ( (error
== noErr
) && hasMoveRename(volParmsInfo
) )
2801 /* If volume supports move and rename, so use it and return */
2802 error
= HMoveRename(vRefNum
, srcDirID
, srcName
, dstDirID
, dstpathName
, copyName
);
2804 else if ( (error
== noErr
) || (error
== paramErr
) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2806 /* MoveRename isn't supported by this volume, so do it by hand */
2808 /* If copyName isn't supplied, we can simply CatMove and return */
2809 if ( copyName
== NULL
)
2811 error
= CatMove(vRefNum
, srcDirID
, srcName
, dstDirID
, dstpathName
);
2815 /* Renaming is required, so we have some work to do... */
2817 /* Get the object's real name, real parent ID and real vRefNum */
2818 error
= GetObjectLocation(vRefNum
, srcDirID
, (StringPtr
)srcName
,
2819 &realVRefNum
, &realParID
, realName
, &isDirectory
);
2820 if ( error
== noErr
)
2822 /* Find the Temporary Items Folder on that volume */
2823 error
= FindFolder(realVRefNum
, kTemporaryFolderType
, kCreateFolder
,
2824 &realVRefNum
, &tempItemsDirID
);
2825 if ( error
== noErr
)
2827 /* Create a new uniquely named folder in the temporary items folder. */
2828 /* This is done to avoid the case where 'realName' or 'copyName' already */
2829 /* exists in the temporary items folder. */
2831 /* Start with current tick count as uniqueTempDirName */
2832 NumToString(TickCount(), uniqueTempDirName
);
2833 uniqueNameoverflow
= 0;
2836 error
= DirCreate(realVRefNum
, tempItemsDirID
, uniqueTempDirName
, &uniqueTempDirID
);
2837 if ( error
== dupFNErr
)
2839 /* Duplicate name - change the first character to the next ASCII character */
2840 ++uniqueTempDirName
[1];
2841 /* Make sure it isn't a colon! */
2842 if ( uniqueTempDirName
[1] == ':' )
2844 ++uniqueTempDirName
[1];
2846 /* Don't go too far... */
2847 ++uniqueNameoverflow
;
2849 } while ( (error
== dupFNErr
) && (uniqueNameoverflow
<= 64) ); /* 64 new files per 1/60th second - not likely! */
2850 if ( error
== noErr
)
2852 /* Move the object to the folder with uniqueTempDirID for renaming */
2853 error
= CatMove(realVRefNum
, realParID
, realName
, uniqueTempDirID
, NULL
);
2854 if ( error
== noErr
)
2856 /* Rename the object */
2857 error
= HRename(realVRefNum
, uniqueTempDirID
, realName
, copyName
);
2858 if ( error
== noErr
)
2860 /* Move object to its new home */
2861 error
= CatMove(realVRefNum
, uniqueTempDirID
, copyName
, dstDirID
, dstpathName
);
2862 if ( error
!= noErr
)
2864 /* Error handling: rename object back to original name - ignore errors */
2865 (void) HRename(realVRefNum
, uniqueTempDirID
, copyName
, realName
);
2868 if ( error
!= noErr
)
2870 /* Error handling: move object back to original location - ignore errors */
2871 (void) CatMove(realVRefNum
, uniqueTempDirID
, realName
, realParID
, NULL
);
2874 /* Done with ourTempDir, so delete it - ignore errors */
2875 (void) HDelete(realVRefNum
, uniqueTempDirID
, NULL
);
2885 /*****************************************************************************/
2887 pascal OSErr
FSpMoveRenameCompat(const FSSpec
*srcSpec
,
2888 const FSSpec
*dstSpec
,
2889 ConstStr255Param copyName
)
2891 /* make sure the FSSpecs refer to the same volume */
2892 if (srcSpec
->vRefNum
!= dstSpec
->vRefNum
)
2893 return (diffVolErr
);
2894 return ( HMoveRenameCompat(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
,
2895 dstSpec
->parID
, dstSpec
->name
, copyName
) );
2898 /*****************************************************************************/
2900 pascal OSErr
BuildAFPVolMountInfo(short flags
,
2910 AFPVolMountInfoPtr
*afpInfoPtr
)
2912 MyAFPVolMountInfoPtr infoPtr
;
2915 /* Allocate the AFPXVolMountInfo record */
2916 infoPtr
= (MyAFPVolMountInfoPtr
)NewPtrClear(sizeof(MyAFPVolMountInfo
));
2917 if ( infoPtr
!= NULL
)
2919 /* Fill in an AFPVolMountInfo record that can be passed to VolumeMount */
2920 infoPtr
->length
= sizeof(MyAFPVolMountInfo
);
2921 infoPtr
->media
= AppleShareMediaType
;
2922 infoPtr
->flags
= flags
;
2923 infoPtr
->nbpInterval
= nbpInterval
;
2924 infoPtr
->nbpCount
= nbpCount
;
2925 infoPtr
->uamType
= uamType
;
2927 infoPtr
->zoneNameOffset
= offsetof(MyAFPVolMountInfo
, zoneName
);
2928 infoPtr
->serverNameOffset
= offsetof(MyAFPVolMountInfo
, serverName
);
2929 infoPtr
->volNameOffset
= offsetof(MyAFPVolMountInfo
, volName
);
2930 infoPtr
->userNameOffset
= offsetof(MyAFPVolMountInfo
, userName
);
2931 infoPtr
->userPasswordOffset
= offsetof(MyAFPVolMountInfo
, userPassword
);
2932 infoPtr
->volPasswordOffset
= offsetof(MyAFPVolMountInfo
, volPassword
);
2934 BlockMoveData(zoneName
, infoPtr
->zoneName
, sizeof(Str32
));
2935 BlockMoveData(serverName
, infoPtr
->serverName
, sizeof(Str32
));
2936 BlockMoveData(volName
, infoPtr
->volName
, sizeof(Str27
));
2937 BlockMoveData(userName
, infoPtr
->userName
, sizeof(Str31
));
2938 BlockMoveData(userPassword
, infoPtr
->userPassword
, sizeof(Str8
));
2939 BlockMoveData(volPassword
, infoPtr
->volPassword
, sizeof(Str8
));
2941 *afpInfoPtr
= (AFPVolMountInfoPtr
)infoPtr
;
2952 /*****************************************************************************/
2954 pascal OSErr
RetrieveAFPVolMountInfo(AFPVolMountInfoPtr afpInfoPtr
,
2958 StringPtr serverName
,
2965 /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
2966 if ( afpInfoPtr
->media
== AppleShareMediaType
)
2968 *flags
= afpInfoPtr
->flags
;
2969 *uamType
= afpInfoPtr
->uamType
;
2971 if ( afpInfoPtr
->zoneNameOffset
!= 0)
2973 tempPtr
= (StringPtr
)((long)afpInfoPtr
+ afpInfoPtr
->zoneNameOffset
);
2974 BlockMoveData(tempPtr
, zoneName
, tempPtr
[0] + 1);
2977 if ( afpInfoPtr
->serverNameOffset
!= 0)
2979 tempPtr
= (StringPtr
)((long)afpInfoPtr
+ afpInfoPtr
->serverNameOffset
);
2980 BlockMoveData(tempPtr
, serverName
, tempPtr
[0] + 1);
2983 if ( afpInfoPtr
->volNameOffset
!= 0)
2985 tempPtr
= (StringPtr
)((long)afpInfoPtr
+ afpInfoPtr
->volNameOffset
);
2986 BlockMoveData(tempPtr
, volName
, tempPtr
[0] + 1);
2989 if ( afpInfoPtr
->userNameOffset
!= 0)
2991 tempPtr
= (StringPtr
)((long)afpInfoPtr
+ afpInfoPtr
->userNameOffset
);
2992 BlockMoveData(tempPtr
, userName
, tempPtr
[0] + 1);
3005 /*****************************************************************************/
3007 pascal OSErr
BuildAFPXVolMountInfo(short flags
,
3018 unsigned long alternateAddressLength
,
3019 void *alternateAddress
,
3020 AFPXVolMountInfoPtr
*afpXInfoPtr
)
3023 MyAFPXVolMountInfoPtr infoPtr
;
3026 /* Calculate the size of the AFPXVolMountInfo record */
3027 infoSize
= sizeof(MyAFPXVolMountInfo
) + alternateAddressLength
- 1;
3029 /* Allocate the AFPXVolMountInfo record */
3030 infoPtr
= (MyAFPXVolMountInfoPtr
)NewPtrClear(infoSize
);
3031 if ( infoPtr
!= NULL
)
3033 /* Fill in an AFPXVolMountInfo record that can be passed to VolumeMount */
3034 infoPtr
->length
= infoSize
;
3035 infoPtr
->media
= AppleShareMediaType
;
3036 infoPtr
->flags
= flags
;
3037 if ( alternateAddressLength
!= 0 )
3039 /* make sure the volMountExtendedFlagsBit is set if there's extended address info */
3040 infoPtr
->flags
|= volMountExtendedFlagsMask
;
3041 /* and set the only extendedFlags bit we know about */
3042 infoPtr
->extendedFlags
= kAFPExtendedFlagsAlternateAddressMask
;
3046 /* make sure the volMountExtendedFlagsBit is clear if there's no extended address info */
3047 infoPtr
->flags
&= ~volMountExtendedFlagsMask
;
3048 /* and clear the extendedFlags */
3049 infoPtr
->extendedFlags
= 0;
3051 infoPtr
->nbpInterval
= nbpInterval
;
3052 infoPtr
->nbpCount
= nbpCount
;
3053 infoPtr
->uamType
= uamType
;
3055 infoPtr
->zoneNameOffset
= offsetof(MyAFPXVolMountInfo
, zoneName
);
3056 infoPtr
->serverNameOffset
= offsetof(MyAFPXVolMountInfo
, serverName
);
3057 infoPtr
->volNameOffset
= offsetof(MyAFPXVolMountInfo
, volName
);
3058 infoPtr
->userNameOffset
= offsetof(MyAFPXVolMountInfo
, userName
);
3059 infoPtr
->userPasswordOffset
= offsetof(MyAFPXVolMountInfo
, userPassword
);
3060 infoPtr
->volPasswordOffset
= offsetof(MyAFPXVolMountInfo
, volPassword
);
3061 infoPtr
->uamNameOffset
= offsetof(MyAFPXVolMountInfo
, uamName
);
3062 infoPtr
->alternateAddressOffset
= offsetof(MyAFPXVolMountInfo
, alternateAddress
);
3064 BlockMoveData(zoneName
, infoPtr
->zoneName
, sizeof(Str32
));
3065 BlockMoveData(serverName
, infoPtr
->serverName
, sizeof(Str32
));
3066 BlockMoveData(volName
, infoPtr
->volName
, sizeof(Str27
));
3067 BlockMoveData(userName
, infoPtr
->userName
, sizeof(Str31
));
3068 BlockMoveData(userPassword
, infoPtr
->userPassword
, sizeof(Str8
));
3069 BlockMoveData(volPassword
, infoPtr
->volPassword
, sizeof(Str8
));
3070 BlockMoveData(uamName
, infoPtr
->uamName
, sizeof(Str32
));
3071 BlockMoveData(alternateAddress
, infoPtr
->alternateAddress
, alternateAddressLength
);
3073 *afpXInfoPtr
= (AFPXVolMountInfoPtr
)infoPtr
;
3084 /*****************************************************************************/
3086 pascal OSErr
RetrieveAFPXVolMountInfo(AFPXVolMountInfoPtr afpXInfoPtr
,
3090 StringPtr serverName
,
3094 unsigned long *alternateAddressLength
,
3095 AFPAlternateAddress
**alternateAddress
)
3098 Ptr alternateAddressStart
;
3099 Ptr alternateAddressEnd
;
3100 Size alternateAddressDataSize
;
3104 /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
3105 if ( afpXInfoPtr
->media
== AppleShareMediaType
)
3107 /* default to noErr */
3110 /* Is this an extended record? */
3111 if ( (afpXInfoPtr
->flags
& volMountExtendedFlagsMask
) != 0 )
3113 if ( ((afpXInfoPtr
->extendedFlags
& kAFPExtendedFlagsAlternateAddressMask
) != 0) &&
3114 (afpXInfoPtr
->alternateAddressOffset
!= 0) )
3117 alternateAddressStart
= (Ptr
)((long)afpXInfoPtr
+ afpXInfoPtr
->alternateAddressOffset
);
3118 alternateAddressEnd
= alternateAddressStart
+ 1; /* skip over alternate address version byte */
3119 addressCount
= *(UInt8
*)alternateAddressEnd
; /* get the address count */
3120 ++alternateAddressEnd
; /* skip over alternate address count byte */
3121 /* alternateAddressEnd now equals &AFPAlternateAddress.fAddressList[0] */
3122 while ( addressCount
!= 0 )
3124 /* parse the address list to find the end */
3125 alternateAddressEnd
+= *(UInt8
*)alternateAddressEnd
; /* add length of each AFPTagData record */
3128 /* get the size of the alternateAddressData */
3129 alternateAddressDataSize
= alternateAddressEnd
- alternateAddressStart
;
3130 /* allocate memory for it */
3131 *alternateAddress
= (AFPAlternateAddress
*)NewPtr(alternateAddressDataSize
);
3132 if ( *alternateAddress
!= NULL
)
3134 /* and return the data */
3135 BlockMoveData(alternateAddressStart
, *alternateAddress
, alternateAddressDataSize
);
3136 *alternateAddressLength
= alternateAddressDataSize
;
3140 /* no memory - fail now */
3145 if ( error
== noErr
) /* fill in more output parameters if everything is OK */
3147 if ( afpXInfoPtr
->uamNameOffset
!= 0 )
3149 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->uamNameOffset
);
3150 BlockMoveData(tempPtr
, uamName
, tempPtr
[0] + 1);
3155 if ( error
== noErr
) /* fill in more output parameters if everything is OK */
3157 *flags
= afpXInfoPtr
->flags
;
3158 *uamType
= afpXInfoPtr
->uamType
;
3160 if ( afpXInfoPtr
->zoneNameOffset
!= 0 )
3162 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->zoneNameOffset
);
3163 BlockMoveData(tempPtr
, zoneName
, tempPtr
[0] + 1);
3166 if ( afpXInfoPtr
->serverNameOffset
!= 0 )
3168 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->serverNameOffset
);
3169 BlockMoveData(tempPtr
, serverName
, tempPtr
[0] + 1);
3172 if ( afpXInfoPtr
->volNameOffset
!= 0 )
3174 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->volNameOffset
);
3175 BlockMoveData(tempPtr
, volName
, tempPtr
[0] + 1);
3178 if ( afpXInfoPtr
->userNameOffset
!= 0 )
3180 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->userNameOffset
);
3181 BlockMoveData(tempPtr
, userName
, tempPtr
[0] + 1);
3193 /*****************************************************************************/
3195 pascal OSErr
GetUGEntries(short objType
,
3198 long *actEntryCount
,
3202 OSErr error
= noErr
;
3203 UGEntry
*endEntryArray
;
3205 pb
.objParam
.ioObjType
= objType
;
3207 for ( endEntryArray
= entries
+ reqEntryCount
; (entries
< endEntryArray
) && (error
== noErr
); ++entries
)
3209 pb
.objParam
.ioObjNamePtr
= (StringPtr
)entries
->name
;
3210 pb
.objParam
.ioObjID
= *objID
;
3211 /* Files.h in the universal interfaces, PBGetUGEntrySync takes a CMovePBPtr */
3212 /* as the parameter. Inside Macintosh and the original glue used HParmBlkPtr. */
3213 /* A CMovePBPtr works OK, but this will be changed in the future back to */
3214 /* HParmBlkPtr, so I'm just casting it here. */
3215 error
= PBGetUGEntrySync(&pb
);
3216 if ( error
== noErr
)
3218 entries
->objID
= *objID
= pb
.objParam
.ioObjID
;
3219 entries
->objType
= objType
;
3227 /*****************************************************************************/