FileAccess.c

/*
    File:       FileAccess.c
    
    Contains:   The underlying BSD layer on Mac OS X 10.0->10.1 does not implement "mandatory"
                locks on files.  This means that if one application opens a file for exclusive 
                write access, it really doesn't have "exclusive" access, and another application
                can write over the file.  This file contains some basic routines to implement a 
                technique to allow exclusive access between applications. This technique used 
                "lockfiles" to signal a file is already open.  We store the PSN within the 
                "lockfile" to guard against applications crashing with open files.  This method 
                should also work between classic and carbon applications.
                
                This file demonstrates how to override FSpOpenDF and FSClose to utilize this
                method of advisory locks.  There are many other variants of the Open cals 
                including: PBHOpenDF, PBHOpenRF, PBHOpen, PBOpenFork, FSOpenFork, HOpen, etc.
                
                "Advisory" locks via fcntl() and flock() are implemented within the BSD layer
                and therefore would only be available from native applications.  For more info 
                on "Advisory" locks use the man pages or reference "Advanced Programming in the 
                UNIX Environment", by Richard Stevens, pages 367-382, or contact Developer 
                Technical Support.
 
    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                ("Apple") in consideration of your agreement to the following terms, and your
                use, installation, modification or redistribution of this Apple software
                constitutes acceptance of these terms.  If you do not agree with these terms,
                please do not use, install, modify or redistribute this Apple software.
 
                In consideration of your agreement to abide by the following terms, and subject
                to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
                copyrights in this original Apple software (the "Apple Software"), to use,
                reproduce, modify and redistribute the Apple Software, with or without
                modifications, in source and/or binary forms; provided that if you redistribute
                the Apple Software in its entirety and without modifications, you must retain
                this notice and the following text and disclaimers in all such redistributions of
                the Apple Software.  Neither the name, trademarks, service marks or logos of
                Apple Computer, Inc. may be used to endorse or promote products derived from the
                Apple Software without specific prior written permission from Apple.  Except as
                expressly stated in this notice, no other rights or licenses, express or implied,
                are granted by Apple herein, including but not limited to any patent rights that
                may be infringed by your derivative works or by other works in which the Apple
                Software may be incorporated.
 
                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                COMBINATION WITH YOUR PRODUCTS.
 
                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
    Copyright © 2001 Apple Computer, Inc., All Rights Reserved
*/
 
 
#ifdef __APPLE_CC__
#include <Carbon/Carbon.h>
#else
#include <Carbon.h>
#endif
 
#include    <stdio.h>
#include    <string.h>
#include    "Main.h"
 
static  OSErr   WriteLockTheFile( FSSpec *spec );
 
static  OSErr   OpenGrabBagSharedFile( short permission, short *refNum );
static  OSErr   ReadFromGrabBagSharedFile( WindowRef window );
static  OSErr   WriteToGrabBagSharedFile( WindowRef window );
static  void    UpdateErrorMessage( WindowRef window, char *errMsg, OSErr err );
 
 
 
extern  GlobalsStruct   g;
 
 
void    OpenGrabBagSharedFileWithWriteAccess( WindowRef window )
{
    OSErr   err;
    
    err = OpenGrabBagSharedFile( fsRdWrPerm, &g.sharedFileRefNum );
    
    UpdateErrorMessage( window, "Open For Writing", err );
}
 
void    SaveDataToGrabBagSharedFile( WindowRef window )
{
    OSErr   err;
    
    err = WriteToGrabBagSharedFile( window );
    
    UpdateErrorMessage( window, "Save To File", err );
}
 
void    DisplayDataFromGrabBagSharedFile( WindowRef window )
{
    OSErr   err;
    
    err = ReadFromGrabBagSharedFile( window );
    
    UpdateErrorMessage( window, "Read From File", err );
}
 
void    CloseGrabBagSharedFile( WindowRef window )
{
    OSErr   err;
    
    err = XFSClose( &g.sharedFileRefNum );
    
    UpdateErrorMessage( window, "Close File", err );
}
 
static  void    UpdateErrorMessage( WindowRef window, char *errMsg, OSErr err )
{
    char            s[1024];
    ControlRef      control;
    ControlID       controlID   = { 'eror', 0 };
    
    sprintf( s, "%s\r\rOSErr = %d", errMsg, err );
 
    GetControlByID( window, &controlID, &control );
    (void) SetControlData( control, 0, kControlStaticTextTextTag, strlen(s), s );
    Draw1Control( control );
}
 
static  OSErr   OpenGrabBagSharedFile( short permission, short *refNum )
{
    short   foundVRefNum;
    long    foundDirID;
    FSSpec  spec;
    OSErr   err;
            
    err = FindFolder( kOnAppropriateDisk, kSharedUserDataFolderType, true, &foundVRefNum, &foundDirID );
    if ( err != noErr ) goto Bail;
    
    err = FSMakeFSSpec( foundVRefNum, foundDirID, "\pGrabBagSharedFile", &spec );
    if ( err == fnfErr )    err = FSpCreate( &spec, 0, 0, smSystemScript );
    if ( err != noErr )     goto Bail;
    
    err = XFSpOpenDF( &spec, permission, refNum );
    
Bail:
    return( err );
}
 
 
 
static  OSErr   ReadFromGrabBagSharedFile( WindowRef window )
{
    OSErr           err;
    ControlRef      control;
    char            buffer[1024];
    long            count   = 1024;
    short           refNum  = g.sharedFileRefNum;
    ControlID       controlID   = { 'FDta', 0 };
    
    if ( g.sharedFileRefNum == 0 )
    {
        err = OpenGrabBagSharedFile( fsRdPerm, &refNum );
        if ( err != noErr ) goto Bail;
    }
        
    (void) SetFPos( refNum, fsFromStart, 0);
    err = FSRead( refNum, &count, buffer );
    if ( (err != noErr) && (err != eofErr) ) goto Bail;
    
    if ( g.sharedFileRefNum == 0 )
        FSClose( refNum );
    
    GetControlByID( window, &controlID, &control );
    err = SetControlData( control, 0, kControlStaticTextTextTag, count, buffer );
    Draw1Control( control );
    
Bail:
    return( err );
}
 
 
static  OSErr   WriteToGrabBagSharedFile( WindowRef window )
{
    OSErr           err;
    ControlRef      control;
    char            buffer[1024];
    long            count   = 1024;
    short           refNum  = g.sharedFileRefNum;
    ControlID       controlID   = { 'FDta', 0 };
    
    if ( g.sharedFileRefNum == 0 )
    {
        err = OpenGrabBagSharedFile( fsRdWrPerm, &refNum );
        if ( err != noErr ) goto Bail;
    }
        
    GetControlByID( window, &controlID, &control );
    (void) GetControlData( control, 0, kControlStaticTextTextTag, count, buffer, &count );
 
    (void) SetFPos( refNum, fsFromStart, 0);
    err = FSWrite( refNum, &count, buffer );
    if ( err != noErr ) goto Bail;
    
    (void) SetEOF( refNum, count );
    
Bail:
    if ( g.sharedFileRefNum == 0 )
        XFSClose( &refNum );
 
    return( err );
}
 
 
 
OSErr   XFSClose( short *refNum )
{
    FSSpec      lockFileSpec;
    Str31       name;
    OSErr       err;
    FCBPBRec    fcbRec;
    
    fcbRec.ioRefNum     = *refNum;
    fcbRec.ioNamePtr    = name;
    fcbRec.ioFCBIndx    = 0;
    fcbRec.ioVRefNum    = 0;
    fcbRec.ioFCBParID   = 0;
 
    err = PBGetFCBInfoSync( &fcbRec );
    if ( err != noErr ) return( fnOpnErr );
    
    err = FSClose( *refNum );
    if ( err != noErr ) goto Bail;
    *refNum = 0;
 
    if ( name[0] > 26 ) name[0] = 26;       //  truncate the name to 31 chars
    PLstrcat( name, "\p.lock" );
 
    err = FSMakeFSSpec( fcbRec.ioFCBVRefNum, fcbRec.ioFCBParID, name, &lockFileSpec );
    if ( err == noErr )                     //  should always be there in this case
        err = FSpDelete( &lockFileSpec );
    else if ( err == fnfErr )
        err = noErr;
    
Bail:
    return( err );
}
 
 
OSErr   XFSpOpenDF( FSSpec *spec, short permission, short *refNum )
{
    OSErr   err;
    
    if ( permission == fsRdWrPerm )
    {
        err = WriteLockTheFile( spec );
        if ( err != noErr ) goto Bail;
    }
 
    err = FSpOpenDF( spec, permission, refNum );
 
Bail:
    return( err );
}
 
 
static  OSErr   WriteLockTheFile( FSSpec *spec )
{
    ProcessSerialNumber     psn;
    ProcessInfoRec          processInfo;
    OSErr                   err;
    FSSpec                  lockFileSpec    = *spec;
    Boolean                 isWriteLocked   = true;
    short                   refNum          = 0;
    long                    count           = sizeof( psn );
    
    if ( lockFileSpec.name[0] > 26 )    lockFileSpec.name[0]    = 26;       //  truncate the name to 31 chars
    PLstrcat( lockFileSpec.name, "\p.lock" );
    
    err = FSMakeFSSpec( spec->vRefNum, spec->parID, lockFileSpec.name, &lockFileSpec );
    if( err == fnfErr )
    {
        isWriteLocked   = false;
    }
    else
    {
        if ( err == noErr )
        {
            err = FSpOpenDF( &lockFileSpec, fsRdPerm, &refNum );
            if ( err != noErr ) goto Bail;
            err = FSRead( refNum, &count, &psn );
            if ( count == sizeof(psn) )
            {
                FSSpec  sp;
                processInfo.processAppSpec      =   &sp;
                processInfo.processName         =   NULL;
                processInfo.processInfoLength   =   sizeof(ProcessInfoRec);
                err = GetProcessInformation( &psn, &processInfo );
                if ( err != noErr )
                {
                    (void) FSClose( refNum );
                    refNum  = 0;
                    (void) FSpDelete( &lockFileSpec );
                    isWriteLocked   = false;
                }
                else
                {
                    err = wrPermErr;
                }
            }
            else
            {
                (void) FSpDelete( &lockFileSpec );
                isWriteLocked   = false;
            }
        }
    }
    
    if ( isWriteLocked == false )
    {
        err = GetCurrentProcess( &psn );
        if ( err != noErr ) goto Bail;
        err = FSpCreate( &lockFileSpec, 0, 0, smSystemScript );                 //  create the lock file
        if ( err != noErr ) goto Bail;
        err = FSpOpenDF( &lockFileSpec, fsRdWrPerm, &refNum );
        if ( err == noErr )
        {
            count   = sizeof(psn);
            (void) FSWrite( refNum, &count, &psn );
        }
        if ( refNum != 0 )  (void) FSClose( refNum );
        return( noErr );
    }
    
Bail:
    if ( refNum != 0 )  (void) FSClose( refNum );
    
    return( wrPermErr );
}