Files
mars-nwe/src/nwnss/comn/common/comnFile.c
2026-06-16 16:10:41 +02:00

7275 lines
224 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/****************************************************************************
|
| (C) Copyright 1995-1997 Novell, Inc.
| All Rights Reserved.
|
| This program is free software; you can redistribute it and/or
| modify it under the terms of version 2 of the GNU General Public
| License as published by the Free Software Foundation.
|
| This program is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License
| along with this program; if not, contact Novell, Inc.
|
| To contact Novell about this file by physical or electronic mail,
| you may find current contact information at www.novell.com
|
|***************************************************************************
|
| NetWare Advance File Services (NSS) Initialization module
|
|---------------------------------------------------------------------------
|
| $Author: taysom $
| $Date: 2008-08-27 01:43:49 +0530 (Wed, 27 Aug 2008) $
|
| $RCSfile$
| $Revision: 2453 $
|
|---------------------------------------------------------------------------
| This module is used to:
| This contains the top level internal file interfaces. All external
| interfaces call these routines.
+-------------------------------------------------------------------------*/
#include <procdefs.h>
#include <bits.h>
#include <config.h>
#include <modify.h>
#include <omni.h> /* NSS Library */
#include <encp.h>
#include <nspace.h>
#include <connect.h>
#include <connexp.h>
#include <scs.h>
#define _NSS_ENCP_H_ /* flag we have included ENCP.H already*/
#include <zOmni.h>
#include <stdlib.h>
#include <xUnicode.h>
#include <utc.h>
#include <stdio.h>
#include <xError.h>
#include <string.h>
#ifdef MARS_NWE_NWNSS_USERSPACE
#include <stdint.h>
#endif
#include "hardLinkBeast.h"
#include "comnPublics.h"
#include "comnParams.h"
#include "comnBeasts.h"
#include "comnIO.h"
#include "comnLock.h"
#include "nameScan.h"
#include "msgName.h"
#include "msgIO.h"
#include "zParams.h"
#include "searchMap.h"
#include "fileHandle.h"
#include "pssConnection.h"
#include "comnAuthorize.h"
#include "volume.h"
#include "name.h"
#include "nameSpace.h"
#include "eventSys.h"
#include "comnDataStream.h"
#include "reserveResources.h"
#include "nssFSHooks.h"
#include "comnZAS.h"
#include "adminVolume.h"
#include "beastStartup.h"
#include "mgmt.h"
#include "guid.h"
#include "cmNSS.h"
#include "msg.h"
#include "opLock.h"
#include "sbsMFL.h"
#include "purgeDir.h"
#include "comnTask.h"
#include "uxaction.h"
#include "objectIDStore.h"
#include "csaLease.h"
#include "cro.h"
#include "comnCompress.h"
#include "macNSpace.h"
#include "unixNSpace.h"
#include "comnVariableData.h"
#include "zasAuthModel.h"
#ifdef MARS_NWE_NWNSS_USERSPACE
#include "inst.h"
#endif
#if zLINUX
#include "lsa.h"
#endif
extern LONG GetCurrentTime();
#define USER_DELAY_COUNTER 20 /** 10 times .5 secs for 5 sec wait **/
#define USER_DELAY_TIME 500 /** 500 milliseconds **/
#ifdef MARS_NWE_NWNSS_USERSPACE
/* comnFile userspace status bridge: Msg_s.sys.where is a QUAD carrier here. */
#undef SetErrnoFromStatus
#define SetErrnoFromStatus(_genMsg, _msg) \
((_genMsg)->errStatus = (_msg)->sys.status, \
((_genMsg)->errStatusSetter = (char *)(uintptr_t)((_msg)->sys.where)))
static inline struct inode *nwnss_igrab(struct inode *inode)
{
return inode;
}
static inline void nwnss_iput(struct inode *inode)
{
(void)inode;
}
#define igrab(_inode) nwnss_igrab(_inode)
#define iput(_inode) nwnss_iput(_inode)
#endif
#define ODDNULL ((void *)1) /* Used to indicate a NULL pointer but
* it is distinguished from NULL so we
* can differentiate between legacy pointer
* and NSS.
*/
/*
* Don't allow these file attributes to be modified by modify info
* because other things have to happen.
*/
#define DO_NOT_MODIFY_MASK (zFA_IS_LINK | zFA_SUBDIRECTORY | zFA_HARDLINK \
| zFA_IS_ADMIN_LINK | zFA_DATA_STREAM_IS_COMPRESSED)
BOOL COMN_IgnoreDoNotModifyMask = FALSE;
extern STATUS MSG_GetFileMap (zNSSMsg_s *msg);
BOOL COMN_OldCreateRights = FALSE; /* Enables the old broken create rights
* where we forced zRR_WRITE_ACCESS
* rights on everybody.
*/
/**************************************************************************
* Send all the necessary pre-close events.
***************************************************************************/
void MSG_Notify (FileHandle_s *fileHandle)
{
GeneralMsg_s genMsg;
STATUS status;
struct EventBlock evBlk;
EventCloseEnter_s enter;
EventCloseExit_s exit;
FSHooks_Close_s parms;
/* It is possible for COMN_Close to be called from a shutdown of a
* connection or a volume, and it is possible that the file handle was
* not fully initialized because the open was still in progress.
* If this is the case, do not send events because the open never finished..
*/
if ((fileHandle->file == NULL) || (fileHandle->dataStream == NULL))
{
return;
}
if ((fileHandle->fhState & FH_NDS_OPEN) &&
(LegacyEventReport[EventReport_CloseFile]))
{
((STATUS (*)(FileHandle_s *))LegacyEventReport[EventReport_CloseFile])(fileHandle);
}
if (!(fileHandle->grantedRights & zRR_PREVENT_FS_HOOKS))
{
COMN_SETUP_GENERAL_MSG( &genMsg, fileHandle->connID,
fileHandle->taskID, zSAGENT_NONE);
CHECK_FSHOOKS_CLOSE_ENTER(CloseFile_Hook, &parms, &genMsg,
fileHandle->door.dr_key);
INIT_EVENT_ID_STATUS(fileHandle->enterExitID, exit.enterRetStatus);
if (NEBEventInfo[EVENT_Close_Enter].consumers)
{
INIT_CLOSE_ENTER_EVENT(&genMsg, evBlk, EVENT_Close_Enter,
enter, fileHandle, fileHandle->enterExitID);
ZOS_ProduceEvent(status, &evBlk);
CHECK_ENTER_RETURN( &genMsg, status, evBlk, exit, cantStopClose);
}
cantStopClose:
fileHandle->enterRetStatus = exit.enterRetStatus;
}
}
/**************************************************************************
* (Greg's contribution)
* Main Entry: 1com€ment
* Pronunciation: 'k€-"ment
* Function: noun
* Etymology: Middle English, from Late Latin commentum, from Latin, invention,
* from neuter of commentus, past participle of comminisci to invent, from
* com- + -minisci (akin to ment-, mens mind) -- more at MIND
* Date: 14th century
* 1 : COMMENTARY
* 2 : a note explaining, illustrating, or criticizing the meaning of a writing
* 3 a : an observation or remark expressing an opinion or attitude b : a
* judgment expressed indirectly
*
***************************************************************************/
/**************************************************************************
* Common internal interface to ASYNC CLOSE a dataStream
*
* COMN_Close will schedule a WorkToDo to asynchronously close a file
* MSG_DestroyKey path in that case does not free the object, so we need
* to do that.
*
***************************************************************************/
void COMN_CloseAsync (FsmLite_s *fsm, FileHandle_s *fileHandle)
{
zASSERT(fileHandle->fhState & FH_CFS_ASYNC_CLOSE);
zASSERT(fileHandle->fhState & FH_CFS_OPEN);
COMN_Close(fileHandle);
if (--fileHandle->door.dr_obj.o_count == 0)
{
objCacheFree(&fileHandle->door.dr_obj);
}
}
/**************************************************************************
* Common internal interface to CLOSE a dataStream
***************************************************************************/
STATUS COMN_Close (FileHandle_s *fileHandle)
{
#ifndef __linux__ // LINUX_ConnMgr
StationControl *station;
#endif
NamedBeast_s *dataStream;
File_s *file;
STATUS status;
GeneralMsg_s genMsg;
NINT fileDeleted;
FSHooks_Close_s parms;
BOOL generateEvents = TRUE;
Key_t key;
struct inode *fh_inode;
typedef struct Stack_s {
struct EventBlock evBlk;
EventCloseExit_s exit;
} Stack_s;
STACK_ALLOC();
ASSERT_MPKNSS_LOCK();
COMN_ENABLE();
ENTER(TCOMMON, COMN_Close);
if ((fileHandle->fhState & FH_CFS_OPEN) &&
(!(fileHandle->fhState & FH_CFS_ASYNC_CLOSE)))
{
/* Sync closing the file, will result in a deadlock. So we decrement
* the granted rights count, to allow the rest of the code to continue
* and schedule a thread to asynchronously close the file.
* We make sure the counts are not decremented twice by setting bits
* in the filehandle.
*/
FH_DecrementRightCounts(fileHandle);
fileHandle->fhState |= FH_CFS_ASYNC_CLOSE;
if (QMEMBER( &fileHandle->bstLink))
{
DQ_RMV(fileHandle, bstLink);
}
WORK_Schedule(&fileHandle->FH_asyncCloseFsm,
(voidfunc_t)COMN_CloseAsync, (ADDR)fileHandle);
STACK_FREE();
RTN_STATUS(zERR_DONT_FREE_OBJECT);
}
/*
* Close should not require reserving any resources because it
* is freeing up resources.
*/
COMN_SETUP_GENERAL_MSG( &genMsg, fileHandle->connID,
fileHandle->taskID, zSAGENT_NONE);
/* It is possible for COMN_Close to be called from a shutdown of a
* connection or a volume, and it is possible that the file handle was
* not fully initialized because the open was still in progress.
* If this is the case, do not send events because the open never finished..
*/
if ((fileHandle->file == NULL) || (fileHandle->dataStream == NULL))
{
generateEvents = FALSE;
}
#ifndef __linux__ // LINUX_ConnMgr
/** Check if the Connection has this handle saved for Packet Burst **/
if ((GetConnectionServiceInformation(
fileHandle->pssConn->connectionNumber, &station) == 0) &&
(station != NULL) &&
(station->SCHandle == (LONG)fileHandle->door.dr_key))
{
station->SCHandle = (LONG)ODDNULL;
}
#endif
dataStream = fileHandle->dataStream;
file = fileHandle->file;
if (dataStream != NULL)
{
zASSERT(dataStream->NAMEDopenCount > 0);
if(--dataStream->NAMEDopenCount == 0)
Inst.file.openFilesCount--;
if (QMEMBER( &fileHandle->bstLink))
{
DQ_RMV(fileHandle, bstLink);
}
zASSERT((dataStream->NAMEDopenCount > 0)
|| (DQ_EMPTY(&dataStream->openFileHandles)));
if (dataStream->NAMEDbstState & BST_STATE_DIOMODE)
{
--dataStream->NAMEDdioModeCount;
if ((dataStream->NAMEDopenCount == 0) &&
(dataStream->NAMEDdioModeCount == 0))
{
dataStream->NAMEDbstState &= ~BST_STATE_DIOMODE;
}
}
dataStream->NAMEDcomnOps.BST_beastNotify( &genMsg,&dataStream->NAMEDroot,BST_NOTIFY_CLOSE);
/** As an optimization we should do the flush only if the file is not being
** deleted and after the alloc_ahead blocks have been truncated which is
** being done in FH_FreeOpenFile **/
/**
** if (closeFlags & zCLOSE_FLUSH)
** BST_flush( &genMsg,dataStream);
**/
/*
* Release any byte range locks
*/
if (dataStream->startLocks)
{
COMN_BeastUnlockByteRange( &genMsg, dataStream, fileHandle);
}
/*---------------------------------------------------------------------------
* Deal with deleting the dataStream on close
*---------------------------------------------------------------------------*/
/* If the closeFlag is set, check rights and mark the beast toBeDeleted */
if ((fileHandle->grantedRights & zRR_PURGE_IMMEDIATE_ON_CLOSE) &&
(dataStream == (NamedBeast_s *)file))
{
file->FILEattributes |= zFA_IMMEDIATE_PURGE;
}
/* This if statement is here for performance reasons. No need to
* latch the beast if these conditions are not true.
*/
if ((fileHandle->opLock != NULL) ||
(fileHandle->grantedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA) ||
(CM_COMPRESSION_ENABLED(dataStream->NAMEDvolume)) ||
(fileHandle->fhState & (FH_WRITE_SNAPSHOT | FH_READ_BACKUP)))
{
if (dataStream != (NamedBeast_s *)file)
X_LATCH(&file->FILEbeastLatch);
X_LATCH(&dataStream->NAMEDbeastLatch);
if (fileHandle->opLock != NULL)
{
OPLOCK_Free(fileHandle->opLock);
}
/* Perform compression-related processing */
if (!(dataStream->NAMEDattributes & zFA_SUBDIRECTORY))
{
GeneralMsg_s cmGenMsg;
/*
* Compression may have to read in the parent for the
* compressed but the connection can go away so setup
* a genMsg with connection 0.
*/
COMN_SETUP_GENERAL_MSG_NOSA( &cmGenMsg);
if ((fileHandle->grantedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA))
{
status = CM_accessCompFileDone(&cmGenMsg, &dataStream->NAMEDroot,
fileHandle->compBeast);
}
else if (CM_COMPRESSION_ENABLED(dataStream->NAMEDvolume))
{
/* The bit zRR_LEAVE_FILE_COMPRESSED can be a grantedRights
* for volumes that don't have compression
*/
zASSERT(fileHandle->compBeast == 0);
if (fileHandle->grantedRights & zRR_LEAVE_FILE_COMPRESSED)
{
CM_uncompFileAccessDone( &cmGenMsg, &dataStream->NAMEDroot);
}
}
}
/*
* NOTE: We need to cleanup the snapshot even if COW has been
* disabled.
*/
if (fileHandle->fhState & (FH_WRITE_SNAPSHOT | FH_READ_BACKUP))
{
CleanupSnapshotBeast(&dataStream->NAMEDroot);
}
UNX_LATCH(&dataStream->NAMEDbeastLatch);
if (dataStream != (NamedBeast_s *)file)
UNX_LATCH(&file->FILEbeastLatch);
}
}
/* For every open file, I have a use Count on a corresponding inode, so
* when we close the file, I decrement this useCount with iput.
*/
fh_inode = fileHandle->fh_inode;
if (fh_inode != NULL)
{
fileHandle->fh_inode = NULL;
MPKNSS_UNLOCK();
iput(fh_inode);
MPKNSS_LOCK();
}
aStack->exit.enterExitID = fileHandle->enterExitID;
aStack->exit.enterRetStatus = fileHandle->enterRetStatus;
key = fileHandle->door.dr_key;
/* This function deletes the file if it is marked for deletion */
fileDeleted = FH_FreeOpenFile(fileHandle);
if (fileHandle->FH_accessLease != NULL)
{
CRO_AccessLeaseRelease(fileHandle);
}
/* This will return zOK or the real error code */
status = GetErrno( &genMsg);
if (!(fileHandle->grantedRights & zRR_PREVENT_FS_HOOKS) && generateEvents)
{
CHECK_FSHOOKS_CLOSE_EXIT(CloseFile_Hook, &parms, &genMsg, key);
if (NEBEventInfo[EVENT_Close_Exit].consumers)
{
INIT_EVENT_BLOCK(aStack->evBlk, EVENT_Close_Exit, &aStack->exit);
INIT_CLOSE_EXIT_EVENT(aStack->exit, fileDeleted, status);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
}
STACK_FREE();
RTN_STATUS(status);
}
/****************************************************************************
* This routine will set up to call into LSA, so inode and dentry information
* for this beast (we need the name in case it is a hard link, then only the
* name can identify the corresponding dentry) can be cleaned up.
****************************************************************************/
STATUS COMN_LsaCleanupSetup(
NamedBeast_s *beast,
// cnt NINT nameUniquifier,
LSAInvalidateDentry_s *lsaInv)
{
NINT nsID = -1;
NINT namelen;
GeneralMsg_s genMsg;
if ( !COMN_IsDerivedFrom(beast, zFTYPE_FILE))
{
return zFAILURE;
}
lsaInv->lid_volID = beast->NAMEDvolume->VOLvolumeID;
if (Ptr_lsa_get_vol_namespace)
{
nsID = Ptr_lsa_get_vol_namespace(&lsaInv->lid_volID);
}
if (nsID == -1)
{
return zFAILURE;
}
COMN_SETUP_GENERAL_MSG_NO_CONNECTION_RESOLVE(&genMsg);
if ( COMN_GetNameFromBeast( &genMsg, beast, /* cnt nameUniquifier, */
nsID, zMAX_COMPONENT_NAME,
lsaInv->lid_name, &namelen) != zOK )
{
printk("<1> Error from COMN_GetNameFromBeast = %d\n", GetErrno(&genMsg));
// cnt printk("<1> COMN_GetNameFromBeast nameUniquifier = %d\n", nameUniquifier);
return zFAILURE;
}
return zOK;
}
/****************************************************************************
* Common internal interface to CREATE a beast. *
****************************************************************************/
STATUS COMN_Create(
GeneralMsg_s *genMsg,
NamingMsg_s *nameMsg,
CreateMsg_s *createMsg,
FileHandleIDP_s *retFileHandle)
{
return COMN_CreateWithCallback(genMsg, nameMsg, createMsg,
retFileHandle, NULL, NULL);
}
/****************************************************************************
* Common internal interface to CREATE a beast. *
* *
* callBack: if not NULL, caller wishes to write some additional *
* information on the newly created beast before it is committed.*
* *
****************************************************************************/
STATUS COMN_CreateWithCallback(
GeneralMsg_s *genMsg,
NamingMsg_s *nameMsg,
CreateMsg_s *createMsg,
FileHandleIDP_s *retFileHandle,
void *userParam,
STATUS (*createCallBack)(GeneralMsg_s *genMsg,
NamingMsg_s *nameMsg,
void *userParam))
{
NamedBeast_s *newBeast;
NamedBeast_s *tempBeast;
NamedBeast_s *existingBeast = NULL;
File_s *curDir;
Volume_s *curVol;
File_s *rightsCheckFile;
// cnt NINT nameUniquifier;
// cnt NINT existBeastNameUniquifier;
unicode_t *beastName = NULL;
BOOL beastNameAllocated = FALSE;
Xaction_s *xaction;
BOOL creatingDataStream = FALSE;
DataStreamCounts_s *dstrCounts = NULL;
NINT dstrFlag = 0;
STATUS status;
NINT beastNamesAdded = 0;
NINT dirNamesAdded = 0;
NINT beastNamesRemoved, dirNamesRemoved;
NINT i;
NamedBeast_s *cleanupSnapBeast = NULL;
NINT fixUpSnapReaders = 0;
BYTE fhState = 0;
FileHandle_s *fh;
BOOL delayedOutOfSpaceError = FALSE;
NINT latchType = NOTLATCHED;
NINT id;
#if NSS_DEBUG IS_ENABLED
ParentEntry_s *pentry;
#endif
BOOL lsaInvDentry = FALSE;
BOOL lsaDelDentry = FALSE;
HardLinkBeast_s *hl;
File_s *primaryBeast = NULL;
File_s *neighborBeast = NULL;
NameSpace_s *dosNameSpace = NULL;
typedef struct Stack_s {
GeneralMsg_s tmpMsg;
Time_t currentTime;
Zid_t parentZid;
Zid_t rightsCheckParentZid;
SQUAD useableBlksVOL;
BOOL doRightsCheck;
BOOL dontDoVisibilityCheck;
NINT tempLen;
FSHooks_F3OpenCreate_s parms;
char asciiBeastName[zMAX_COMPONENT_NAME];
struct EventBlock evBlk;
EventCreateEnter_s enter;
EventCreateExit_s exit;
LSAInvalidateDentry_s lsaInv;
Zid_t lsaPzid;
} Stack_s;
STACK_ALLOC();
COMN_ENABLE();
ENTER(TCOMMON, COMN_Create);
ASSERT_MPKNSS_LOCK();
RESERVE_RSRC(NAME_RESERVE);
aStack->doRightsCheck =
((nameMsg->parseFlags & NAMPFL_dontDoRightsChecks) == 0) ? TRUE : FALSE;
aStack->dontDoVisibilityCheck =
(nameMsg->parseFlags & NAMPFL_dontDoVisibilityChecks) ? TRUE : FALSE;
if (createMsg->attributes & zFA_SUBDIRECTORY)
{
if (retFileHandle && (genMsg->saID == zSAGENT_NETWARE))
{
SetErrno(genMsg, zERR_DIR_CANNOT_BE_OPENED);
goto cleanup_errorAndReturn;
}
//9/24/97 Brenda removed this, it really happens (transaction bit set...)
// zASSERT(!(createMsg->attributes & ~zFA_VALID_DIRECTORY_ATTRIBUTES));
createMsg->attributes &= zFA_VALID_DIRECTORY_ATTRIBUTES;
}
else
{
//9/24/97 Brenda removed this, it really happens (transaction bit set...)
// zASSERT(!(createMsg->attributes & ~zFA_VALID_FILE_ATTRIBUTES));
createMsg->attributes &= zFA_VALID_FILE_ATTRIBUTES;
}
/*---------------------------------------------------------------------------
* Check for presence of BOTH zFA_COMPRESS_FILE_IMMEDIATELY and
* zFA_DO_NOT_COMPRESS_FILE. If both are here, clear the
* zFA_COMPRESS_FILE_IMMEDIATELY bit and continue on. This functionality
* matches what occurs in the legacy file system.
*---------------------------------------------------------------------------*/
if((createMsg->attributes & zFA_COMPRESS_FILE_IMMEDIATELY) &&
(createMsg->attributes & zFA_DO_NOT_COMPRESS_FILE))
{
createMsg->attributes &= ~zFA_COMPRESS_FILE_IMMEDIATELY;
}
/*---------------------------------------------------------------------------
* Check File Type
*---------------------------------------------------------------------------*/
if ((!(COMN_IsIDDerivedFrom(createMsg->beastClassID,zFTYPE_FILE))) &&
(createMsg->beastClassID != zFTYPE_NAMED_DATA_STREAM) &&
(createMsg->beastClassID != zFTYPE_EXTENDED_ATTRIBUTE))
{
SetErrno(genMsg, zERR_INVALID_TYPE_FILE);
goto cleanup_errorAndReturn;
}
/*---------------------------------------------------------------------------
* Mask Valid Requested Rights - NOTE -- requested Rights are only valid
* if the user os also opening the file (IE retFileHandle is non-NULL)
*---------------------------------------------------------------------------*/
if (retFileHandle != NULL)
{
zASSERT(!(createMsg->requestedRights & ~zVALID_FILE_REQ_RIGHTS));
createMsg->requestedRights &= zVALID_FILE_REQ_RIGHTS;
if (createMsg->requestedRights & zRR_ALLOW_SECURE_DIRECTORY_ACCESS)
{
genMsg->flags |= ALLOW_SECURE_ACCESS;
}
/*---------------------------------------------------------------------------
* If retFileHandle is non-NULL, the caller wants to open the new file too...
*---------------------------------------------------------------------------*/
retFileHandle->key = 0;
}
/*---------------------------------------------------------------------------
* If neither read nor write access is desired, we might prevent FS_Hooks calls
*---------------------------------------------------------------------------*/
if((createMsg->requestedRights & (zRR_READ_ACCESS | zRR_WRITE_ACCESS))== 0)
{
/* if it's NOT a subdirectory, then prevent FS_Hooks calls */
if(!(createMsg->attributes & zFA_SUBDIRECTORY))
{
createMsg->requestedRights |= zRR_PREVENT_FS_HOOKS;
}
}
/*---------------------------------------------------------------------------
* Lookup the directory in which the file is to be created, and also
* validate that "create" is allowed in this directory
*---------------------------------------------------------------------------*/
if (nameMsg->parseMode == NAMPMODE_Undefined)
nameMsg->parseMode = NAMPMODE_DoNotResolveLeafName;
/*
* Should be able to parse the create path even if you don't have visibility
* if you have create rights.
*/
zASSERT(nameMsg->latchType == XLATCHED);
status = COMN_Lookup(genMsg,nameMsg);
if (status != zOK)
{
goto cleanup_freeOpenFileAndReturn;
}
#if NSS_DEBUG IS_ENABLED
pentry = NAME_GetParentEntry(genMsg,nameMsg->curFile/* cnt ,nameMsg->fileNameUniquifier*/);
zASSERT(pentry->p.nameType == zNTYPE_FILE);
zASSERT(nameMsg->fileNameType == zNTYPE_FILE);
#endif
/*
* Do not allow op locks on virtual files.
*/
if (nameMsg->curvol->VOLenabledAttributes & zATTR_VIRTUAL_FILES)
{
createMsg->attributes |= zFA_VOLATILE;
}
/*---------------------------------------------------------------------------
* Check to make sure we still have user disk space before proceeding
* with create.
*---------------------------------------------------------------------------*/
if (nameMsg->curvol->v_statusFlag & VOL_SF_LOGICAL_VOLUME)
{
BOOL rsrcLatched = FALSE;
for (i = 0;;)
{
aStack->useableBlksVOL = nameMsg->curvol->VOLtotalBlocks -
nameMsg->curvol->VOLinUseBlocks +
nameMsg->curvol->VOLpurgeableBlocks +
nameMsg->curvol->VOLnonPurgeableBlocks;
if (aStack->useableBlksVOL <= nameMsg->curvol->VOLfreeBlockAdjustment)
{
/* Volume has used all its space in non-deleted
* files. We cannot create any more files
*/
if (rsrcLatched)
{
UNX_LATCH(&nameMsg->curvol->v_pool->rsrcLatch);
rsrcLatched = FALSE;
}
if (createMsg->createFlags &
(zCREATE_OPEN_IF_THERE | zCREATE_TRUNCATE_IF_THERE))
{
delayedOutOfSpaceError = TRUE;
break;
}
SetErrno(genMsg, zERR_OUT_OF_SPACE);
goto cleanup_freeOpenFileAndReturn;
}
if ((nameMsg->curvol->v_pool->POOLtotalBlocks -
nameMsg->curvol->v_pool->POOLinUseBlocks) >
nameMsg->curvol->v_pool->VOLfreeBlockAdjustment)
{
if (rsrcLatched)
{
UNX_LATCH(&nameMsg->curvol->v_pool->rsrcLatch);
rsrcLatched = FALSE;
}
break;
}
if (((nameMsg->curvol->v_pool->POOLpurgeableBlocks == 0) &&
(nameMsg->curvol->VOLnumDeletedFiles == 0) ) ||
(i++ >= USER_DELAY_COUNTER))
{
if (rsrcLatched)
{
UNX_LATCH(&nameMsg->curvol->v_pool->rsrcLatch);
rsrcLatched = FALSE;
}
if (createMsg->createFlags &
(zCREATE_OPEN_IF_THERE | zCREATE_TRUNCATE_IF_THERE))
{
delayedOutOfSpaceError = TRUE;
break;
}
SetErrno(genMsg, zERR_OUT_OF_SPACE);
goto cleanup_freeOpenFileAndReturn;
}
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
if (!rsrcLatched)
{
X_LATCH(&nameMsg->curvol->v_pool->rsrcLatch);
rsrcLatched = TRUE;
}
COMN_MakeVolumeFreeSpace(genMsg, nameMsg->curvol, FALSE, TRUE, 0);
LB_delay(USER_DELAY_TIME); /* Wait .5 sec */
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
}
/*---------------------------------------------------------------------------
* If the directory exists, but it is currently being purged, fail the
* create.
*---------------------------------------------------------------------------*/
if (nameMsg->curFile->FILEbstState & BST_STATE_PURGING)
{
SetErrno(genMsg,zERR_NAME_NOT_FOUND_IN_DIRECTORY);
goto cleanup_freeOpenFileAndReturn;
}
ASSERT_XLATCH(&nameMsg->curFile->FILEbeastLatch);
/*---------------------------------------------------------------------------
* If the beast being created is a dataStream, then mask to the valid
* data stream attributes. NOTE -- This code assumes that
* zFA_VALID_DATA_STREAM_ATTRIBUTES is a subset of zFA_VALID_FILE_ATTRIBUTES.
*---------------------------------------------------------------------------*/
if ((nameMsg->workNameType == zNTYPE_DATA_STREAM) ||
(nameMsg->workNameType == zNTYPE_EXTENDED_ATTRIBUTE))
{
creatingDataStream = TRUE;
if (((nameMsg->workNameType == zNTYPE_DATA_STREAM) &&
(createMsg->beastClassID != zFTYPE_NAMED_DATA_STREAM)) ||
((nameMsg->workNameType == zNTYPE_EXTENDED_ATTRIBUTE) &&
(createMsg->beastClassID != zFTYPE_EXTENDED_ATTRIBUTE)))
{
SetErrno(genMsg, zERR_INVALID_TYPE_FILE);
goto cleanup_freeOpenFileAndReturn;
}
if (nameMsg->workNameType == zNTYPE_DATA_STREAM)
{
if ((nameMsg->curvol->VOLenabledAttributes &
zATTR_DATA_STREAMS) == 0)
{
SetErrno(genMsg,zERR_DATA_STREAMS_NOT_ENABLED);
goto cleanup_freeOpenFileAndReturn;
}
}
else /* nameMsg->workNameType == zNTYPE_EXTENDED_ATTRIBUTE */
{
if ((nameMsg->curvol->VOLenabledAttributes &
zATTR_EXTENDED_ATTRIBUTES) == 0)
{
SetErrno(genMsg,zERR_EXTENDED_ATTR_NOT_ENABLED);
goto cleanup_freeOpenFileAndReturn;
}
}
if (createMsg->createFlags & zCREATE_DELETE_IF_THERE)
{
createMsg->createFlags &= ~zCREATE_DELETE_IF_THERE;
createMsg->createFlags |= zCREATE_TRUNCATE_IF_THERE;
}
/* Data Streams may not be directories */
if (createMsg->attributes & zFA_SUBDIRECTORY)
{
SetErrno(genMsg, zERR_INVALID_DATA_STREAM);
goto cleanup_freeOpenFileAndReturn;
}
zASSERT(!(createMsg->attributes & ~zFA_VALID_DATA_STREAM_ATTRIBUTES));
createMsg->attributes &= zFA_VALID_DATA_STREAM_ATTRIBUTES;
/* Make sure we have a valid dataStreamInfo structure on the file
* beast, and make sure that if we have any existing data stream
* beasts of this name type, that we have their summary info in memory*/
if (COMN_InitDataStreamInfo(genMsg,nameMsg->curFile,
nameMsg->workNameType) != zOK)
{
goto cleanup_freeOpenFileAndReturn;
}
zASSERT(nameMsg->curFile->FILEdataStreamInfo != NULL);
/* setup local values for simpler code later on */
if (nameMsg->workNameType == zNTYPE_DATA_STREAM)
{
dstrCounts = &nameMsg->curFile->FILEdataStreamInfo->dataStream;
dstrFlag = NFL_HAS_DATA_STREAMS;
}
else
{
dstrCounts = &nameMsg->curFile->FILEdataStreamInfo->extAttr;
dstrFlag = NFL_HAS_EXTENDED_ATTRIBUTES;
}
}
else
{
if (!(COMN_IsIDDerivedFrom(createMsg->beastClassID,zFTYPE_FILE)))
{
zASSERT(FALSE);
SetErrno(genMsg, zERR_INVALID_TYPE_FILE);
goto cleanup_freeOpenFileAndReturn;
}
}
/*---------------------------------------------------------------------------
* If the name has wildcard characters we cannot create.
*---------------------------------------------------------------------------*/
if (nameMsg->scanMsg.retScanFlags & NSRETSFL_hasWildcardChars)
{
SetErrno(genMsg,zERR_INVALID_NAME);
goto cleanup_freeOpenFileAndReturn;
}
/*---------------------------------------------------------------------------
* Verify that the user is not trying to create an object in the
* zNTYPE_DELETED_FILE nameType. This is illegal.
*---------------------------------------------------------------------------*/
if (nameMsg->workNameType == zNTYPE_DELETED_FILE)
{
SetErrno(genMsg, zERR_INVALID_NAME_TYPE);
goto cleanup_freeOpenFileAndReturn;
}
/*---------------------------------------------------------------------------
* If there are no remaining parsed components, then this beast was previously
* resolved, or it was specified by Zid. In any case, this is legal if one of
* OPEN_IF_THERE, CREATE_IF_THERE or TRUNCATE_IF_THERE is specified.
*---------------------------------------------------------------------------*/
if (!(nameMsg->retParseFlags & NAMRETPFL_haveAParsedComponent))
{
/* At this point, nameMsg->curBeast points directly at the beast
* which is to be created (it already exists). There is no path */
if (!(createMsg->createFlags & (zCREATE_OPEN_IF_THERE|
zCREATE_TRUNCATE_IF_THERE|zCREATE_DELETE_IF_THERE)))
{
SetErrno(genMsg,zERR_INVALID_NAME);
goto cleanup_freeOpenFileAndReturn;
}
if ((beastName = malloc(zMAX_COMPONENT_NAME*sizeof(unicode_t))) == NULL)
{
SetErrno(genMsg,zERR_NO_MEMORY);
goto cleanup_freeOpenFileAndReturn;
}
beastNameAllocated = TRUE;
/* Setup nameMsg to point to the container, and existingBeast to
* point to the existing data stream, and parentZid to point to the
* parent of existingBeast */
if (nameMsg->curDataStream == (NamedBeast_s *)nameMsg->curFile)
{
if (COMN_GetNameFromBeast(genMsg, nameMsg->hlFile ? nameMsg->hlFile : (HardLinkBeast_s*)nameMsg->curFile,
/* cnt nameMsg->fileNameUniquifier,*/ nameMsg->workNameSpaceID,
zMAX_COMPONENT_NAME, beastName, &aStack->tempLen) != zOK)
{
goto cleanup_freeOpenFileAndReturn;
}
/* Unlatch the beasts when going for the parent beast to avoid deadlock */
COMN_UNLATCH_NAMEMSG_BEASTS_KEEP_LATCHTYPE(nameMsg);
tempBeast = COMN_LookupByZid(genMsg,nameMsg->curvol,
nameMsg->fileParentZid,XLATCHED,aStack->dontDoVisibilityCheck);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg,XLATCHED);
if (tempBeast == NULL)
goto cleanup_freeOpenFileAndReturn;
aStack->parentZid = tempBeast->NAMEDzid;
/*----------------------------------------------------------
* The nameMsg has two pointers "curFile and curDataStream"
* In this case they are both the same. Each has a useCount,
* but the beast is latched only once. We need to replace these
* two pointers with the new parent pointer, preserving these
* two pointers in the existingBeast pointer. The complexity
* here involves releasing the extra use count on the current
* beast and re-getting the extra use count on the new beast.
*----------------------------------------------------------*/
existingBeast = (NamedBeast_s *)nameMsg->curFile; /* Move latched pointer w/useCount */
// cnt existBeastNameUniquifier = nameMsg->fileNameUniquifier;
COMN_Release(&nameMsg->curDataStream); /* Release extra use count on the beast */
nameMsg->curFile = (File_s *)tempBeast; /* save new latched pointer w/useCount */
nameMsg->curDataStream = tempBeast;
COMN_USE_BEAST(&nameMsg->curDataStream->NAMEDroot); /* Get extra usecount on beast */
nameMsg->fileParentZid = nameMsg->curFile->FILEfirstParentZid;
// cnt nameMsg->fileNameUniquifier =
// cnt nameMsg->curFile->FILEfirstParentNameUniquifier;
nameMsg->fileNameType = nameMsg->curFile->FILEfirstParentNameType;
}
else /* file != dataStream */
{
/* this is a dataStream other than the primary. Set the nameMsg
* to point to the file, and existingBeast to point to the
* existing data stream and parentZid to point to the
* parent of existingBeast. */
if (NAME_GetFirstNameFromBeast(genMsg,nameMsg->curDataStream,
zMAX_COMPONENT_NAME, beastName, NULL, NULL, NULL) != zOK)
{
goto cleanup_freeOpenFileAndReturn;
}
aStack->parentZid = nameMsg->curFile->FILEzid;
existingBeast = nameMsg->curDataStream;
// cnt existBeastNameUniquifier =
// cnt nameMsg->curDataStream->NAMEDfirstParentNameUniquifier;
nameMsg->curDataStream = (NamedBeast_s *)nameMsg->curFile;
COMN_USE_BEAST(&nameMsg->curDataStream->NAMEDroot);
}
/* At this point, nameMsg->curFile/DataStream points to the container,
* which has useCounts and an XLATCH on it.
* existingBeast is a pointer to the existing beast, which has
* a useCount and an XLATCH on it.
* Beastname points to the name of the existingBeast.
* parentZid is the zid of the parent of existingBeast. */
}
/*---------------------------------------------------------------------------
* Else, process the leaf name as the name of the file to create
*---------------------------------------------------------------------------*/
else
{
reLookupLeafName:
zASSERT(nameMsg->retParseFlags & NAMRETPFL_haveAParsedComponent);
zASSERT(nameMsg->retParseFlags & NAMRETPFL_lastComponent);
zASSERT(nameMsg->scanMsg.retScanFlags & NSRETSFL_componentIsUnicode);
zASSERT(nameMsg->scanMsg.retUnicodeComp != NULL);
zASSERT(nameMsg->curDataStream == (NamedBeast_s *)nameMsg->curFile);
/*---------------------------------------------------------------------------
* Validate the name to make sure it is a legal name (not reserved etc...)
* Note- the nameMsg->nameSpace ptr is guaranteed to be setup after COMN_Lookup
*---------------------------------------------------------------------------*/
if (nameMsg->workNameSpace->nSpaceOps->isLegalName(genMsg,
nameMsg->scanMsg.retUnicodeComp) != zOK)
{
goto cleanup_freeOpenFileAndReturn;
}
aStack->parentZid = nameMsg->curFile->FILEzid;
/*
* If file already exists, find it, set useCount and latch it.
* NOTE - As a fix to Defect #230368 and Defect #230747, this lookup
* always does NOT do a visibility check. We need to know if the beast
* already exists, even if we don't have rights to see it.
* If the beast does exist, it can still be "deleted_if_there" or
* "truncated_if_there" even without file scan rights, as long as the
* caller has "Delete" or "Write" rights.
*/
existingBeast = NAME_FindNameInDirSpecialRules(
genMsg, nameMsg, &dosNameSpace, TRUE, TRUE);
if (existingBeast == NULL)
{
/* The file does not already exist with this name */
if (nameMsg->scanMsg.retScanFlags & NSRETSFL_hasExtendedASCII)
{
/* Note - only DOS sets this flag. We did not find the name
* already in the directory. The name has extended ASCII
* characters in it, so before creating the new file, we must
* reparse the extended ASCII characters using DOS uppercasing
* rules or we will have problems.
* Up to this point, the path was already parsed without doing
* uppercasing, because we need to look up existing file
* names exactly as typed by the user, or we wouldn't be able
* to lookup files that contain illegal characters.
*/
if (NMSG_ReparseNameMsgDOSComponent(genMsg,nameMsg) != zOK)
{
goto cleanup_freeOpenFileAndReturn;
}
goto reLookupLeafName;
}
if (nameMsg->scanMsg.retScanFlags & NSRETSFL_hasIllegalChars)
{
/* We had an illegal name, and the name does not already exist,
* so do not even try to create a new file with the illegal name.
* The genMsg errno was already set by the name parsing code
* and/or the NAME_FindNameInDir, so just get out now. */
goto cleanup_freeOpenFileAndReturn;
}
}
if ((existingBeast) && (!aStack->dontDoVisibilityCheck))
{
/* Check if this is a data Stream */
if (creatingDataStream)
{
/* Check the parent beast for rights. If we are dealing with
* a hardlink, the parentZid we need to use here is the
* parent of the hardlink, not the Inode beast
*/
zASSERT( !COMN_IsDerivedFrom(existingBeast,zFTYPE_FILE) );
rightsCheckFile = nameMsg->curFile;
aStack->rightsCheckParentZid = (nameMsg->hlFile ?
nameMsg->hlParentZid : nameMsg->fileParentZid);
}
else
{
/* Check the existing beast itself for rights. If dealing
* with a hardlink, existing beast points to the hardlink
* beast, so we need to get the primary to use for authorization
* functions. */
zASSERT(COMN_IsDerivedFrom(existingBeast,zFTYPE_FILE));
primaryBeast = HL_CheckIfHardlinkAndReturnPrimary(
genMsg, existingBeast, XLATCHED);
rightsCheckFile =
primaryBeast? primaryBeast : (File_s *)existingBeast;
aStack->rightsCheckParentZid = aStack->parentZid;
}
if ( nameMsg->curFile->FILEmayIDoThis(genMsg, rightsCheckFile,
aStack->rightsCheckParentZid, MAY_I_SEE_THE_OBJECT) != zOK)
{
/*
* We know there is an existing file, but if the user is not
* authorized to see it, so return an error.
*/
SetErrno(genMsg, zERR_FILE_ALREADY_EXISTS);
goto cleanup_freeOpenFileAndReturn;
}
if (primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
}
ClearErrno(genMsg); /* Remove error if file did not exist */
beastName = nameMsg->scanMsg.retUnicodeComp;
}
/*===========================================================================
* At this point, nameMsg->curFile/curDataStream points to the current container,
* which has useCounts and an XLATCH on it.
* existingBeast, if non-NULL, is a pointer to the existing beast, which has
* a useCount and an XLATCH on it.
* beastName points to the name of the leaf component, whether it exists or not.
* parentZid contains the parentZid of the beast to be created.
*===========================================================================*/
INIT_EVENT_ID_STATUS(id, aStack->exit.enterRetStatus);
if ((!(createMsg->requestedRights & zRR_PREVENT_FS_HOOKS)) &&
FSHOOKS_OR_EVENTS(F3OpenCreate_Hook, EVENT_Create_Enter))
{
if (existingBeast)
{
UNX_LATCH(&existingBeast->NAMEDbeastLatch);
}
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
RELEASE_RSRC(NAME_RESERVE);
CHECK_FSHOOKS_CREATE_ENTER(F3OpenCreate_Hook, &aStack->parms, status,
cleanup_freeOpenFileExit, genMsg, nameMsg, createMsg,
beastName, aStack->asciiBeastName, retFileHandle);
if (NEBEventInfo[EVENT_Create_Enter].consumers)
{
INIT_CREATE_ENTER_EVENT(genMsg, nameMsg, aStack->evBlk, EVENT_Create_Enter,
aStack->enter, id, beastName, createMsg, retFileHandle);
ZOS_ProduceEvent(status,&aStack->evBlk);
CHECK_ENTER_RETURN(genMsg, status, aStack->evBlk, aStack->exit,
cleanup_freeOpenFileExit);
}
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
if (existingBeast)
{
X_LATCH(&existingBeast->NAMEDbeastLatch);
}
/*-------------------------------------------------------------------
* The FSHOOKS/EVENT code above unlatched both the container and the
* existing beast pointers. It is entirely possible that several bad
* things could have happened during this interval of time. The
* container or existing beast could have been deleted, and/or a new
* beast could have been created where no existing beast was there
* before.
*-------------------------------------------------------------------*/
if (nameMsg->curFile->FILEbstState & BST_STATE_PURGING)
{
/* The parent dir was deleted during the unlatched period. */
SetErrno(genMsg,zERR_NAME_NOT_FOUND_IN_DIRECTORY);
goto cleanup_freeOpenFile;
}
if ((existingBeast) && (existingBeast->NAMEDbstState & BST_STATE_PURGING))
{
/* The existing beast was deleted during the unlatched period. */
if (!(nameMsg->retParseFlags & NAMRETPFL_haveAParsedComponent))
{
/* We were creating the existing beast by ZID ... (no name
* was specified) and it is now deleted. Return a NOT found
* error. Since the original lookup was by ZID, and this ZID
* is gone ... we don't want to keep going.
*/
SetErrno(genMsg,zERR_NAME_NOT_FOUND_IN_DIRECTORY);
goto cleanup_freeOpenFile;
}
else
{
/* We had looked up an existing beast by name, but that beast is
* now being deleted. Just pretend we never found the existing
* beast, so we will try to find it again.
*/
COMN_UnlatchAndRelease(&existingBeast, XLATCHED);
}
}
/*-------------------------------------------------------------------
* If there was no existing beast before the FSHOOKS/EVENT, there may be
* one now. (This could also be the case if the existing beast was
* deleted during the unlatched period.) Lookup the name again to make
* sure it does not exist now. Not doing this here was actually causing
* duplicates before adding this code.
* This isn't a major performance hit, because there will still be a
* negative or positive name cache entry in most cases. This will
* rarely go down into the name tree.
*-------------------------------------------------------------------*/
if (existingBeast == NULL)
{
existingBeast = NAME_FindNameInDirSpecialRules(
genMsg, nameMsg, &dosNameSpace, TRUE, TRUE);
if ((existingBeast) && (!aStack->dontDoVisibilityCheck))
{
/* Check if this is a data Stream */
if (creatingDataStream)
{
/* Check the parent beast for rights */
zASSERT( !COMN_IsDerivedFrom(existingBeast,zFTYPE_FILE) );
rightsCheckFile = nameMsg->curFile;
aStack->rightsCheckParentZid = (nameMsg->hlFile ?
nameMsg->hlParentZid : nameMsg->fileParentZid);
}
else
{
/* Check the existing beast itself for rights */
zASSERT(COMN_IsDerivedFrom(existingBeast,zFTYPE_FILE));
primaryBeast = HL_CheckIfHardlinkAndReturnPrimary(
genMsg, existingBeast, XLATCHED);
rightsCheckFile =
primaryBeast? primaryBeast : (File_s *)existingBeast;
aStack->rightsCheckParentZid = aStack->parentZid;
}
if ( nameMsg->curFile->FILEmayIDoThis(genMsg, rightsCheckFile,
aStack->rightsCheckParentZid, MAY_I_SEE_THE_OBJECT) != zOK)
{
/*
* We know there is an existing file, but if the user is not
* authorized to see it, so return an error.
*/
SetErrno(genMsg, zERR_FILE_ALREADY_EXISTS);
goto cleanup_freeOpenFile;
}
if (primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
}
ClearErrno(genMsg); /* Remove error if file did not exist */
}
}
#if NSS_DEBUG IS_ENABLED
/* the following assert checks for the create (open if there) of an existing file, or if
* a hardlink was traversed, the hardlink must be marked as a file */
if(nameMsg->hlFile)
zASSERT(nameMsg->hlFile->HARDLfirstParentNameType == zNTYPE_FILE);
else
zASSERT(nameMsg->curFile->FILEfirstParentNameType == zNTYPE_FILE);
#endif
/*---------------------------------------------------------------------------
* Ask the container's beast class if this class allows CREATE
*---------------------------------------------------------------------------*/
if (nameMsg->curFile->FILEcomnOps.BST_beastNotify(genMsg,&nameMsg->curFile->FILEroot,
BST_NOTIFY_CREATE) != zOK)
{
goto cleanup_freeOpenFile;
}
/*---------------------------------------------------------------------------
* Check the container to see if we have rights to create a file. We put
* an exclusive latch on the container at this point which will be asserted
* for the entire operation.
* NOTE - We know that the container must be a File_s beast because
* a NamedBeast_s may never contains sub-beasts.
*---------------------------------------------------------------------------*/
if ((existingBeast == NULL) &&
(!(COMN_IsDerivedFrom(nameMsg->curFile,zFTYPE_FILE))))
{
SetErrno(genMsg,zERR_CONTAINER_NOT_FILE_BEAST);
goto cleanup_freeOpenFile;
}
zASSERT(COMN_IsDerivedFrom(nameMsg->curFile,zFTYPE_FILE));
/*---------------------------------------------------------------------------
* Check if the given beast already exists.
*---------------------------------------------------------------------------*/
if (existingBeast)
{
/* Defect #306666. We can't allow unlicensed connection access */
if (!ConnectionIsLoggedIn(genMsg->pssConn.id))
{
if (createMsg->createFlags & zCREATE_OPEN_IF_THERE)
{
/* We're opening an existing file, and not logged in ...
* Mimic the open check where we allow the open if we are
* not doing rights checking.
*/
if (aStack->doRightsCheck)
{
/* They are not logged in (LICENCED), so limit their access */
if (createMsg->requestedRights &
(zRR_READ_ACCESS | zRR_SCAN_ACCESS))
{
createMsg->requestedRights &= ~zRR_WRITE_ACCESS;
}
else
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
}
}
else
{
/* Not doing "open if there". Fail this unlicensed access */
SetErrno(genMsg, zERR_NO_CREATE_PRIVILEGE);
goto cleanup_freeOpenFile;
}
}
/* Check if this is a data Stream */
if (creatingDataStream)
{
/* Check the parent beast for rights */
zASSERT( !COMN_IsDerivedFrom(existingBeast,zFTYPE_FILE) );
rightsCheckFile = nameMsg->curFile;
aStack->rightsCheckParentZid = (nameMsg->hlFile ?
nameMsg->hlParentZid : nameMsg->fileParentZid);
}
else
{
/* Check the existing beast itself for rights */
zASSERT(COMN_IsDerivedFrom(existingBeast,zFTYPE_FILE));
primaryBeast = HL_CheckIfHardlinkAndReturnPrimary(
genMsg, existingBeast, XLATCHED);
rightsCheckFile =
primaryBeast? primaryBeast : (File_s *)existingBeast;
aStack->rightsCheckParentZid = aStack->parentZid;
}
/* Cannot create a directory if an existing beast already exists, and
* cannot create a file if the existing beast is a directory */
if ((createMsg->attributes & zFA_SUBDIRECTORY)
|| (existingBeast->NAMEDattributes & zFA_SUBDIRECTORY))
{
if (!((genMsg->saID != zSAGENT_NETWARE)
&& (createMsg->createFlags & zCREATE_OPEN_IF_THERE)))
{
SetErrno(genMsg, zERR_FILE_ALREADY_EXISTS);
goto cleanup_freeOpenFile;
}
}
/* The file exists ... see if we want to OPEN or TRUNCATE it.*/
if (createMsg->createFlags & (zCREATE_OPEN_IF_THERE|zCREATE_TRUNCATE_IF_THERE))
{
if (retFileHandle != NULL)
{
if (nameMsg->curvol->VOLenabledAttributes & zATTR_READONLY)
{
// zASSERT(!(createMsg->requestedRights &
// ~zVALID_READ_ONLY_FILE_REQ_RIGHTS));
if ((createMsg->requestedRights &
~zVALID_READ_ONLY_FILE_REQ_RIGHTS) ||
(createMsg->createFlags & zCREATE_TRUNCATE_IF_THERE))
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
}
/* Mask read in drop boxes */
if ((createMsg->requestedRights & zRR_MASK_READ_IN_DROP_BOXES)
&& (rightsCheckFile->FILEmayIDoThis(genMsg, rightsCheckFile,
aStack->rightsCheckParentZid, MAY_I_CREATE_ONLY) == zOK))
{
createMsg->requestedRights &= ~zRR_READ_ACCESS;
}
/* see if we can READ from it*/
if (createMsg->requestedRights & zRR_READ_ACCESS)
{
if ((aStack->doRightsCheck) &&
(rightsCheckFile->FILEmayIDoThis(genMsg, rightsCheckFile,
aStack->rightsCheckParentZid, MAY_I_READ_DATA) != zOK))
{
if (rightsCheckFile->FILEmayIDoThis(genMsg,
rightsCheckFile, aStack->rightsCheckParentZid,
MAY_I_CREATE) == zOK)
{ /* The file can be open with create rights if:
* 1. It was created by the current user
* 2. It is empty
*/
if (LB_GUIDCompare(&rightsCheckFile->FILEownerID,
&genMsg->pssConn.ptr->authInfo.authenticatedIDs[0]) != 0)
{
goto cantRead;
}
if (existingBeast->NAMEDeof != 0)
{
goto cantRead;
}
}
else
{
cantRead:
SetErrno(genMsg, zERR_ACCESS_DENIED);
goto cleanup_freeOpenFile;
}
}
}
/* see if we can WRITE to it*/
if (createMsg->requestedRights & zRR_WRITE_ACCESS)
{
if ((aStack->doRightsCheck) &&
(rightsCheckFile->FILEmayIDoThis(genMsg, rightsCheckFile,
aStack->rightsCheckParentZid, MAY_I_WRITE_DATA) != zOK))
{
if (rightsCheckFile->FILEmayIDoThis(genMsg,
rightsCheckFile, aStack->rightsCheckParentZid,
MAY_I_CREATE) == zOK)
{ /* The file can be open with create rights if:
* 1. It was created by the current user
* 2. It is empty
*/
if (LB_GUIDCompare(&rightsCheckFile->FILEownerID,
&genMsg->pssConn.ptr->authInfo.authenticatedIDs[0]) != 0)
{
goto cantWrite;
}
if (existingBeast->NAMEDeof != 0)
{
goto cantWrite;
}
}
else
{
cantWrite:
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
}
if (rightsCheckFile->FILEattributes & zFA_READ_ONLY)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
if (nameMsg->curvol->VOLenabledAttributes & zATTR_COW)
{
if ((existingBeast->root.fileSnapshotBeast == NULL) &&
(existingBeast->NAMEDsnapReaderCount != 0))
{
fixUpSnapReaders =
existingBeast->NAMEDsnapReaderCount;
}
if (SetupSnapshotBeast(genMsg,
&existingBeast->NAMEDroot, nameMsg->curvol) != zOK)
{
goto cleanup_freeOpenFile;
}
for (i = 0; i < fixUpSnapReaders; i++)
{
/* Increment the use count on the snapbeast so
* that when the snapshot reader close the file
* they can decrement it
*/
SetupSnapshotBeast(genMsg,
&existingBeast->NAMEDroot, nameMsg->curvol);
}
fhState |= FH_WRITE_SNAPSHOT;
cleanupSnapBeast = existingBeast;
}
}
}
createMsg->ret_openCreateAction = zBEAST_EXISTED;
ASSERT_XLATCH(&existingBeast->NAMEDbeastLatch);
if (createMsg->createFlags & zCREATE_TRUNCATE_IF_THERE)
{
BOOL retried = FALSE;
/* Don't allow truncation if the file is open with "DENY_WRITE"
* by some other file handle */
checkAgain:
if (existingBeast->NAMEDdenyWriterCount != 0)
{
if ((existingBeast->NAMEDvolume->VOLenabledAttributes &
zATTR_CFS_MASTER) && (!retried))
{
retried = TRUE;
CSA_CloseNotInUseFileHandles(existingBeast,
zRR_WRITE_ACCESS, NULL);
goto checkAgain;
}
SetErrno(genMsg,zERR_FILE_WRITE_LOCKED);
goto cleanup_freeOpenFile;
}
/* postpone the tructation until getting file handle */
fhState |= FH_MODIFIED;
if (existingBeast->NAMEDroot.csaBeast != NULL)
{
existingBeast->NAMEDroot.csaBeast->CB_dataSeqNum++;
}
}
newBeast = existingBeast;
// cnt nameUniquifier = existBeastNameUniquifier;
existingBeast = NULL;
if (primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
goto returnTheFile; /* return the beast */
}
/*---------------------------------------------------------------------------
* If the zCREATE_DELETE_IF_THERE bit is not set then we must return an error
* because the file already exists, but we were not instructed to open,
* truncate, or delete it...
*---------------------------------------------------------------------------*/
if (!(createMsg->createFlags & zCREATE_DELETE_IF_THERE))
{
SetErrno(genMsg,zERR_FILE_ALREADY_EXISTS);
goto cleanup_freeOpenFile;
}
else /* file exists and needs to be deleted and recreated */
{
if ((aStack->doRightsCheck) &&
(((File_s *)rightsCheckFile)->FILEmayIDoThis(genMsg,
rightsCheckFile, aStack->rightsCheckParentZid,
MAY_I_DELETE) != zOK))
{
SetErrno(genMsg, zERR_NO_CREATE_DELETE_PRIVILEGE);
goto cleanup_freeOpenFile;
}
if ((existingBeast->NAMEDdontDeleteWhileOpenCount != 0) &&
(FH_OpenFileMayNotBeDeleted(genMsg,existingBeast)))
{
SetErrno(genMsg, zERR_CANT_DELETE_OPEN_FILE);
goto cleanup_freeOpenFile;
}
if (((File_s *)rightsCheckFile)->FILEattributes & zFA_DELETE_INHIBIT)
{
SetErrno(genMsg,zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
if ((nameMsg->retParseFlags & NAMRETPFL_foundDOSNameWithLong) &&
(!beastNameAllocated))
{
/* The caller is requesting a "delete if there", but the name
* provided to us was a DOS name given to us in the long
* namespace. To make this work as expected, we need to find
* the long name of the file being deleted, and the new file
* needs to be created with that long name.
*/
if ((beastName = malloc(zMAX_COMPONENT_NAME*sizeof(unicode_t))) == NULL)
{
SetErrno(genMsg,zERR_NO_MEMORY);
goto cleanup_freeOpenFile;
}
beastNameAllocated = TRUE;
if (COMN_GetNameFromBeast(genMsg, existingBeast,
nameMsg->workNameSpaceID,
zMAX_COMPONENT_NAME, beastName, &aStack->tempLen) != zOK)
{
goto cleanup_freeOpenFile;
}
}
/* Deal with the actual delete after the new beast is initialized.
* We flag this condition by the fact that existingBeast is non-NULL.
* Fall out of this code into the BST_new code...
*/
}
if (primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
}
/*===========================================================================
* At this point, nameMsg->curFile points to the current directory, which
* has a useCount and an XLATCH on it.
* existingBeast, if non-NULL, is a pointer to the existing beast, which has
* a useCount and an XLATCH on it. If existingBeast is non-NULL it will be
* deleted later on because zCREATE_DELETE_IF_THERE was specified.
*===========================================================================*/
/*-------------------------------------------------------------------------
* Create the new file object
*-------------------------------------------------------------------------*/
/* If we delayed an "out of space" error, return it now before attempting
* to create a new beast. We do this to allow "open-if-there" and
* "truncate-if-there" on a beast that already exists, even if the volume
* is full.
*/
if (delayedOutOfSpaceError)
{
SetErrno(genMsg, zERR_OUT_OF_SPACE);
goto cleanup_freeOpenFile;
}
/* Defect #306666. We can't allow unlicensed connection access */
if (!ConnectionIsLoggedIn(genMsg->pssConn.id))
{
/* Not doing "open if there". Fail this unlicensed access */
SetErrno(genMsg, zERR_NO_CREATE_PRIVILEGE);
goto cleanup_freeOpenFile;
}
/*
* Check if the volume is READ-ONLY
*/
if (nameMsg->curvol->VOLenabledAttributes & zATTR_READONLY)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
/*
* Check for Create Trustee rights
*/
if ((aStack->doRightsCheck) &&
(((File_s *)nameMsg->curFile)->FILEmayIDoThis(genMsg,
nameMsg->curFile, nameMsg->fileParentZid, MAY_I_CREATE) != zOK))
{
SetErrno(genMsg, zERR_NO_CREATE_PRIVILEGE);
goto cleanup_freeOpenFile;
}
/* If file is going to be created on admin volume, we want to convert
it to zFTYPE_ADMIN_VOL_FILE, so that avfile and avol comnops and volops
can be used.
*/
curVol = nameMsg->curvol;
curDir = (File_s *)nameMsg->curFile;
if (curVol == (Volume_s *)&AdminVolume)
{
/*
* Make sure _admin volume files are marked as volatile so op locks
* aren't used with these file.
*/
createMsg->attributes |= zFA_VOLATILE;
if ((PersistAdminVolume != NULL) &&
(curDir->FILEzid >= AVOL_FIRST_PERSISTENT_ZID))
{
curVol = PersistAdminVolume;
}
else if (curDir->FILEattributes & zFA_IS_ADMIN_LINK)
{
curVol = PersistAdminVolume;
curDir = COMN_AVOL_ResolveLink(genMsg, &curDir->FILEnamed);
if (curDir == NULL)
{
goto cleanup_freeOpenFile;
}
COMN_UNLATCH_AND_RELEASE_NAMEMSG_BEASTS_KEEP_LATCHTYPE(nameMsg);
X_LATCH(&curDir->FILEbeastLatch);
nameMsg->curFile = curDir;
nameMsg->curDataStream = &curDir->FILEnamed;
COMN_USE_BEAST(&nameMsg->curDataStream->NAMEDroot); \
}
else
{
createMsg->beastClassID = zFTYPE_ADMIN_VOL_FILE;
}
}
newBeast = BST_new(genMsg, createMsg->beastClassID, curVol);
if (newBeast == NULL)
{
goto cleanup_freeOpenFile;
}
/* The root beast constructor put the beast on the volume. A hardware error
* caused the volume to be disabling, tossing the beasts on the volume,
* resulting in a window where this beast is being tossed and in error
* conditions we will toss it again, hence the extra use count
*/
/** 10/28/03 - Vandana -
** Don't need this anymore, because BST_new (ROOT_BST_Construct) no
** longer puts the beast on the volume linked list. That is now
** done as part of BEASTHASH_Insert
**/
// newBeast->NAMEDuseCount++;
X_LATCH(&newBeast->NAMEDbeastLatch);
/* See if we will be deleting the existing beast and if so, do some setup for
the beast deletion before starting the transaction
*/
if(existingBeast)
{
if(BST_deleteSetup(genMsg, (File_s*)existingBeast, &primaryBeast, &neighborBeast) != zOK)
{
goto cleanup_freeOpenFile;
}
}
/*---------------------------------------------------------------------------
* Start transactioning the create operation
*---------------------------------------------------------------------------*/
xaction = COMN_BeginXLocal(curDir);
DEBUG_PRINTF(TCREATE, DBG_NOINDENT, (GREEN, MSGNot("CurT=%d, Ticks=%d, Thr=0x%x, %s\n"), GetCurrentTime(), Ticks, ThreadId(), WHERE));
COMN_MARK_BEAST_XLOCAL(&newBeast->NAMEDroot,xaction);
/*---------------------------------------------------------------------------
* Init the beast.
*---------------------------------------------------------------------------*/
newBeast->NAMEDzid = curDir->FILEcomnOps.BST_getZID(genMsg, &curDir->FILEnamed, xaction);
if (newBeast->NAMEDzid == zINVALID_ZID)
{
UNX_LATCH(&newBeast->NAMEDbeastLatch);
// newBeast->NAMEDuseCount--;
BST_free(newBeast);
goto cleanup_endXlocal;
}
BEASTHASH_Insert(&newBeast->NAMEDroot);
/* The above routine put at useCount, so we no longer need the extra use
* count from above.
*/
// newBeast->NAMEDuseCount--;
newBeast->NAMEDbstState |= BST_STATE_NEW;
DEBUG_PRINTF(TCREATE, DBG_NOINDENT, (GREEN, MSGNot("CurT=%d, Ticks=%d, Thr=0x%x, %s\n"), GetCurrentTime(), Ticks, ThreadId(), WHERE));
/*-------------------------------------------------------------------------
* Fill in the attributes for the new beast and the container
*-------------------------------------------------------------------------*/
aStack->currentTime = GetUTCTime();
if (creatingDataStream)
{
/* Fix SPD 194361 - Fixed to NOT set the modify time when creating
* data streams and extended attributes. */
nameMsg->curFile->FILEaccessedTime =
nameMsg->curFile->FILEmetaDataModifiedTime = aStack->currentTime;
nameMsg->curFile->FILEarchivedTime = INVALID_UTC_TIME;
nameMsg->curFile->FILEmetaDataModifierID = createMsg->ownerID;
nameMsg->curFile->FILEmetaDataSeqNum++;
COMN_MARK_BEAST_MESSY(&nameMsg->curFile->FILEroot);
}
else if (COMN_IsDerivedFrom(newBeast,zFTYPE_FILE))
{
curDir->FILEmodifiedTime = aStack->currentTime;
curDir->FILEmodifiedDOSTime = INVALID_DOS_TIME;
curDir->FILEmodifierID = createMsg->ownerID;
curDir->FILEmetaDataSeqNum++;
curDir->FILEvolume->VOLfile.FILEmodifiedTime = aStack->currentTime;
COMN_MARK_BEAST_MESSY(&curDir->FILEroot);
((File_s *)newBeast)->FILEaccessedTime =
((File_s *)newBeast)->FILEcreatedTime =
((File_s *)newBeast)->FILEmodifiedTime =
((File_s *)newBeast)->FILEmetaDataModifiedTime = aStack->currentTime;
((File_s *)newBeast)->FILEarchivedTime = INVALID_UTC_TIME;
if (createMsg->attributes & (zFA_ARCHIVE|zFA_ATTR_ARCHIVE))
{
/* If setting archive bits, add to MFL */
SBS_markFileModified((File_s *)newBeast,
(createMsg->attributes & (zFA_ARCHIVE|zFA_ATTR_ARCHIVE)),
xaction);
}
/* DOS Times are set in the constructor */
OID_SaveObjectID(curVol, &createMsg->ownerID);
((File_s *)newBeast)->FILEownerID =
((File_s *)newBeast)->FILEmodifierID =
((File_s *)newBeast)->FILEmetaDataModifierID = createMsg->ownerID;
newBeast->NAMEDmetaDataSeqNum++;
// The beast is already marked xlocal. We must not mark it dirty
// or set a timer on it. The forcebeastwrite will make it journalled
// before endxlocal. In error cases this mark dirty resulted in
// orphan beasts. Defect: 180354
// Also not marking it dirty guarantees it is the same xaction
// that gets the zid and writes it to disk.
// COMN_MARK_BEAST_DIRTY(&newBeast->NAMEDroot);
}
/* Don't allow to set IC bit if volume doesn't have compression turned on. Mike
* Olsen said it is what traditional file system behaves
*/
if (!CM_COMPRESSION_ENABLED(newBeast->NAMEDvolume))
{
createMsg->attributes &= ~zFA_COMPRESS_FILE_IMMEDIATELY;
}
newBeast->NAMEDattributes = createMsg->attributes;
/*---------------------------------------------------------------------------
* If the file already exists we will only get this far if the
* zCREATE_DELETE_IF_THERE bit is set. If this is the case, we must now
* delete the old file before proceeding.
*---------------------------------------------------------------------------*/
createMsg->ret_openCreateAction = zBEAST_CREATED;
if (existingBeast != NULL)
{
if (creatingDataStream)
{
dstrCounts->count--;
dstrCounts->dataSize -= existingBeast->NAMEDeof;
dstrCounts->nameSize -= unilen(beastName);
if (dstrCounts->count == 0)
{
zASSERT((dstrCounts->dataSize == 0) && (dstrCounts->nameSize == 0));
curDir->FILEnameFlags &= ~(dstrFlag);
COMN_MARK_BEAST_XLOCAL(&curDir->FILEroot,xaction);
}
}
if ( COMN_LsaCleanupSetup(existingBeast, /* cnt existBeastNameUniquifier,*/
&aStack->lsaInv) == zOK)
{
if (primaryBeast != NULL)
{
aStack->lsaInv.lid_zid = primaryBeast->FILEzid;
}
else
{
aStack->lsaInv.lid_zid = existingBeast->NAMEDzid;
}
aStack->lsaInv.lid_pZid = curDir->FILEzid;
lsaDelDentry = TRUE;
}
status = BST_delete(genMsg, curDir, existingBeast, primaryBeast,
neighborBeast, xaction, FALSE, TRUE);
if(status != zOK)
{
goto cleanup_newBeast;
}
createMsg->ret_openCreateAction |= zBEAST_EXISTED;
}
/*---------------------------------------------------------------------------
* Insert all of the names into this new beast and its parent directory
*---------------------------------------------------------------------------*/
DEBUG_PRINTF(TCREATE, DBG_NOINDENT, (GREEN, MSGNot("CurT=%d, Ticks=%d, Thr=0x%x, %s\n"), GetCurrentTime(), Ticks, ThreadId(), WHERE));
if (NAME_InsertAndMangleName(genMsg,newBeast, NULL, curDir,
nameMsg->workNameSpaceID, nameMsg->workNameType,
beastName,INSNAMEFL_NEW_PARENT_AUTH,xaction,// cnt &nameUniquifier,
&beastNamesAdded,&dirNamesAdded) != zOK)
{
goto cleanup_newBeast;
}
zASSERT(beastNamesAdded == dirNamesAdded);
DEBUG_PRINTF(TCREATE, DBG_NOINDENT, (GREEN, MSGNot("CurT=%d, Ticks=%d, Thr=0x%x, %s\n"), GetCurrentTime(), Ticks, ThreadId(), WHERE));
///*---------------------------------------------------------------------------
// * If this is a directory, initialize it
// *---------------------------------------------------------------------------*/
// if (createMsg->attributes & zFA_SUBDIRECTORY)
// {
// if (newBeast->NAMEDcomnOps.initDirectory(genMsg,newBeast) != zOK)
// {
// goto cleanup_removeNames;
// }
// }
/*---------------------------------------------------------------------------
* Set up for rights checking
*---------------------------------------------------------------------------*/
/* Check if this is a data Stream */
if (creatingDataStream)
{
zASSERT((createMsg->beastClassID == zFTYPE_NAMED_DATA_STREAM) ||
(createMsg->beastClassID == zFTYPE_EXTENDED_ATTRIBUTE));
#ifdef USER_GPACHNER
#if NSS_DEBUG IS_ENABLED
DBG_DebugPrintf( CYAN, "%s Zid %Ld is a %s\n", WHERE,
newBeast->NAMEDzid,
(createMsg->beastClassID == zFTYPE_NAMED_DATA_STREAM) ?
"Data Stream" : "Extended Attribute" );
#endif
#endif
/* Check the parent beast for rights */
rightsCheckFile = (File_s *)nameMsg->curFile;
aStack->rightsCheckParentZid = nameMsg->fileParentZid;
dstrCounts->count++;
dstrCounts->nameSize += unilen(beastName);
curDir->FILEnameFlags |= dstrFlag;
COMN_MARK_BEAST_XLOCAL(&curDir->FILEroot,xaction);
}
else
{
zASSERT(COMN_IsIDDerivedFrom(createMsg->beastClassID,zFTYPE_FILE));
/* Check the existing beast itself for rights */
rightsCheckFile = (File_s *)newBeast;
aStack->rightsCheckParentZid = aStack->parentZid;
}
/*---------------------------------------------------------------------------
* Check access rights to see if we can allow reading of the file
*---------------------------------------------------------------------------*/
if ((retFileHandle != NULL) &&
(!(createMsg->attributes & zFA_SUBDIRECTORY)))
{
if (createMsg->requestedRights & zRR_READ_ACCESS)
{
if ((aStack->doRightsCheck) &&
(rightsCheckFile->FILEmayIDoThis(genMsg, rightsCheckFile,
aStack->rightsCheckParentZid, MAY_I_READ_DATA) != zOK))
{
if (createMsg->requestedRights & zRR_CREATE_WITHOUT_READ_ACCESS)
{
createMsg->requestedRights &= ~zRR_READ_ACCESS;
}
else
{
SetErrno(genMsg, zERR_NO_OPEN_PRIVILEGE);
goto cleanup_removeNames;
}
}
DEBUG_PRINTF(TCREATE, DBG_NOINDENT, (GREEN, MSGNot("CurT=%d, Ticks=%d, Thr=0x%x, %s\n"), GetCurrentTime(), Ticks, ThreadId(), WHERE));
}
/*
* Back in the beginning of time of NSS, Neal and I may have
* decided to force write access rights when creating a file.
* Since then, I have learned that database programs use
* access rights to do locking so must only give what they
* ask for. This is consistent with the traditional file
* system.
*/
if (COMN_OldCreateRights)
{
createMsg->requestedRights |= zRR_WRITE_ACCESS;
}
}
/*-------------------------------------------------------------------------
* We are ending the transaction. Write the file and the directory beast
* (if we need too) and then end the transaction
*-------------------------------------------------------------------------*/
if (COMN_ForceBeastWrite(genMsg,curDir,xaction) != zOK)
{
goto cleanup_removeNames;
}
DEBUG_PRINTF(TCREATE, DBG_NOINDENT, (GREEN, MSGNot("CurT=%d, Ticks=%d, Thr=0x%x, %s\n"), GetCurrentTime(), Ticks, ThreadId(), WHERE));
/* update nameMsg */
if (creatingDataStream)
{
COMN_Release(&nameMsg->curDataStream);
nameMsg->curDataStream = newBeast;
}
else
{
/* Set parentIsCompress flag for the file */
nameMsg->retParseFlags &= ~NAMRETPFL_parentIsImmCompress;
if (nameMsg->curFile->FILEattributes & zFA_COMPRESS_FILE_IMMEDIATELY)
nameMsg->retParseFlags |= NAMRETPFL_parentIsImmCompress;
/* we don't unlatch curDir here because it is needed end transaction */
// COMN_UNLATCH_AND_RELEASE_NAMEMSG_BEASTS_KEEP_LATCHTYPE(nameMsg);
zASSERT((NamedBeast_s *)nameMsg->curFile == nameMsg->curDataStream);
zASSERT(nameMsg->curFile == curDir);
COMN_Release(&nameMsg->curDataStream);
nameMsg->curFile = (File_s *)newBeast;
nameMsg->curDataStream = newBeast;
COMN_USE_BEAST(&nameMsg->curDataStream->NAMEDroot);
nameMsg->fileParentZid = aStack->parentZid;
// cnt nameMsg->fileNameUniquifier = nameUniquifier;
nameMsg->fileNameType = nameMsg->workNameType;
}
nameMsg->retParseFlags &= ~NAMRETPFL_haveAParsedComponent;
nameMsg->handlePathType = zHPT_VOLUME_ZID;
nameMsg->parseFlags |= NAMPFL_nameMsgFullyResolved;
/* call user specific function */
if (createCallBack != NULL)
{
if (createCallBack(genMsg, nameMsg, userParam) != zOK)
{
goto cleanup_resetNameMsg;
}
}
if (COMN_ForceBeastWrite(genMsg,newBeast,xaction) != zOK)
{
goto cleanup_resetNameMsg;
}
DEBUG_PRINTF(TCREATE, DBG_NOINDENT, (GREEN, MSGNot("CurT=%d, Ticks=%d, Thr=0x%x, %s\n"), GetCurrentTime(), Ticks, ThreadId(), WHERE));
COMN_EndXLocal(curDir,&xaction);
if(neighborBeast)
{
COMN_UnlatchAndRelease(&neighborBeast, XLATCHED);
}
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
if (!creatingDataStream)
{
aStack->lsaPzid = curDir->FILEzid;
COMN_UnlatchAndRelease(&curDir, nameMsg->latchType);
if (newBeast && !(createMsg->createFlags & zCREATE_KEEP_VFS_CACHE))
{
lsaInvDentry = TRUE;
}
}
if (existingBeast)
{
COMN_UnlatchAndRelease(&existingBeast,XLATCHED);
}
if (lsaDelDentry && Ptr_lsa_invalidate_dentry)
{
Ptr_lsa_invalidate_dentry(&aStack->lsaInv);
}
if (lsaInvDentry)
{
if ( COMN_LsaCleanupSetup(newBeast, /* cnt nameUniquifier, */
&aStack->lsaInv) == zOK)
{
aStack->lsaInv.lid_zid = zINVALID_ZID;
aStack->lsaInv.lid_pZid = aStack->lsaPzid;
if (Ptr_lsa_invalidate_dentry)
{
Ptr_lsa_invalidate_dentry(&aStack->lsaInv);
}
}
}
goto fillFileHandle;
/*---------------------------------------------------------------------------
* Update NamingMsg to the correct state.
*---------------------------------------------------------------------------*/
returnTheFile:
/* curDir is now invalid */
if (creatingDataStream)
{
COMN_Release(&nameMsg->curDataStream);
nameMsg->curDataStream = newBeast;
}
else
{
/* Set parentIsCompress flag for the file */
nameMsg->retParseFlags &= ~NAMRETPFL_parentIsImmCompress;
if (nameMsg->curFile->FILEattributes & zFA_COMPRESS_FILE_IMMEDIATELY)
nameMsg->retParseFlags |= NAMRETPFL_parentIsImmCompress;
if ((hl = nameMsg->hlFile))
{
COMN_USE_BEAST(&nameMsg->hlFile->HARDLroot);
}
COMN_UNLATCH_AND_RELEASE_NAMEMSG_BEASTS_KEEP_LATCHTYPE(nameMsg);
if(hl)
{
nameMsg->hlFile = hl;
COND_LATCH(&nameMsg->hlFile->HARDLbeastLatch, nameMsg->latchType)
}
nameMsg->curFile = (File_s *)newBeast;
nameMsg->curDataStream = newBeast;
COMN_USE_BEAST(&nameMsg->curDataStream->NAMEDroot);
nameMsg->fileParentZid = aStack->parentZid;
// cnt nameMsg->fileNameUniquifier = nameUniquifier;
nameMsg->fileNameType = nameMsg->workNameType;
}
nameMsg->retParseFlags &= ~NAMRETPFL_haveAParsedComponent;
nameMsg->handlePathType = zHPT_VOLUME_ZID;
nameMsg->parseFlags |= NAMPFL_nameMsgFullyResolved;
/*---------------------------------------------------------------------------
* If the file is being opened, do it now...
*---------------------------------------------------------------------------*/
fillFileHandle:
if (retFileHandle != NULL)
{
if (nameMsg->curDataStream->NAMEDcomnOps.BST_beastNotify(genMsg,
&nameMsg->curDataStream->NAMEDroot,BST_NOTIFY_OPEN) != zOK)
{
goto cleanup_freeOpenFile;
}
/* Compression-related checks */
if (CM_COMPRESSION_ENABLED(nameMsg->curvol))
{
/* If this is the first open on this beast, then we check if
* all compression related flags are compatible
*/
if (!(nameMsg->curDataStream->NAMEDattributes & zFA_SUBDIRECTORY)
&& nameMsg->curDataStream->NAMEDopenCount == 0)
{
if (fixCompFlags(genMsg, nameMsg->curDataStream) != zOK)
{
goto cleanup_freeOpenFile;
}
}
}
if (createMsg->requestedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA)
{
if (! CM_COMPRESSION_ENABLED(nameMsg->curvol))
{
COMN_EnableIOWarning(nameMsg->curvol);
SetErrno(genMsg, zERR_NOT_SUPPORTED);
goto cleanup_freeOpenFile;
}
if (createMsg->requestedRights & zRR_WRITE_ACCESS)
{
/*
* To write compressed one must be writing to a NEW file and one
* must be requesting exclusive access.
*/
if (!(createMsg->requestedRights & zRR_DENY_READ) ||
!(createMsg->requestedRights & zRR_DENY_WRITE))
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
}
else
{
if (!(createMsg->requestedRights & zRR_DENY_WRITE))
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
}
}
/* if we need to open the file */
if (!(nameMsg->parseFlags & NAMPFL_dontDoRightsChecks))
{
/* break any possible open oplocks. It could happen if createMode
* is zCREATE_XXX_IF_THERE
*/
if (OPLOCK_BreakExclusive(genMsg, nameMsg->curFile, TRUE) != zOK)
{
goto cleanup_freeOpenFile;
}
if (!(createMsg->requestedRights & zRR_PSA_CACHE))
{
OPLOCK_BreakPSA(nameMsg->curFile);
}
}
if (FH_OpenFile(genMsg, retFileHandle, nameMsg, fhState) != zOK)
{
goto cleanup_freeOpenFile;
}
if (FH_SetOpenFileGrantedRights(genMsg, NULL, retFileHandle,
createMsg->requestedRights) != zOK)
{
goto cleanup_freeOpenFile;
}
fh = retFileHandle->ptr;
if (nameMsg->curvol->VOLenabledAttributes & zATTR_CFS_SLAVE)
{
if (CRO_AccessLeaseGet(genMsg, fh) != zOK)
{
goto cleanup_freeOpenFile;
}
}
if (fh->grantedRights & zRR_SCAN_ACCESS)
{
#if NSS_DEBUG IS_ENABLED
extern NINT DBG_NumberOfSearchMapExplicitFrees;
++DBG_NumberOfSearchMapExplicitFrees;
#endif
fh->smap = SMAP_NewSearchMap(genMsg, nameMsg);
fh->smap->options |= SMAPOPT_notReusable;
}
/* Perform compression-related processing */
if (!(nameMsg->curDataStream->NAMEDattributes & zFA_SUBDIRECTORY))
{
fh->parentIsImmCompress = 0;
if (nameMsg->retParseFlags & NAMRETPFL_parentIsImmCompress)
{
fh->parentIsImmCompress = 1;
}
fh->compBeast = 0;
if (!(nameMsg->curDataStream->NAMEDattributes & zFA_DATA_STREAM_IS_COMPRESSED))
{
fh->grantedRights &= ~zRR_LEAVE_FILE_COMPRESSED;
}
if (fh->grantedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA)
{
/* Prepare to access compressed version of the datastream. */
RootBeast_s *compBeast = 0;
if (CM_prepareToAccessCompFile(genMsg,
&nameMsg->curDataStream->NAMEDroot, &compBeast) != zOK)
{
goto cleanup_freeOpenFile;
}
if (compBeast)
{
fh->compBeast = compBeast;
UN_LATCH(&compBeast->ROOTbeastLatch);
}
}
else if (CM_COMPRESSION_ENABLED(nameMsg->curvol))
{
if (createMsg->ret_openCreateAction == zBEAST_EXISTED)
{
BOOL keep_comp_version =
(((fh->grantedRights & zRR_LEAVE_FILE_COMPRESSED) ||
fh->parentIsImmCompress)
? TRUE : FALSE);
NINT access_mode =
(fh->grantedRights & zRR_WRITE_ACCESS)
? CACHE_UPDATE : CACHE_READ;
if (CM_prepareToAccessUncompFile(genMsg, nameMsg->curFile,
&nameMsg->curDataStream->NAMEDroot, access_mode,
&keep_comp_version) != zOK)
{
goto cleanup_freeOpenFile;
}
if (keep_comp_version)
{
fh->grantedRights |= zRR_LEAVE_FILE_COMPRESSED;
}
else
{
fh->grantedRights &= ~zRR_LEAVE_FILE_COMPRESSED;
}
}
else
{
if ((fh->parentIsImmCompress) ||
(createMsg->attributes & zFA_COMPRESS_FILE_IMMEDIATELY))
{
fh->grantedRights |= zRR_LEAVE_FILE_COMPRESSED;
}
}
}
}
}
/* Defect 309982 - Create-OpenIfThere was not setting accessedTime */
if (!(createMsg->ret_openCreateAction & zBEAST_CREATED) &&
(createMsg->createFlags & zCREATE_OPEN_IF_THERE))
{
/* Set the accessed time. We no longer set accessed time on reads and
* writes. It is only set when a file is opened or created */
if (!creatingDataStream)
{
BOOL noAtime;
noAtime = (nameMsg->curvol->VOLenabledAttributes & zATTR_NO_ATIME) != 0? TRUE : FALSE;
if (!(nameMsg->curvol->VOLenabledAttributes & zATTR_READONLY) &&
!noAtime &&
(retFileHandle != NULL) &&
!(retFileHandle->ptr->grantedRights & zRR_DONT_UPDATE_ACCESS_TIME))
{
((File_s *)newBeast)->FILEaccessedTime = GetUTCTime();
((File_s *)newBeast)->FILEaccessedDOSTime = INVALID_DOS_TIME;
COMN_MARK_BEAST_MESSY( &((File_s *)newBeast)->FILEroot);
}
}
}
/* truncate newFile if it's not new and if createFlags instructs so */
if (!(createMsg->ret_openCreateAction & zBEAST_CREATED) &&
(createMsg->createFlags & zCREATE_TRUNCATE_IF_THERE))
{
if (!creatingDataStream)
{
((File_s *)newBeast)->FILEmodifierID =
createMsg->ownerID;
aStack->currentTime = GetUTCTime();
((File_s *)newBeast)->FILEmodifiedTime =
((File_s *)newBeast)->FILEaccessedTime =
((File_s *)newBeast)->FILEmetaDataModifiedTime =
aStack->currentTime;
((File_s *)newBeast)->FILEmodifiedDOSTime =
((File_s *)newBeast)->FILEaccessedDOSTime =
((File_s *)newBeast)->FILEmetaDataModifiedDOSTime =
INVALID_DOS_TIME;
newBeast->NAMEDmetaDataSeqNum++;
}
COMN_MARK_BEAST_MESSY(&newBeast->NAMEDroot);
createMsg->ret_openCreateAction |= zBEAST_TRUNCATED;
if (newBeast->NAMEDeof != 0)
{
/* physically remove all blocks from block 0 to the end
* of the file. It does not matter if this call gets an
* error or not, because it is OK for the physical EOF
* to be larger than logical EOF */
if ((nameMsg->curvol->VOLenabledAttributes & zATTR_COW) &&
(retFileHandle != NULL) &&
(fhState & FH_WRITE_SNAPSHOT) &&
(newBeast->NAMEDroot.fileSnapshotBeast != NULL))
{
if (COMN_CopyFilemapToSnapshot(genMsg,
&newBeast->NAMEDroot, 0) != zOK)
{
zASSERT(0);
ClearErrno(genMsg);
}
}
if (BST_truncate(genMsg, newBeast, 0, -1, 0) != zOK)
{
zASSERT(0); /* just to see if it ever happens, can be removed*/
ClearErrno(genMsg);
}
/* After beast is truncated, its related compBeast is thrown away.
* we need to decrement compBeast's useCount that we add in
* CM_prepareAccessCompFile
*/
if (retFileHandle != NULL && retFileHandle->ptr->compBeast)
{
COMN_Release(&retFileHandle->ptr->compBeast);
}
if (creatingDataStream)
dstrCounts->dataSize -= newBeast->NAMEDeof;
newBeast->NAMEDeof = 0;
COMN_MARK_BEAST_DIRTY(&newBeast->NAMEDroot);
}
}
status = zOK;
/* For every open file, I want to make sure that:
* 1) There is an inode associated with this file.
* This is to ensure that pages read and written will come from
* the Linux cache. Without an inode this cannot happen.
* Open can also happen from zOpen or zCreate.
* 2) There is an useCount on the inode. Close will do iput.
* 3) lsa_get_inode does an iget which will call lsa_read_inode which
* will do a lookup which will set rb_inode for the beast.
* 4) Create with Open does not have the inode count incremented,
* so it should not come with the NAMPFL_noExtraInodeCount set.
*/
if (retFileHandle && Ptr_lsa_get_inode)
{
struct inode *fh_inode = NULL;
BOOL needLsaInode = FALSE;
if (nameMsg->curFile->FILEroot.rb_inode)
{
/* if (!(nameMsg->parseFlags & NAMPFL_noExtraInodeCount)) */
{
fh_inode = igrab(nameMsg->curFile->FILEroot.rb_inode);
if (!fh_inode)
{
needLsaInode = TRUE;
}
}
}
else
{
needLsaInode = TRUE;
}
if (needLsaInode)
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
fh_inode = Ptr_lsa_get_inode(
&nameMsg->curFile->FILEvolume->VOLvolumeID,
nameMsg->curFile->FILEzid);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
#if 0
// if (!(nameMsg->curFile->FILEroot.rb_inode) ||
// ( /* !(nameMsg->parseFlags & NAMPFL_noExtraInodeCount) && */
// ((fh_inode = igrab(nameMsg->curFile->FILEroot.rb_inode)) == NULL)))
// {
// COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
// fh_inode = Ptr_lsa_get_inode(
// &nameMsg->curFile->FILEvolume->VOLvolumeID,
// nameMsg->curFile->FILEzid);
// COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
// }
#endif
retFileHandle->ptr->fh_inode = fh_inode;
}
sendExitEvent:
RELEASE_RSRC(NAME_RESERVE);
if ((!(createMsg->requestedRights & zRR_PREVENT_FS_HOOKS)) &&
FSHOOKS_OR_EVENTS(F3OpenCreate_Hook, EVENT_Create_Exit))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
CHECK_FSHOOKS_CREATE_EXIT(F3OpenCreate_Hook, &aStack->parms, genMsg, nameMsg,
createMsg, beastName, aStack->asciiBeastName, retFileHandle);
if (NEBEventInfo[EVENT_Create_Exit].consumers)
{
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk, EVENT_Create_Exit, aStack->exit, id);
INIT_CREATE_EXIT_EVENT(aStack->exit, status, createMsg, nameMsg,
retFileHandle);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
if (beastNameAllocated)
{
free(beastName);
beastNameAllocated = FALSE;
}
if (dosNameSpace != NULL)
{
COMN_Release(&dosNameSpace);
}
STACK_FREE();
RTN_STATUS(status);
/*===========================================================================*/
//cleanup_freeSpace:
// if (createMsg->attributes & zFA_SUBDIRECTORY)
// {
// BST_truncate(genMsg,newBeast,0,-1, 0);
// }
cleanup_resetNameMsg:
if (creatingDataStream)
{
nameMsg->curDataStream = (NamedBeast_s *)nameMsg->curFile;
COMN_USE_BEAST(&nameMsg->curDataStream->NAMEDroot);
}
else
{
COMN_Release(&nameMsg->curDataStream);
nameMsg->curFile = curDir;
nameMsg->curDataStream = (NamedBeast_s *)curDir;
COMN_USE_BEAST(&nameMsg->curDataStream->NAMEDroot);
}
cleanup_removeNames:
if (creatingDataStream)
{
dstrCounts->count--;
dstrCounts->nameSize -= unilen(beastName);
if (dstrCounts->count == 0)
{
zASSERT((dstrCounts->dataSize == 0) && (dstrCounts->nameSize == 0));
curDir->FILEnameFlags &= ~(dstrFlag);
COMN_MARK_BEAST_XLOCAL(&curDir->FILEroot,xaction);
}
}
NAME_RemoveAllNames(genMsg,newBeast,NULL,curDir, // cnt nameUniquifier,
(REMNAMEFL_REMOVE_ALL_NAMES | REMNAMEFL_REMOVE_PARENT_AUTH |
REMNAMEFL_ERROR_BACKOUT), xaction,/* cnt NULL, */
&beastNamesRemoved,&dirNamesRemoved, NULL);
if ((beastNamesRemoved != beastNamesAdded) ||
(dirNamesRemoved != dirNamesAdded))
{
/* We are in a transaction where we inserted some names and
* were unable to back them out. Today, this deactivates the
* volume and prevents further writes. In the future
* this will be a real transaction abort (backout). */
/* FixFixFix6 -- When we implement real transaction
* abort, we need to revisit this code */
zASSERT("Unable to remove Inserted names after failed Create" == NULL);
SetErrno(genMsg,zERR_NAMING_INCONSISTENCY);
COMN_AbortXLocal(genMsg,curDir->FILEvolume,xaction, WHERE);
}
cleanup_newBeast:
{
BST_UNLINK_FROM_ALL_LISTS(&newBeast->NAMEDroot);
/* Bug 340515 - Added BST_STATE_DO_NOT_WRITE to next line so that
* the new beast does not get written. If it gets written the
* volume ends up with 'orphaned objects' because we have already
* removed the names with NAME_RemoveAllNames.
*/
newBeast->NAMEDbstState |= (BST_STATE_TOSS_ON_RELEASE|BST_STATE_DO_NOT_WRITE);
COMN_UnlatchAndRelease(&newBeast, XLATCHED);
// NamedBeast_s *tempBeast;
//
// /* Assign newbeast to a temp pointer and unlatch and release (and NULLIFY)
// * the temp. This allows us to hang onto the real "newBeast" pointer so we
// * can use it in the subsequent call to BST_free.
// */
// tempBeast = newBeast;
// CANCEL_ALARM(newBeast->NAMEDmycache.agent.timer);
// COMN_UnlatchAndRelease(&tempBeast, XLATCHED);
/* Save errno around the write to prevent a successful write obscuring
* an error we are in the process of returning. COMN_ForceBeastWrite
* has paths that clear errno.
*/
aStack->tmpMsg = *genMsg;
if (COMN_ForceBeastWrite(&aStack->tmpMsg,curDir,xaction) != zOK)
{
zASSERT("Unable to Write directory beast" == NULL);
if (GetErrno(genMsg) == zOK)
{
*genMsg = aStack->tmpMsg;
}
}
}
cleanup_endXlocal:
COMN_EndXLocal(curDir,&xaction);
cleanup_freeOpenFile:
if (cleanupSnapBeast)
{
CleanupSnapshotBeast(&cleanupSnapBeast->NAMEDroot);
}
if (existingBeast)
COMN_UnlatchAndRelease(&existingBeast, XLATCHED);
if (beastNameAllocated)
{
free(beastName);
beastNameAllocated = FALSE;
}
if (retFileHandle)
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
MSG_DestroyKey(retFileHandle->key);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
if(neighborBeast)
{
COMN_UnlatchAndRelease(&neighborBeast, XLATCHED);
}
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
status = zFAILURE;
goto sendExitEvent;
cleanup_freeOpenFileAndReturn:
if (existingBeast)
{
COMN_UnlatchAndRelease(&existingBeast, XLATCHED);
}
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
if (beastNameAllocated)
{
free(beastName);
beastNameAllocated = FALSE;
}
if (retFileHandle)
{
if ((nameMsg->curFile) || (nameMsg->curDataStream))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
}
MSG_DestroyKey(retFileHandle->key);
if ((nameMsg->curFile) || (nameMsg->curDataStream))
{
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
}
cleanup_errorAndReturn:
if (dosNameSpace != NULL)
{
COMN_Release(&dosNameSpace);
}
RELEASE_RSRC(NAME_RESERVE);
STACK_FREE();
RTN_STATUS(zFAILURE);
/*** This error path only comes from events and fshooks when operation ***
*** is canceled ***/
cleanup_freeOpenFileExit:
if (existingBeast)
COMN_Release(&existingBeast);
if (beastNameAllocated)
{
free(beastName);
beastNameAllocated = FALSE;
}
if (retFileHandle)
{
MSG_DestroyKey(retFileHandle->key);
}
if ((nameMsg->curFile) || (nameMsg->curDataStream))
{
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
if (dosNameSpace != NULL)
{
COMN_Release(&dosNameSpace);
}
STACK_FREE();
RTN_STATUS(zFAILURE);
}
/**************************************************************************
* Common internal interface to DELETE any type of file
***************************************************************************/
STATUS COMN_Delete(
GeneralMsg_s *genMsg,
NamingMsg_s *nameMsg,
DeleteMsg_s *deleteMsg)
{
File_s *curDir;
STATUS status = zOK;
Xaction_s *xaction;
// cnt NINT nameUniquifier;
Time_t currentTime;
UserID_t userID = COMN_GetUserID(genMsg);
BOOL deletingDataStream = FALSE;
BOOL deletingExtAttr = FALSE;
NINT dstrFlag = 0;
NINT dstrNameLen;
BOOL curDirLatched = FALSE;
NINT latchType;
NINT id;
DataStreamCounts_s *dstrCounts = NULL;
ParentEntry_s *pentry;
NINT hookType;
STATUS rc;
HardLinkBeast_s *hl;
File_s *primaryBeast = NULL;
File_s *neighborBeast = NULL;
BOOL DoPurgeDeletedDirectoryTree;
BOOL lsaDelDentry = FALSE;
typedef struct Stack_s {
SearchMsg_s srchMsg;
FSHooks_F3EraseFile_s eraseParms;
FSHooks_PurgeFile_s purgeParms;
struct EventBlock evBlk;
EventDeleteEnter_s enter;
EventDeleteExit_s exit;
LSAInvalidateDentry_s lsaInv;
} Stack_s;
STACK_ALLOC();
ASSERT_MPKNSS_LOCK();
COMN_ENABLE();
ENTER(TCOMMON, COMN_Delete);
RESERVE_RSRC(NAME_RESERVE);
/* Defect #306666. We can't allow unlicensed connection access */
if (!ConnectionIsLoggedIn(genMsg->pssConn.id))
{
/* They are not logged in (LICENCED), so limit their access */
SetErrno(genMsg, zERR_NO_DELETE_PRIVILEGE);
goto cleanup_error;
}
/*---------------------------------------------------------------------------
* Resolve up to, but not including the leaf name
* NOTE--If nameMsg points to a volume/Zid this resolves
*---------------------------------------------------------------------------*/
if (nameMsg->parseMode == NAMPMODE_Undefined)
COMN_SET_NAMING_MSG_PARSEMODE(nameMsg, NAMPMODE_DoNotResolveLeafName);
zASSERT(nameMsg->latchType == XLATCHED);
nameMsg->parseFlags |= NAMPFL_dontProcessHardLink; /* make sure we don't auto-forward on a hardlink just yet */
rc = COMN_Lookup(genMsg, nameMsg);
nameMsg->parseFlags &= ~NAMPFL_dontProcessHardLink;
if (rc != zOK)
goto cleanup_error;
ASSERT_XLATCH(&nameMsg->curFile->FILEbeastLatch);
ASSERT_XLATCH(&nameMsg->curDataStream->NAMEDbeastLatch);
/* Check if the volume is READ-ONLY */
if (nameMsg->curvol->VOLenabledAttributes & zATTR_READONLY)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_error;
}
if (!(nameMsg->retParseFlags & NAMRETPFL_haveAParsedComponent))
{
/* This is for the case where there was no path, we had a file handle, and the link was
detected at file open time and noted in the file handle structure.
If we have traversed through a hardlink, we need to backup to the hardlink beast and
release all reference to the primary. Make sure the latches are the hardlink, not on
the primary
*/
if((hl = nameMsg->hlFile))
{
nameMsg->hlFile = NULL;
COMN_UNLATCH_AND_RELEASE_NAMEMSG_BEASTS_KEEP_LATCHTYPE(nameMsg);
nameMsg->curDataStream = (NamedBeast_s*) hl;
nameMsg->curFile = (File_s*)hl;
COMN_USE_BEAST(&nameMsg->curFile->FILEroot)
nameMsg->fileParentZid = nameMsg->curFile->FILEfirstParentZid;
}
/* we resolved to a leaf because the input path was a zid or handle.
* we must get curdir with a separate lookup. */
if ((nameMsg->curDataStream->NAMEDzid == zROOTDIR_ZID) ||
(nameMsg->fileParentZid == zINVALID_ZID))
{
/* Can't delete rootdir, or beast without a parent */
SetErrno(genMsg,zERR_INVALID_PATH);
goto cleanup_error;
}
if (nameMsg->curDataStream == (NamedBeast_s *)nameMsg->curFile)
{
if ((curDir = COMN_LookupByZid(genMsg, nameMsg->curvol,
nameMsg->fileParentZid, NOTLATCHED, FALSE)) == NULL)
{
goto cleanup_error;
}
}
else /* This is a secondary datastream */
{
deletingDataStream = TRUE;
curDir = nameMsg->curFile;
COMN_USE_BEAST(&curDir->FILEroot);
zASSERT(!COMN_IsDerivedFrom(nameMsg->curDataStream,zFTYPE_FILE));
}
}
else /* >= 1 component, may not be a secondary datastream */
{
zASSERT(nameMsg->curDataStream == (NamedBeast_s *)nameMsg->curFile);
zASSERT(COMN_IsDerivedFrom(nameMsg->curFile,zFTYPE_FILE));
curDir = nameMsg->curFile;
COMN_USE_BEAST(&curDir->FILEroot);
if ((nameMsg->workNameType == zNTYPE_DATA_STREAM) ||
(nameMsg->workNameType == zNTYPE_EXTENDED_ATTRIBUTE))
{
deletingDataStream = TRUE;
}
}
if (!deletingDataStream)
{
zASSERT(curDir->FILEattributes & zFA_SUBDIRECTORY);
}
else
{
BOOL relatchNeeded = FALSE;
/* Make sure we have a valid dataStreamInfo structure on the file
* beast, and make sure that if we have any existing data stream
* beasts of this name type, that we have their summary info in memory*/
if (nameMsg->curDataStream != (NamedBeast_s *)nameMsg->curFile)
{
UNX_LATCH(&nameMsg->curDataStream->NAMEDbeastLatch);
relatchNeeded = TRUE;
}
if (COMN_InitDataStreamInfo(genMsg,nameMsg->curFile,
nameMsg->workNameType) != zOK)
{
if (relatchNeeded)
X_LATCH(&nameMsg->curDataStream->NAMEDbeastLatch);
goto cleanup_releaseDirectory;
}
if (relatchNeeded)
X_LATCH(&nameMsg->curDataStream->NAMEDbeastLatch);
zASSERT(nameMsg->curFile->FILEdataStreamInfo != NULL);
/* setup local values for simpler code later on */
if (nameMsg->workNameType == zNTYPE_DATA_STREAM)
{
dstrCounts = &nameMsg->curFile->FILEdataStreamInfo->dataStream;
dstrFlag = NFL_HAS_DATA_STREAMS;
}
else
{
dstrCounts = &nameMsg->curFile->FILEdataStreamInfo->extAttr;
dstrFlag = NFL_HAS_EXTENDED_ATTRIBUTES;
}
}
zASSERT(COMN_IsDerivedFrom(curDir,zFTYPE_FILE));
/*---------------------------------------------------------------------------
* At this point, curDir is a pointer to the container, which has a use Count
* on it. nameMsg->curDataStream points to either the container or to the
* beast to be deleted, and it is XLATCHED and has a use Count.
*---------------------------------------------------------------------------*/
if (!(nameMsg->scanMsg.retScanFlags & NSRETSFL_hasWildcardChars))
{
/*---------------------------------------------------------------------------
* There were no wildcard characters, Delete the file directly by name
* Save a pointer to the directory before calling lookup.
*---------------------------------------------------------------------------*/
COMN_SET_NAMING_MSG_PARSEMODE(nameMsg, NAMPMODE_FullyResolveAny);
nameMsg->parseFlags |= NAMPFL_dontProcessHardLink; /* make sure we don't auto-forward on a hardlink just yet */
rc = COMN_Lookup(genMsg, nameMsg);
nameMsg->parseFlags &= ~NAMPFL_dontProcessHardLink;
if (rc != zOK)
goto cleanup_releaseDirectory;
/* Never get the latch of a parent when the child is latched. This
* is to prevent deadlock. Unlatch the child, latch the parent,
* and then re-latch the child. */
if (curDir != nameMsg->curFile)
{
COMN_UNLATCH_NAMEMSG_BEASTS_KEEP_LATCHTYPE(nameMsg);
X_LATCH(&curDir->FILEbeastLatch);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg,XLATCHED);
curDirLatched = TRUE;
}
/* Based on the nameType, this is either a purge or a delete */
hookType = (nameMsg->workNameType == zNTYPE_DELETED_FILE) ?
PurgeFile_Hook : F3EraseFile_Hook;
INIT_EVENT_ID_STATUS(id, aStack->exit.enterRetStatus);
if (FSHOOKS_OR_EVENTS(hookType, EVENT_Delete_Enter))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
if (curDirLatched)
{
UNX_LATCH(&curDir->FILEbeastLatch);
curDirLatched = FALSE;
}
RELEASE_RSRC(NAME_RESERVE);
if (hookType == PurgeFile_Hook)
{
CHECK_FSHOOKS_PURGE_ENTER(PurgeFile_Hook, &aStack->purgeParms, status,
releaseDirectoryExit, genMsg, nameMsg);
}
else
{
CHECK_FSHOOKS_DELETE_ENTER(F3EraseFile_Hook, &aStack->eraseParms, status,
releaseDirectoryExit, genMsg, nameMsg);
}
if (NEBEventInfo[EVENT_Delete_Enter].consumers)
{
INIT_ENTER_EVENT(genMsg, nameMsg, aStack->evBlk, EVENT_Delete_Enter,
aStack->enter, id);
ZOS_ProduceEvent(status, &aStack->evBlk);
CHECK_ENTER_RETURN(genMsg, status, aStack->evBlk, aStack->exit,
releaseDirectoryExit);
}
RESERVE_RSRC(NAME_RESERVE);
if (curDir != nameMsg->curFile)
{
curDirLatched = TRUE;
X_LATCH(&curDir->FILEbeastLatch);
}
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
primaryBeast = HL_CheckIfHardlinkAndReturnPrimary(
genMsg, nameMsg->curFile, nameMsg->latchType);
if (nameMsg->curFile->FILEmayIDoThis(genMsg,
primaryBeast ? primaryBeast : nameMsg->curFile,
nameMsg->fileParentZid, MAY_I_DELETE) != zOK)
{
SetErrno(genMsg, zERR_NO_DELETE_PRIVILEGE);
if (primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, nameMsg->latchType);
}
goto cleanup_unlatchDirectory;
}
if (primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, nameMsg->latchType);
}
if (!(nameMsg->curFile->FILEattributes & zFA_HARDLINK) && (nameMsg->curFile->opLockControl != NULL))
{
status = OPLOCK_Break(genMsg, nameMsg->curFile, FALSE);
if (status != zOK)
{
if (GetErrno(genMsg) != zERR_OPLOCK_MUST_WAIT)
{
goto cleanup_unlatchDirectory;
}
ClearErrno(genMsg);
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
if (curDirLatched)
{
UNX_LATCH(&curDir->FILEbeastLatch);
curDirLatched = FALSE;
}
status = OPLOCK_WaitForBreak(genMsg, nameMsg->curFile);
if (status != zOK)
{
goto sendExitEvent;
}
if (curDir != nameMsg->curFile)
{
curDirLatched = TRUE;
X_LATCH(&curDir->FILEbeastLatch);
}
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
OPLOCK_BreakShared(nameMsg->curFile);
}
}
/* See if we will be deleting the existing beast and if so, do some setup for
the beast deletion before starting the transaction
*/
if(BST_deleteSetup(genMsg,(File_s*)nameMsg->curDataStream, &primaryBeast, &neighborBeast) != zOK)
{
goto cleanup_unlatchDirectory;
}
/* Don't allow delete if the file is exclusive locked */
if ((nameMsg->curDataStream->NAMEDdenyReaderCount != 0) ||
(nameMsg->curDataStream->NAMEDdenyWriterCount != 0))
{
if (COMN_IsDenyReaderWriterNotMe(genMsg,nameMsg->curDataStream))
{
SetErrno(genMsg,zERR_ALL_FILES_IN_USE);
goto cleanup_unlatchDirectory;
}
}
if ((nameMsg->curDataStream->NAMEDdontDeleteWhileOpenCount != 0) &&
(FH_OpenFileMayNotBeDeleted(genMsg,nameMsg->curDataStream)))
{
SetErrno(genMsg, zERR_CANT_DELETE_OPEN_FILE);
goto cleanup_unlatchDirectory;
}
if (!(deleteMsg->deleteFlags & zDELETE_FORCE_DELETE) &&
nameMsg->curFile->FILEattributes & zFA_DELETE_INHIBIT)
{
SetErrno(genMsg,zERR_ALL_FILES_READ_ONLY);
goto cleanup_unlatchDirectory;
}
if (deletingDataStream)
{
if (NAME_GetFirstNameFromBeast(genMsg, nameMsg->curDataStream,
0, NULL, NULL, NULL, &dstrNameLen) != zOK)
{
goto cleanup_unlatchDirectory;
}
}
/*---------------------------------------------------------------------------
* At this point, we have the beasts re-latched. Since we have previously
* unlatched them, we need to check to see if they are still valid. The
* main case we are looking for here is when another thread comes in and
* deletes the beast while we have it unlatched. This actually happens
* in real life.
*---------------------------------------------------------------------------*/
if ((pentry = NAME_GetParentEntry(genMsg, nameMsg->curDataStream /* cnt nameUniquifier */)) == NULL)
{
goto cleanup_unlatchDirectory;
}
if (nameMsg->workNameType != pentry->p.nameType)
{
/* The beast is no longer in the same "nameType" state that it was
* when we first looked it up */
SetErrno(genMsg,zERR_NAME_NOT_FOUND_IN_DIRECTORY);
goto cleanup_unlatchDirectory;
}
/*---------------------------------------------------------------------------
* Delete the beast, this is done inside of a transaction.
*---------------------------------------------------------------------------*/
xaction = COMN_BeginXLocal(curDir);
if (deletingDataStream)
{
dstrCounts->count--;
dstrCounts->dataSize -= nameMsg->curDataStream->NAMEDeof;
dstrCounts->nameSize -= dstrNameLen;
if (dstrCounts->count == 0)
{
zASSERT((dstrCounts->dataSize == 0) && (dstrCounts->nameSize == 0));
curDir->FILEnameFlags &= ~(dstrFlag);
COMN_MARK_BEAST_XLOCAL(&curDir->FILEroot,xaction);
}
if (COMN_IsDerivedFrom(nameMsg->curDataStream,
zFTYPE_EXTENDED_ATTRIBUTE))
{
deletingExtAttr = TRUE;
}
}
if ( !(deleteMsg->deleteFlags & zDELETE_KEEP_VFS_CACHE))
{
if ( COMN_LsaCleanupSetup(nameMsg->curDataStream,
/* cnt nameUniquifier,*/ &aStack->lsaInv) == zOK)
{
if (primaryBeast != NULL)
{
aStack->lsaInv.lid_zid = primaryBeast->FILEzid;
}
else
{
aStack->lsaInv.lid_zid = nameMsg->curDataStream->NAMEDzid;
}
aStack->lsaInv.lid_pZid = curDir->FILEzid;
lsaDelDentry = TRUE;
}
}
if ((deleteMsg->deleteFlags & zDELETE_PURGE_IMMEDIATE) &&
(!(nameMsg->curFile->FILEattributes & zFA_IMMEDIATE_PURGE)))
{
nameMsg->curFile->FILEattributes |= zFA_IMMEDIATE_PURGE;
status = BST_delete( genMsg, curDir, nameMsg->curDataStream,
primaryBeast, neighborBeast, xaction, FALSE, TRUE);
if ((status != zOK) && (nameMsg->curFile != NULL))
{
nameMsg->curFile->FILEattributes &= ~zFA_IMMEDIATE_PURGE;
}
}
else
{
status = BST_delete( genMsg, curDir, nameMsg->curDataStream,
primaryBeast, neighborBeast, xaction, FALSE, TRUE);
}
if ((status != zOK) &&
(GetErrno(genMsg) == zERR_DIRECTORY_NOT_EMPTY) &&
(nameMsg->workNameType == zNTYPE_DELETED_FILE))
{
DoPurgeDeletedDirectoryTree = TRUE;
}
else
{
DoPurgeDeletedDirectoryTree = FALSE;
}
currentTime = GetUTCTime();
if (!deletingExtAttr)
{
curDir->FILEmodifiedTime = currentTime;
curDir->FILEmodifiedDOSTime = INVALID_DOS_TIME;
curDir->FILEmodifierID = userID;
}
OID_SaveObjectID(nameMsg->curvol, &userID);
curDir->FILEmetaDataSeqNum++;
curDir->FILEvolume->VOLfile.FILEmodifiedTime = currentTime;
COMN_MARK_BEAST_MESSY(&curDir->FILEroot);
COMN_ForceBeastWrite(genMsg,curDir,xaction);
COMN_EndXLocal(curDir,&xaction);
if(neighborBeast)
{
COMN_UnlatchAndRelease(&neighborBeast, XLATCHED);
}
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
if (status == zOK)
{
COMN_UnlatchAndRelease(&nameMsg->curDataStream, XLATCHED);
if (deletingDataStream)
{
COMN_UnlatchAndRelease(&nameMsg->curFile, nameMsg->latchType);
}
else
{
COMN_Release(&nameMsg->curFile);
}
}
else
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
}
if (curDirLatched)
{
UNX_LATCH(&curDir->FILEbeastLatch);
curDirLatched = FALSE;
}
if (lsaDelDentry && Ptr_lsa_invalidate_dentry)
{
Ptr_lsa_invalidate_dentry( &aStack->lsaInv );
}
if (DoPurgeDeletedDirectoryTree)
{
zASSERT(!deletingDataStream);
/* We just tried to purge a deleted directory, but failed because
* the dir still contains other deleted files/dirs. Synchronously
* auto-purge each contained subfile first. We do this at this high
* level so that we can be outside of the original transaction, and
* so that we can start an individual transaction for every purge.
*/
ClearErrno(genMsg);
status = COMN_PurgeDeletedDirectoryTree( genMsg, curDir->FILEzid,
/* cnt nameUniquifier, */ nameMsg->curFile, FALSE);
}
sendExitEvent:
RELEASE_RSRC(NAME_RESERVE);
if (hookType == PurgeFile_Hook)
{
CHECK_FSHOOKS_PURGE_EXIT(PurgeFile_Hook, &aStack->purgeParms, genMsg,
nameMsg);
}
else
{
CHECK_FSHOOKS_DELETE_EXIT(F3EraseFile_Hook, &aStack->eraseParms, genMsg,
nameMsg);
}
if (NEBEventInfo[EVENT_Delete_Exit].consumers)
{
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk, EVENT_Delete_Exit, aStack->exit, id);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
goto releaseDirectoryExit;
}
/*---------------------------------------------------------------------------
* Wildcard was specified, are wildcards allowed
*---------------------------------------------------------------------------*/
else if (!(nameMsg->parseFlags & NAMPFL_allowWildcardChars))
{
SetErrno(genMsg,zERR_INVALID_NAME);
goto cleanup_releaseDirectory;
}
else
{
/*---------------------------------------------------------------------------
* Do a wildcard delete.
*---------------------------------------------------------------------------*/
NINT deleteCnt = 0;
BOOL noPriv = FALSE;
BOOL readOnly = FALSE;
BOOL evOp = FALSE;
BOOL locked = FALSE;
STATUS evError = zOK;
COMN_INIT_SEARCH_MSG(&aStack->srchMsg);
COMN_SETUP_WILDOPEN_SEARCH_MSG(&aStack->srchMsg,zNTYPE_FILE);
if (COMN_WildOpen(genMsg,nameMsg,&aStack->srchMsg) != zOK)
goto cleanup_releaseDirectory;
for (;;)
{
genMsg->flags |= DO_NOT_SEND_FSHOOKS;
nameMsg->parseFlags |= NAMPFL_dontProcessHardLink; /* make sure we don't auto-forward on a hardlink just yet */
rc = COMN_WildRead(genMsg,nameMsg,&aStack->srchMsg);
nameMsg->parseFlags &= ~NAMPFL_dontProcessHardLink;
if ( rc != zOK)
{
genMsg->flags &= ~DO_NOT_SEND_FSHOOKS;
if (GetErrno(genMsg) != zERR_NAME_NOT_FOUND_IN_DIRECTORY)
goto cleanup_closedir;
ClearErrno(genMsg);
break;
}
genMsg->flags &= ~DO_NOT_SEND_FSHOOKS;
/* Never get the latch of a parent when the child is latched. This
* is to prevent deadlock. Unlatch the child, latch the parent,
* and then re-latch the child. */
if (curDir != nameMsg->curFile)
{
COMN_UNLATCH_NAMEMSG_BEASTS_KEEP_LATCHTYPE(nameMsg);
X_LATCH(&curDir->FILEbeastLatch);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg,XLATCHED);
curDirLatched = TRUE;
}
/* Based on the nameType, this is either a purge or a delete */
hookType = (nameMsg->workNameType == zNTYPE_DELETED_FILE) ?
PurgeFile_Hook : F3EraseFile_Hook;
INIT_EVENT_ID_STATUS(id, aStack->exit.enterRetStatus);
if (FSHOOKS_OR_EVENTS(F3EraseFile_Hook, EVENT_Delete_Enter))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
if (curDirLatched)
{
UNX_LATCH(&curDir->FILEbeastLatch);
curDirLatched = FALSE;
}
RELEASE_RSRC(NAME_RESERVE);
if (hookType == PurgeFile_Hook)
{
CHECK_FSHOOKS_PURGE_ENTER(PurgeFile_Hook, &aStack->purgeParms,
status, skipOperation, genMsg, nameMsg);
}
else
{
CHECK_FSHOOKS_DELETE_ENTER(F3EraseFile_Hook, &aStack->eraseParms,
status, skipOperation, genMsg, nameMsg);
}
if (NEBEventInfo[EVENT_Delete_Enter].consumers)
{
INIT_ENTER_EVENT(genMsg, nameMsg, aStack->evBlk,
EVENT_Delete_Enter, aStack->enter, id);
ZOS_ProduceEvent(status, &aStack->evBlk);
CHECK_ENTER_RETURN(genMsg, status, aStack->evBlk, aStack->exit,
skipOperation);
}
RESERVE_RSRC(NAME_RESERVE);
if (curDir != nameMsg->curFile)
{
curDirLatched = TRUE;
X_LATCH(&curDir->FILEbeastLatch);
}
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
/* See if we will be deleting the existing beast and if so, do some setup for
the beast deletion before starting the transaction
*/
if(BST_deleteSetup(genMsg,(File_s*)nameMsg->curDataStream,
&primaryBeast, &neighborBeast) != zOK)
{
goto deleteNextFile;
}
if (nameMsg->curFile->FILEmayIDoThis(genMsg,
primaryBeast ? primaryBeast : nameMsg->curFile,
nameMsg->fileParentZid, MAY_I_DELETE) != zOK)
{
noPriv = TRUE;
SetErrno(genMsg, zERR_NO_DELETE_PRIVILEGE);
goto deleteNextFile;
}
/* Don't allow delete if the file is exclusive locked */
if ((nameMsg->curDataStream->NAMEDdenyReaderCount != 0) ||
(nameMsg->curDataStream->NAMEDdenyWriterCount != 0))
{
if (COMN_IsDenyReaderWriterNotMe(genMsg,nameMsg->curDataStream))
{
locked = TRUE;
SetErrno(genMsg,zERR_ALL_FILES_IN_USE);
goto deleteNextFile;
}
}
if ((nameMsg->curDataStream->NAMEDdontDeleteWhileOpenCount != 0) &&
(FH_OpenFileMayNotBeDeleted(genMsg,nameMsg->curDataStream)))
{
locked = TRUE;
SetErrno(genMsg, zERR_CANT_DELETE_OPEN_FILE);
goto deleteNextFile;
}
if (!(deleteMsg->deleteFlags & zDELETE_FORCE_DELETE) &&
nameMsg->curFile->FILEattributes & zFA_DELETE_INHIBIT)
{
readOnly = TRUE;
SetErrno(genMsg,zERR_ALL_FILES_READ_ONLY);
goto deleteNextFile;
}
if (deletingDataStream)
{
if (NAME_GetFirstNameFromBeast(genMsg, nameMsg->curDataStream,
0, NULL, NULL, NULL, &dstrNameLen) != zOK)
{
goto deleteNextFile;
}
}
/*---------------------------------------------------------------------------
* At this point, we have the beasts re-latched. Since we have previously
* unlatched them, we need to check to see if they are still valid. The
* main case we are looking for here is when another thread comes in and
* deletes the beast while we have it unlatched. This actually happens
* in real life.
*---------------------------------------------------------------------------*/
if ((pentry = NAME_GetParentEntry(genMsg, nameMsg->curDataStream/* cnt nameUniquifier */)) == NULL)
{
goto deleteNextFile;
}
if (nameMsg->workNameType != pentry->p.nameType)
{
/* The beast is no longer in the same "nameType" state that it
* was when we first looked it up */
SetErrno(genMsg,zERR_NAME_NOT_FOUND_IN_DIRECTORY);
goto deleteNextFile;
}
/*---------------------------------------------------------------------------
* Start a transaction and then delete the beast.
*---------------------------------------------------------------------------*/
xaction = COMN_BeginXLocal(curDir);
if (deletingDataStream)
{
dstrCounts->count--;
dstrCounts->dataSize -= nameMsg->curDataStream->NAMEDeof;
dstrCounts->nameSize -= dstrNameLen;
if (dstrCounts->count == 0)
{
zASSERT((dstrCounts->dataSize == 0) && (dstrCounts->nameSize == 0));
curDir->FILEnameFlags &= ~(dstrFlag);
COMN_MARK_BEAST_XLOCAL(&curDir->FILEroot,xaction);
}
if (COMN_IsDerivedFrom(nameMsg->curDataStream,
zFTYPE_EXTENDED_ATTRIBUTE))
{
deletingExtAttr = TRUE;
}
}
if ( !(deleteMsg->deleteFlags & zDELETE_KEEP_VFS_CACHE))
{
if ( COMN_LsaCleanupSetup(nameMsg->curDataStream,
/* cnt nameUniquifier,*/ &aStack->lsaInv) == zOK)
{
if (primaryBeast != NULL)
{
aStack->lsaInv.lid_zid = primaryBeast->FILEzid;
}
else
{
aStack->lsaInv.lid_zid = nameMsg->curDataStream->NAMEDzid;
}
aStack->lsaInv.lid_pZid = curDir->FILEzid;
lsaDelDentry = TRUE;
}
}
if ((deleteMsg->deleteFlags & zDELETE_PURGE_IMMEDIATE) &&
(!(nameMsg->curFile->FILEattributes & zFA_IMMEDIATE_PURGE)))
{
nameMsg->curFile->FILEattributes |= zFA_IMMEDIATE_PURGE;
status = BST_delete( genMsg, curDir, nameMsg->curDataStream,
primaryBeast, neighborBeast, xaction, FALSE, TRUE);
if ((status != zOK) && (nameMsg->curFile != NULL))
{
nameMsg->curFile->FILEattributes &= ~zFA_IMMEDIATE_PURGE;
}
}
else
{
status = BST_delete( genMsg, curDir, nameMsg->curDataStream,
primaryBeast, neighborBeast, xaction, FALSE, TRUE);
}
if ((status != zOK) &&
(GetErrno(genMsg) == zERR_DIRECTORY_NOT_EMPTY) &&
(nameMsg->workNameType == zNTYPE_DELETED_FILE))
{
DoPurgeDeletedDirectoryTree = TRUE;
}
else
{
DoPurgeDeletedDirectoryTree = FALSE;
}
currentTime = GetUTCTime();
if (!deletingExtAttr)
{
curDir->FILEmodifiedTime = currentTime;
curDir->FILEmodifiedDOSTime = INVALID_DOS_TIME;
curDir->FILEmodifierID = userID;
}
OID_SaveObjectID(nameMsg->curvol, &userID);
curDir->FILEmetaDataSeqNum++;
curDir->FILEvolume->VOLfile.FILEmodifiedTime = currentTime;
COMN_MARK_BEAST_MESSY(&curDir->FILEroot);
COMN_ForceBeastWrite(genMsg,curDir,xaction);
COMN_EndXLocal(curDir,&xaction);
if(neighborBeast)
{
COMN_UnlatchAndRelease(&neighborBeast, XLATCHED);
}
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
if (status == zOK)
{
COMN_UnlatchAndRelease(&nameMsg->curDataStream, XLATCHED);
COMN_Release(&nameMsg->curFile);
}
else
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
}
if (curDirLatched)
{
UNX_LATCH(&curDir->FILEbeastLatch);
curDirLatched = FALSE;
}
if (lsaDelDentry && Ptr_lsa_invalidate_dentry)
{
Ptr_lsa_invalidate_dentry( &aStack->lsaInv );
lsaDelDentry = FALSE;
}
if (DoPurgeDeletedDirectoryTree)
{
/* We just tried to purge a deleted directory, but failed
* because the dir still contains other deleted files/dirs.
* Synchronously auto-purge each contained subfile first.
* We do this at this high level so that we can be outside of
* the original transaction, and so that we can start an
* individual transaction for every purge.
*/
ClearErrno(genMsg);
status = COMN_PurgeDeletedDirectoryTree( genMsg, curDir->FILEzid,
/* cnt nameUniquifier,*/ nameMsg->curFile, FALSE);
}
if (FSHOOKS_OR_EVENTS(hookType, EVENT_Delete_Exit))
{
RELEASE_RSRC(NAME_RESERVE);
if (hookType == PurgeFile_Hook)
{
CHECK_FSHOOKS_PURGE_EXIT(PurgeFile_Hook, &aStack->purgeParms,
genMsg, nameMsg);
}
else
{
CHECK_FSHOOKS_DELETE_EXIT(F3EraseFile_Hook, &aStack->eraseParms,
genMsg, nameMsg);
}
if (NEBEventInfo[EVENT_Delete_Exit].consumers)
{
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk,
EVENT_Delete_Exit, aStack->exit, id);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
RESERVE_RSRC(NAME_RESERVE);
}
if (status != zOK)
{
goto cleanup_closedir;
}
deleteCnt++;
/*---------------------------------------------------------------------------
* reset the nameMessage back to a search pattern type for the next loop
* of WildRead
*---------------------------------------------------------------------------*/
continueWithNextFile:
if (COMN_ResetNameMsgToSearchPattern(genMsg,nameMsg) != zOK)
goto cleanup_closedir;
continue;
deleteNextFile:
if(neighborBeast)
{
COMN_UnlatchAndRelease(&neighborBeast, XLATCHED);
}
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
if (curDirLatched)
{
UNX_LATCH(&curDir->FILEbeastLatch);
curDirLatched = FALSE;
}
if (FSHOOKS_OR_EVENTS(hookType, EVENT_Delete_Exit))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
RELEASE_RSRC(NAME_RESERVE);
if (hookType == PurgeFile_Hook)
{
CHECK_FSHOOKS_PURGE_EXIT(PurgeFile_Hook, &aStack->purgeParms,
genMsg, nameMsg);
}
else
{
CHECK_FSHOOKS_DELETE_EXIT(F3EraseFile_Hook, &aStack->eraseParms,
genMsg, nameMsg);
}
if (NEBEventInfo[EVENT_Delete_Exit].consumers)
{
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk,
EVENT_Delete_Exit, aStack->exit, id);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
ClearErrno(genMsg);
goto continueWithNextFile;
skipOperation:
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
if (status != zOK)
{
if (evOp == FALSE)
{
evOp = TRUE;
evError = GetErrno(genMsg);
}
ClearErrno(genMsg);
}
goto continueWithNextFile;
}
if (COMN_WildClose(genMsg,&aStack->srchMsg) != zOK)
{
zASSERT("Error calling COMN_WildClose on wildcard Delete"==NULL);
}
/*---------------------------------------------------------------------------
* See if we need to return an error. If no files were delete then an error
* is always returned. If some files were deleted, only return an error if
* we terminated prematurly.
*---------------------------------------------------------------------------*/
if (deleteCnt <= 0)
{
if (noPriv)
{
SetErrno(genMsg, zERR_NO_DELETE_PRIVILEGE);
}
else if (locked)
{
SetErrno(genMsg,zERR_ALL_FILES_IN_USE);
}
else if (readOnly)
{
SetErrno(genMsg,zERR_ALL_FILES_READ_ONLY);
}
else if (evOp)
{
SetErrno(genMsg, evError);
}
else
{
SetErrno(genMsg,zERR_NO_FILES_FOUND);
}
goto cleanup_releaseDirectory;
}
else /* Files were deleted--See if we need to return any errors */
{
if (noPriv)
{
SetErrno(genMsg, zERR_NO_DELETE_PRIVILEGE);
goto cleanup_releaseDirectory;
}
else if (locked)
{
SetErrno(genMsg,zERR_SOME_FILES_IN_USE);
goto cleanup_releaseDirectory;
}
else if (readOnly)
{
SetErrno(genMsg,zERR_SOME_FILES_READ_ONLY);
goto cleanup_releaseDirectory;
}
else if (evOp)
{
SetErrno(genMsg, evError);
goto cleanup_releaseDirectory;
}
else if (GetErrno(genMsg) != zOK)
{
goto cleanup_releaseDirectory;
}
}
}
COMN_Release(&curDir);
status = zOK;
justReturn:
if(neighborBeast)
{
COMN_UnlatchAndRelease(&neighborBeast, XLATCHED);
}
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
RELEASE_RSRC(NAME_RESERVE);
STACK_FREE();
RTN_STATUS(status);
/** You get here only if hooks or events cancel the operation **
** OR if resources are already released for both error and normal cases **
** in the non wild card mode **/
releaseDirectoryExit:
if(neighborBeast)
{
COMN_UnlatchAndRelease(&neighborBeast, XLATCHED);
}
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
COMN_Release(&curDir);
STACK_FREE();
RTN_STATUS(status);
cleanup_closedir:
COMN_Release(&curDir);
COMN_WildClose(genMsg,&aStack->srchMsg);
status = zFAILURE;
goto justReturn;
cleanup_unlatchDirectory:
if (curDirLatched)
{
UNX_LATCH(&curDir->FILEbeastLatch);
curDirLatched = FALSE;
}
status = zFAILURE;
goto sendExitEvent;
cleanup_releaseDirectory:
COMN_Release(&curDir);
cleanup_error:
status = zFAILURE;
goto justReturn;
}
/****************************************************************************
* This is a latch used by COMN_PurgeDeletedDirectoryTree and by
* BST_PurgeFileName. It's purpose is to allow any number of simultaneous
* auto purge requests from the LSS, but to single thread any purge that
* causes a stranded subtree to be auto-purged.
*****************************************************************************/
Latch_s PurgeDirLatch;
/****************************************************************************
* Delete all files contained in the subtree underneath a deleted directory
*****************************************************************************/
STATUS COMN_PurgeDeletedDirectoryTree(
GeneralMsg_s *genMsg,
Zid_t parentZid,
File_s *delDir,
BOOL skipDelDir) /* If true, don't purge delDir itself */
{
SearchMsg_s searchMsg;
NameSpace_s *nameSpace;
File_s *curDir;
Xaction_s *xaction;
Volume_s *vol;
File_s *primaryBeast = NULL;
typedef struct Stack_s {
NamingMsg_s nameMsg;
} Stack_s;
STACK_ALLOC();
ASSERT_MPKNSS_LOCK();
/* Do this before any error returns */
COMN_INIT_NAMING_MSG(&aStack->nameMsg);
/* Save inuse pointer to starting directory, and make sure it is a file */
curDir = delDir;
zASSERT(COMN_IsDerivedFrom(curDir,zFTYPE_FILE));
COMN_USE_BEAST(&curDir->FILEroot);
/* Single thread all auto purging when we are purging a deleted sub-tree.
* This prevents complicated and error prone interraction when multiple
* threads end up trying to purge parts of the same deleted subdirectory
* tree. This case is rare, so we don't take a performance hit by
* single threading these calls.
*/
X_LATCH(&PurgeDirLatch);
/* Lock the volume in an ACTIVE state so it can't be
* deactivated while we are actively purging this sub-tree. Note - This
* lock is separate from the one put in on behalf of the nameMsg. That
* extra lock is put in place because the CleanupNameMsg call unlocks it
* too. This extra lock is needed in case the nameMsg is ever reset
* in the middle by any of the wildcard code. */
vol = curDir->FILEvolume;
if (COMN_LockVolumeActive(genMsg,vol,FALSE) != zOK)
{
goto cleanup_return_noUnlock;
}
/*---------------------------------------------------------------------------
* Setup a nameMsg that points to the starting directory
*---------------------------------------------------------------------------*/
if ((nameSpace = COMN_NameSpaceIDLookup(genMsg, zNSPACE_DOS)) == NULL)
{
goto cleanup_return;
}
/* This extra lock is for the nameMsg cleanup code so our count wont
* get out of sync. */
if (COMN_LockVolumeActive(genMsg,curDir->FILEvolume,FALSE) != zOK)
{
COMN_Release(&nameSpace);
goto cleanup_return;
}
/*---------------------------------------------------------------------------
* If the skipDelDir flag is set, this means that the starting "delDir"
* has already been deleted, but an extra use count has been maintained on it.
* We need to use a special flag in genMsg that allows us to still lookup
* this deleted "delDir", but we also take special precautions to not allow
* this flag to let anything else be looked up that it shouldn't allow.
*---------------------------------------------------------------------------*/
if (skipDelDir)
{
genMsg->flags |= ALLOW_BST_STATE_PURGING;
}
COMN_USE_BEAST(&curDir->FILEvolume->VOLroot)
COMN_SETUP_NAMING_MSG_FILE_BEAST_PTR(&aStack->nameMsg, curDir->FILEvolume,
parentZid, /* cnt nameUniquifier,*/ curDir,
NAMPMODE_FullyResolveAny, NOTLATCHED, nameSpace,
zNTYPE_DELETED_FILE, NULL);
COMN_LATCH_AND_USE_NAMEMSG_BEASTS(&aStack->nameMsg,aStack->nameMsg.latchType);
/*---------------------------------------------------------------------------
* Setup a searchMsg to search every beast in the subtree
*---------------------------------------------------------------------------*/
COMN_STRUCT_INIT(searchMsg);
COMN_SETUP_SEARCH_MSG (&searchMsg, -1,
(SMAPOPT_32BitMode|SMAPOPT_searchAllDirs|SMAPOPT_matchAllEntries|
SMAPOPT_returnAllFilesFirst|SMAPOPT_notReusable),
zNTYPE_DELETED_FILE);
if (COMN_WildOpen(genMsg,&aStack->nameMsg,&searchMsg) != zOK)
{
goto cleanup_return;
}
/*---------------------------------------------------------------------------
* Read through the subtree, and delete every located file. Make a
* separate transaction for each file.
*---------------------------------------------------------------------------*/
genMsg->flags |= DO_NOT_SEND_FSHOOKS;
while (COMN_WildRead(genMsg, &aStack->nameMsg, &searchMsg) == zOK)
{
/* If asked to skip the starting "delDir", check if we are there yet */
if (skipDelDir)
{
if (aStack->nameMsg.curFile == delDir)
{
/* If we have just looked up the "delDir", we know it is already
* deleted. We can now just exit the loop.
*/
zASSERT(aStack->nameMsg.curFile->FILEbstState & BST_STATE_PURGING);
genMsg->flags &= ~ALLOW_BST_STATE_PURGING;
break;
}
else if (aStack->nameMsg.curFile->FILEbstState & BST_STATE_PURGING)
{
/* We just looked up a deleted file which we should not touch.
* skip this one.
*/
continue;
}
}
/* Get latch on the correct directory, and then latch the beast */
if (aStack->nameMsg.fileParentZid == curDir->FILEzid)
{
X_LATCH(&curDir->FILEbeastLatch);
}
else
{
COMN_Release(&curDir);
if ((curDir = COMN_LookupByZid(genMsg, aStack->nameMsg.curvol,
aStack->nameMsg.fileParentZid, XLATCHED, TRUE)) == NULL)
{
/*
* Change the error so the caller does not think the
* original name passed in is not found
*/
if (GetErrno(genMsg) == zERR_ZID_NOT_FOUND)
{
ForceSetErrno(genMsg, zERR_INTERNAL_DIRECTORY_ERROR);
}
break;
}
}
if(aStack->nameMsg.curDataStream->NAMEDattributes & zFA_HARDLINK)
{
zASSERT(((HardLinkBeast_s *)aStack->nameMsg.curDataStream)->HARDLprimaryZid != NULL);
/* get the primary */
primaryBeast = COMN_LookupByZid(genMsg, aStack->nameMsg.curDataStream->NAMEDvolume,
((HardLinkBeast_s *)aStack->nameMsg.curDataStream)->HARDLprimaryZid,
XLATCHED, TRUE);
if(primaryBeast == NULL)
{
zASSERT("Primary beast has dissappeared" == NULL);
ForceSetErrno(genMsg, zERR_INTERNAL_DIRECTORY_ERROR);
UNX_LATCH(&curDir->FILEbeastLatch);
break;
}
}
COMN_LATCH_NAMEMSG_BEASTS(&aStack->nameMsg,XLATCHED);
xaction = COMN_BeginXLocal(curDir);
if (BST_delete(genMsg, curDir, aStack->nameMsg.curDataStream, primaryBeast,
NULL, /*neighborBeast only applies to hardlinks */xaction, TRUE, TRUE) != zOK)
{
COMN_EndXLocal(curDir,&xaction);
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
UNX_LATCH(&curDir->FILEbeastLatch);
/* No need to unlatch/release the nameMsg beasts. The
* cleanup will do that */
break;
}
COMN_EndXLocal(curDir,&xaction);
/* Release extra pointer to the deleted beast. BST_delete already
* released curDataStream and unlatched it */
if(primaryBeast)
{
COMN_UnlatchAndRelease(&primaryBeast, XLATCHED);
}
COMN_UnlatchAndRelease(&aStack->nameMsg.curDataStream, XLATCHED);
COMN_Release(&aStack->nameMsg.curFile);
UNX_LATCH(&curDir->FILEbeastLatch);
aStack->nameMsg.latchType = NOTLATCHED;
if (vol->v_statusFlag & VOL_SF_LEAVING_ACTIVE_STATE_CLEANUP)
{
/* The volume is being deactivated. Abort the rest of this
* purge. It can be done later. */
break;
}
/* We used to do a PERIODIC_YIELD here, but it didn't yield often
* enough. We changed this to yield more often here.
*/
Yield();
}
if (GetErrno(genMsg) == zERR_NAME_NOT_FOUND_IN_DIRECTORY)
ClearErrno(genMsg);
/*---------------------------------------------------------------------------
* Close out the wildcard search map process
*---------------------------------------------------------------------------*/
COMN_WildClose(genMsg,&searchMsg);
if (GetErrno(genMsg) != zOK)
goto cleanup_return;
/*---------------------------------------------------------------------------
* There is no need to delete the original directory, because it should have
* disappeared automatically when it's last subbeast was deleted.
*---------------------------------------------------------------------------*/
/*===========================================================================*/
cleanup_return:
/* Remove the Cleanup wait count so the volume can be deactivated if
* necessary. NOTE -- the COMN_CleanupNameMsg call removes the other
* active lock on this same volume pointer. */
COMN_UnlockVolumeActive(vol, FALSE);
cleanup_return_noUnlock:
genMsg->flags &= ~DO_NOT_SEND_FSHOOKS;
COMN_CleanupNameMsg(genMsg, &aStack->nameMsg);
if (curDir)
{
COMN_Release(&curDir);
}
if (skipDelDir)
{
/* DO NOT let this bit remain set when we exit .... */
genMsg->flags &= ~ALLOW_BST_STATE_PURGING;
}
/* Allow other auto purges to run now */
UNX_LATCH(&PurgeDirLatch);
if (GetErrno(genMsg) == zOK)
{
STACK_FREE();
return(zOK);
}
else
{
STACK_FREE();
return(zFAILURE);
}
}
/****************************************************************************
* Common internal interface to Flush a file
*****************************************************************************/
STATUS COMN_Flush(
GeneralMsg_s *genMsg,
FileHandleIDP_s *fileHandleIDP)
{
FileHandle_s *fileHandle;
RootBeast_s *beast;
ASSERT_MPKNSS_LOCK();
COMN_ENABLE();
ENTER(TCOMMON, COMN_Flush);
/*
* Flush should not require reserving any resources.
*/
if ((fileHandle = COMN_RESOLVE_FILEHANDLE(genMsg,fileHandleIDP)) == NULL)
{
return zFAILURE;
}
if ((beast = fileHandle->compBeast) == NULL)
{
beast = &fileHandle->dataStream->NAMEDroot;
}
RTN_STATUS(BST_flush(beast));
}
/**************************************************************************
* Get information about the given file by NAME
***************************************************************************/
STATUS COMN_GetInfo(
GeneralMsg_s *genMsg,
NamingMsg_s *nameMsg,
GetInfoMsg_s *infoMsg)
{
STATUS status;
ASSERT_MPKNSS_LOCK();
COMN_ENABLE();
ENTER(TCOMMON, COMN_GetInfo);
RESERVE_RSRC(NAME_RESERVE);
if (COMN_Lookup(genMsg, nameMsg) != zOK)
{
RELEASE_RSRC(NAME_RESERVE);
RTN_STATUS(zFAILURE);
}
ASSERT_LATCH(&nameMsg->curFile->FILEbeastLatch);
/* If not a real datastream, make sure the namespace is valid for the volume */
if ((nameMsg->curDataStream == (NamedBeast_s *)nameMsg->curFile) &&
(!(nameMsg->curvol->VOLnameSpaceMask & (1<<infoMsg->nameSpaceID))))
{
SetErrno(genMsg,zERR_INVALID_NAMESPACE_ID);
status = zFAILURE;
goto justReturn;
}
/*---------------------------------------------------------------------------
* Fill in the file IDs before calling the comnOps function. These IDs
* are actually parameters to the lower level functions.
*---------------------------------------------------------------------------*/
if (infoMsg->ret_getInfo)
{
zASSERT(nameMsg->curFile);
zASSERT(nameMsg->curDataStream);
infoMsg->ret_getInfo->std.volumeID = nameMsg->curvol->VOLvolumeID;
/* if we did a lookup via a hardlink, the hardlink zid must be returned */
if(nameMsg->hlFile)
{
/* we don't put a use count on hlFile because the need only lives long enough to be
* used by BST_getInfo, and the beast is under usecount from the nameMsg for the
* duration of the call */
infoMsg->ret_getInfo->std.parentZid = nameMsg->hlFile->HARDLParentZid;
infoMsg->hlFile = nameMsg->hlFile;
infoMsg->ret_getInfo->std.zid = nameMsg->hlFile->HARDLzid;
if ((NamedBeast_s *)nameMsg->curFile == nameMsg->curDataStream)
{
/* Not a real data stream ... */
infoMsg->ret_getInfo->std.dataStreamZid = nameMsg->hlFile->HARDLzid;
}
else
{
/* Real data streams still point to the actual data stream ZID */
infoMsg->ret_getInfo->std.dataStreamZid = nameMsg->curDataStream->NAMEDzid;
}
if (infoMsg->ret_getInfo->infoVersion >= zINFO_VERSION_D)
{
((zInfoD_s *)infoMsg->ret_getInfo)->hardlinkPrimaryZid =
nameMsg->hlFile->HARDLprimaryZid;
}
}
else
{
infoMsg->ret_getInfo->std.parentZid = nameMsg->fileParentZid;
infoMsg->ret_getInfo->std.zid = nameMsg->curFile->FILEzid;
infoMsg->ret_getInfo->std.dataStreamZid = nameMsg->curDataStream->NAMEDzid;
if (infoMsg->ret_getInfo->infoVersion >= zINFO_VERSION_D)
{
((zInfoD_s *)infoMsg->ret_getInfo)->hardlinkPrimaryZid = zINVALID_ZID;
}
}
}
status = nameMsg->curDataStream->NAMEDcomnOps.BST_getInfo(genMsg,
&nameMsg->curDataStream->NAMEDroot,infoMsg);
justReturn:
RELEASE_RSRC(NAME_RESERVE);
RTN_STATUS(status);
}
/****************************************************************************
* Get information about the given dataStreamBeast by pointer
*****************************************************************************/
STATUS COMN_GetInfoByBeastPtr(
GeneralMsg_s *genMsg,
Zid_t parentZid,
// cnt NINT fileNameUniquifier,
void *voidFileBeast,
void *voidDataStreamBeast,
GetInfoMsg_s *infoMsg)
{
File_s *fileBeast = (File_s *)voidFileBeast;
NamedBeast_s *dataStreamBeast = (NamedBeast_s *)voidDataStreamBeast;
STATUS status = zFAILURE;
ASSERT_MPKNSS_LOCK();
COMN_ENABLE();
ENTER(TCOMMON, COMN_GetInfoByBeastPtr);
RESERVE_RSRC(NAME_RESERVE);
// cnt zASSERT(COMN_IsDerivedFrom(fileBeast,zFTYPE_FILE));
zASSERT(COMN_IsDerivedFrom(dataStreamBeast,zFTYPE_NAMED_DATA_STREAM));
if (COMN_LockVolumeActive(genMsg,fileBeast->FILEvolume,FALSE) != zOK)
{
goto just_return;
}
/* If not a real datastream, make sure the namespace is valid for the volume */
if ((dataStreamBeast == (NamedBeast_s *)fileBeast) &&
(!(dataStreamBeast->NAMEDvolume->VOLnameSpaceMask & (1<<infoMsg->nameSpaceID))))
{
SetErrno(genMsg, zERR_INVALID_NAMESPACE_ID);
goto return_unlockActive;
}
if (dataStreamBeast != (NamedBeast_s *)fileBeast)
S_LATCH(&fileBeast->FILEbeastLatch);
S_LATCH(&dataStreamBeast->NAMEDbeastLatch);
/*---------------------------------------------------------------------------
* Fill in the file IDs before calling the comnOps function. These IDs
* are actually parameters to the lower level functions.
*---------------------------------------------------------------------------*/
if (infoMsg->ret_getInfo)
{
if (parentZid == zINVALID_ZID)
{
parentZid = fileBeast->FILEfirstParentZid;
}
/* cnt if (fileNameUniquifier == zFNU_FIRST_PARENT)
{
fileNameUniquifier = fileBeast->FILEfirstParentNameUniquifier;
} */
// cnt infoMsg->nameUniquifier = fileNameUniquifier;
infoMsg->ret_getInfo->std.parentZid = parentZid;
infoMsg->ret_getInfo->std.volumeID = fileBeast->FILEvolume->VOLvolumeID;
if(infoMsg->hlFile)
{
infoMsg->ret_getInfo->std.zid = infoMsg->hlFile->HARDLzid;
infoMsg->ret_getInfo->std.dataStreamZid = infoMsg->hlFile->HARDLzid;
if (infoMsg->ret_getInfo->infoVersion >= zINFO_VERSION_D)
{
((zInfoD_s *)infoMsg->ret_getInfo)->hardlinkPrimaryZid =
infoMsg->hlFile->HARDLprimaryZid;
}
}
else
{
infoMsg->ret_getInfo->std.zid = fileBeast->FILEzid;
infoMsg->ret_getInfo->std.dataStreamZid = dataStreamBeast->NAMEDzid;
if (infoMsg->ret_getInfo->infoVersion >= zINFO_VERSION_D)
{
((zInfoD_s *)infoMsg->ret_getInfo)->hardlinkPrimaryZid =
zINVALID_ZID;
}
}
}
status = dataStreamBeast->NAMEDcomnOps.BST_getInfo(genMsg,&dataStreamBeast->NAMEDroot,infoMsg);
if (dataStreamBeast != (NamedBeast_s *)fileBeast)
UNS_LATCH(&fileBeast->FILEbeastLatch);
UNS_LATCH(&dataStreamBeast->NAMEDbeastLatch);
return_unlockActive:
COMN_UnlockVolumeActive(fileBeast->FILEvolume,FALSE);
just_return:
RELEASE_RSRC(NAME_RESERVE);
RTN_STATUS(status);
}
/**************************************************************************
* This routine links file objects.
* No wildcarding is allowed.
* The name associated with destNameMsg is set to link to the beast
* identified by srcNameMsg.
***************************************************************************/
STATUS COMN_Link(
GeneralMsg_s *genMsg,
NamingMsg_s *srcNameMsg,
NamingMsg_s *destNameMsg,
LinkMsg_s *linkMsg)
{
STATUS status = zOK;
Xaction_s *xaction;
LONG id;
NINT srcLatchType, destLatchType;
HardLinkBeast_s *hlBeast;
File_s *primaryDir = NULL;
BOOL primaryDirIsLatched = FALSE;
Time_t currentTime;
typedef struct Stack_s {
struct EventBlock evBlk;
EventLinkEnter_s enter;
EventLinkExit_s exit;
LSAInvalidateDentry_s lsaInv;
FCNTL_SetHardLinkFlags_s fcntlSetHLFlags;
} Stack_s;
STACK_ALLOC();
ENTER(TCOMMON, COMN_Link);
ASSERT_MPKNSS_LOCK();
/*---------------------------------------------------------------------------
* Require that the latchTypes come in == XLATCHED, but change them temporarily
* to NOTLATCHED. We will do our own XLATCHING later.
* We must do this because it is possible that the destNameMsg points to the
* same directory as the srcNameMsg, and we can't latch the directory twice.
*---------------------------------------------------------------------------*/
zASSERT(destNameMsg->latchType == XLATCHED);
zASSERT(srcNameMsg->latchType == XLATCHED);
if (destNameMsg->curFile != NULL)
UNX_LATCH(&destNameMsg->curFile->FILEbeastLatch);
destNameMsg->latchType = NOTLATCHED;
if (srcNameMsg->curFile != NULL)
UNX_LATCH(&srcNameMsg->curFile->FILEbeastLatch);
srcNameMsg->latchType = NOTLATCHED;
/*---------------------------------------------------------------------------
* Only implement hardLinks for now...
*---------------------------------------------------------------------------*/
if (!(linkMsg->linkFlags & zLF_HARD_LINK))
{
SetErrno(genMsg,zERR_INVALID_LINK_TYPE);
goto error_cleanupAndReturn;
}
/*---------------------------------------------------------------------------
* We are doing a hardLink. Fully resolve the srcNameMsg. It must resolve to
* a standard non-container beast that is derived from file.
*---------------------------------------------------------------------------*/
zASSERT(srcNameMsg->parseMode == NAMPMODE_Undefined);
srcNameMsg->parseMode = NAMPMODE_FullyResolveAny;
if (COMN_Lookup(genMsg,srcNameMsg) != zOK)
{
goto error_cleanupAndReturn;
}
if(srcNameMsg->curFile->FILEbeastVersion <= BEAST_VERSION_2)
{
SetErrno(genMsg, zERR_MUST_UPGRADE_TO_LINK);
goto error_cleanupAndReturn;
}
if (srcNameMsg->curvol->VOLenabledAttributes & zATTR_READONLY)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto error_cleanupAndReturn;
}
if ((srcNameMsg->curvol->VOLenabledAttributes & zATTR_HARD_LINKS) == 0)
{
SetErrno(genMsg,zERR_HARD_LINKS_NOT_ENABLED);
goto error_cleanupAndReturn;
}
if (srcNameMsg->curDataStream != (NamedBeast_s *)srcNameMsg->curFile)
{
SetErrno(genMsg,zERR_CANT_HARD_LINK_DATA_STREAMS);
goto error_cleanupAndReturn;
}
if ( !(COMN_IsDerivedFrom(srcNameMsg->curFile,zFTYPE_FILE)) )
{
SetErrno(genMsg,zERR_CANT_HARD_LINK_TO_NON_FILE);
goto error_cleanupAndReturn;
}
if (srcNameMsg->curFile->FILEattributes & zFA_SUBDIRECTORY)
{
SetErrno(genMsg,zERR_CANT_HARD_LINK_TO_DIRECTORY);
goto error_cleanupAndReturn;
}
if(srcNameMsg->curFile->FILEnumParents == MAX_LINKS)
{
SetErrno(genMsg, zERR_TOO_MANY_HARD_LINKS);
goto error_cleanupAndReturn;
}
if ( srcNameMsg->curFile->FILEmayIDoThis(genMsg, srcNameMsg->curFile,
srcNameMsg->hlFile ? srcNameMsg->hlParentZid : srcNameMsg->fileParentZid,
MAY_I_READ_DATA) != zOK)
{
/*
* We know there is an existing file, but if the user is not
* authorized to read it, so return an error.
*/
SetErrno(genMsg, zERR_ACCESS_DENIED);
goto error_cleanupAndReturn;
}
/*---------------------------------------------------------------------------
* Now, resolve the destNameMsg to the directory, and make sure it is
* a directory, derived from File, and that the leaf name does not already
* exist in that directory.
*---------------------------------------------------------------------------*/
zASSERT(destNameMsg->parseMode == NAMPMODE_Undefined);
destNameMsg->parseMode = NAMPMODE_DoNotResolveLeafName;
if (COMN_Lookup(genMsg,destNameMsg) != zOK)
{
goto error_cleanupAndReturn;
}
if (!(destNameMsg->retParseFlags & NAMRETPFL_haveAParsedComponent))
{
SetErrno(genMsg,zERR_LINK_DEST_FILE_ALREADY_EXISTS);
goto error_cleanupAndReturn;
}
if (!(destNameMsg->retParseFlags & NAMRETPFL_lastComponent))
{
SetErrno(genMsg,zERR_INVALID_PATH);
goto error_cleanupAndReturn;
}
zASSERT(destNameMsg->scanMsg.retScanFlags & NSRETSFL_componentIsUnicode);
zASSERT(destNameMsg->curDataStream == (NamedBeast_s *)destNameMsg->curFile);
if ((!(COMN_IsDerivedFrom(destNameMsg->curFile,zFTYPE_FILE))) ||
((destNameMsg->curFile->FILEattributes & zFA_SUBDIRECTORY)==0))
{
SetErrno(genMsg,zERR_MUST_HARD_LINK_FROM_DIRECTORY);
goto error_cleanupAndReturn;
}
/*---------------------------------------------------------------------------
* Attempt to fully resolve the destNameMsg to see if it already exists
*---------------------------------------------------------------------------*/
destNameMsg->parseMode = NAMPMODE_FullyResolveAny;
if (COMN_Lookup(genMsg,destNameMsg) == zOK)
{
SetErrno(genMsg,zERR_LINK_DEST_FILE_ALREADY_EXISTS);
goto error_cleanupAndReturn;
}
if (GetErrno(genMsg) != zERR_NAME_NOT_FOUND_IN_DIRECTORY)
{
goto error_cleanupAndReturn;
}
/* The target file does not exist yet...curFile is still pointing to the
* directory. We will link the src file to the directory identified by
* destNameMsg->curFile, with the new name identified by destNameMsg->
* curComponent */
ClearErrno(genMsg);
zASSERT(destNameMsg->curFile != NULL);
zASSERT(destNameMsg->scanMsg.retUnicodeComp != NULL);
zASSERT(destNameMsg->scanMsg.retScanFlags & NSRETSFL_componentIsUnicode);
zASSERT(destNameMsg->retParseFlags & NAMRETPFL_haveAParsedComponent);
zASSERT(destNameMsg->retParseFlags & NAMRETPFL_lastComponent);
zASSERT(destNameMsg->curFile->FILEattributes & zFA_SUBDIRECTORY);
/*---------------------------------------------------------------------------
* When we GET here we have the following state:
* srcNameMsg->curFile - points to the primary beast, to which a
* link is to be created.
* srcNameMsg->hlFile - if non-null, points to the HL beast through
* which the primary beast was found
* destNameMsg->curFile - the target link directory
* destNameMsg->curComponent - points to the new link name to be created
*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
* First Generate an Event
*---------------------------------------------------------------------------*/
INIT_EVENT_ID_STATUS(id, aStack->exit.enterRetStatus);
if (NEBEventInfo[EVENT_Link_Enter].consumers)
{
INIT_LINK_ENTER_EVENT(genMsg, srcNameMsg, destNameMsg, aStack->evBlk,
EVENT_Link_Enter, aStack->enter, id,
destNameMsg->scanMsg.retUnicodeComp, linkMsg);
ZOS_ProduceEvent(status,&aStack->evBlk);
CHECK_ENTER_RETURN(genMsg, status, aStack->evBlk, aStack->exit,
error_cleanupExit);
}
/*---------------------------------------------------------------------------
* Now XLatch the necessary beasts. Do the destNameMsg first, because it
* points to the new target directory. (We have validated this previously)
* Then do the srcNameMsg last, because we know it points to a primary beast.
* By definition the file is either below the directory, or it is in a
* different part of the tree, so we wont encounter a deadlock.
*---------------------------------------------------------------------------*/
COMN_LATCH_NAMEMSG_BEASTS(destNameMsg,XLATCHED);
if(srcNameMsg->curFile->FILEhardLinkZid == zINVALID_ZID) // if no initial shadow beast, we have to create one
{
if(srcNameMsg->fileParentZid == destNameMsg->curFile->FILEzid)
{
primaryDir = destNameMsg->curFile;
COMN_USE_BEAST(&primaryDir->FILEroot); /* Get a usecount on the primary directory beast */
}
else if ((primaryDir = COMN_LookupByZid(genMsg, srcNameMsg->curvol,
srcNameMsg->fileParentZid, XLATCHED, FALSE)) != NULL)
{
primaryDirIsLatched = TRUE;
}
else
{
zASSERT("could not file directory parent for primar file during COMN_Hardlink. What now?"==0);
status = zFAILURE;
goto sendExitEvent;
}
}
COMN_LATCH_NAMEMSG_BEASTS(srcNameMsg,XLATCHED);
/*---------------------------------------------------------------------------
* Now, get a transaction and add the new name to the dest dir and the beast
*---------------------------------------------------------------------------*/
xaction = COMN_BeginXLocal(destNameMsg->curFile);
/*---------------------------------------------------------------------------
* Check to see if this is the initial link creation. If so, pull out the
* primary file info.
*---------------------------------------------------------------------------*/
if(srcNameMsg->curFile->FILEhardLinkZid == zINVALID_ZID)
{
ParentEntry_s *pentry;
NINT removeNameFlags;
NINT nsFlag;
LinkedNameEntry_s *nentry;
zASSERT(srcNameMsg->hlFile == NULL);
srcNameMsg->hlFile = HL_doCreateHardLinkBeast( genMsg,(NamedBeast_s*)srcNameMsg->curFile, NULL,
&srcNameMsg->curFile->FILEfirstParent, xaction);
if(srcNameMsg->hlFile == NULL)
{
zASSERT("HL_doCreateHardLinkBeast did not work. Now what?"==NULL);
status = zFAILURE;
goto error_cleanupTransaction;
}
srcNameMsg->hlFile->HARDLhlFlags = zHL_FIRST_HLNAME;
/* Fill in srcNameMsg so it will be unlatch/released later */
zASSERT(srcNameMsg->hlParentZid == zINVALID_ZID);
srcNameMsg->hlParentZid = srcNameMsg->curFile->FILEfirstParentZid;
pentry = &srcNameMsg->hlFile->HARDLfirstParent; // the names were moved from the inode firstParentEntry to the hlFile at hlBeast creation time
removeNameFlags = NF_FIRST_NAME;
DQ_FOREACH(&pentry->names, nentry, LinkedNameEntry_s, nlink)
{
if(DQ_ISHEADNEXT(&pentry->names, nentry, LinkedNameEntry_s, nlink))
removeNameFlags |= NF_LAST_NAME;
nsFlag = nentry->p.flagsAndLen & ~NAMEnameLenMask;
NAME_RemoveNameFromCache(genMsg, srcNameMsg->curFile->FILEvolume,
pentry->p.zid, nentry->p.nameSpaceMask, pentry->p.nameType,
nentry->p.name, nsFlag);
if(srcNameMsg->curFile->FILEcomnOps.BST_removeNameFromDirectory(
genMsg, srcNameMsg->curFile->FILEzid,
(NamedBeast_s*)primaryDir, nentry->p.nameSpaceMask,
pentry->p.nameType, nentry->p.name, removeNameFlags,
xaction, pentry->t) != zOK)
{
zASSERT("Error removing name from directory in HardLInk upgrade. Now what?" == NULL);
COMN_AbortXLocal(genMsg,srcNameMsg->curvol,xaction, WHERE);
}
if (srcNameMsg->curFile->FILEcomnOps.BST_addNameToDirectory(genMsg,
(NamedBeast_s*)srcNameMsg->hlFile,
(NamedBeast_s*)primaryDir, nentry->p.nameSpaceMask,
pentry->p.nameType, nentry->p.name,
nsFlag, removeNameFlags,
MA_ATTR_SET_MASK(srcNameMsg->curFile->FILEattributes),
xaction, pentry->t) != zOK)
{
zASSERT("Error adding name to directory in HardLInk upgrade. Now what?" == NULL);
COMN_AbortXLocal(genMsg,srcNameMsg->curvol,xaction, WHERE);
}
aStack->fcntlSetHLFlags.beast = (NamedBeast_s *)srcNameMsg->hlFile;
aStack->fcntlSetHLFlags.directory = (NamedBeast_s *)primaryDir;
aStack->fcntlSetHLFlags.nameSpaceMask = nentry->p.nameSpaceMask;
aStack->fcntlSetHLFlags.nameType = pentry->p.nameType;
aStack->fcntlSetHLFlags.name = nentry->p.name;
aStack->fcntlSetHLFlags.newHardLinkFlags = (HL_FLAG_IS_VALID | HL_FLAG_IS_HARD_LINK);
if (srcNameMsg->curvol->VOLcomnVolOps.VOL_FCNTL(genMsg,
srcNameMsg->curvol,
VOL_FCNTL_SET_HARDLINK_NAMETREE_FLAGS,
(FCNTL_In_s *)&aStack->fcntlSetHLFlags,
NULL,
xaction) != zOK)
{
zASSERT("Error setting HLFlags in HardLInk upgrade. Now what?" == NULL);
COMN_AbortXLocal(genMsg,srcNameMsg->curvol,xaction, WHERE);
}
}
srcNameMsg->curFile->FILEfirstParentZid = srcNameMsg->hlFile->HARDLfirstParentZid;
/* force the conversion to disk */
COMN_MARK_BEAST_XLOCAL((RootBeast_s*)primaryDir, xaction);
COMN_ForceBeastWrite(genMsg, primaryDir, xaction);
COMN_MARK_BEAST_XLOCAL((RootBeast_s*)srcNameMsg->curFile, xaction);
COMN_ForceBeastWrite(genMsg, srcNameMsg->curFile, xaction);
COMN_MARK_BEAST_XLOCAL((RootBeast_s*)srcNameMsg->hlFile, xaction);
COMN_ForceBeastWrite(genMsg, srcNameMsg->hlFile, xaction);
}
if((hlBeast = HL_doCreateHardLinkBeast(genMsg,(NamedBeast_s*)srcNameMsg->curFile,
(NamedBeast_s*)srcNameMsg->hlFile, NULL, xaction)) == NULL)
{
zASSERT("Could not HL_doCreateHardLinkBeast in COMN_Link. Now what?"==NULL);
status = zFAILURE;
goto error_cleanupTransaction;
}
/* now inserting the new name(s) into the new hardlink beast */
zASSERT(destNameMsg->workNameType == zNTYPE_FILE);
if (NAME_InsertAndMangleName(genMsg, hlBeast,srcNameMsg->curFile,
destNameMsg->curFile, destNameMsg->workNameSpaceID,
destNameMsg->workNameType, destNameMsg->scanMsg.retUnicodeComp,
INSNAMEFL_NEW_PARENT_AUTH, xaction, NULL, NULL) != zOK)
{
/* We failed to add new names. If any names were partially
* added we would have called AbendTheVolume inside of this
* function. It is safe to assume here that failed names were backed
* out so we can simply abort the link attempt with no damange done.
*/
HL_doDestroyHardLinkBeast(genMsg, hlBeast, srcNameMsg->curFile, xaction);
goto error_cleanupTransaction;
}
/* Defect #134243 - Set the Modified time */
currentTime = GetUTCTime();
destNameMsg->curFile->FILEmodifiedTime = currentTime;
destNameMsg->curFile->FILEmodifiedDOSTime = INVALID_DOS_TIME;
//FixFixFix - We need a modifier ID passed in by the caller
// destNameMsg->curFile->FILEmodifierID = linkMsg->ownerID;
destNameMsg->curFile->FILEmetaDataSeqNum++;
destNameMsg->curFile->FILEvolume->VOLfile.FILEmodifiedTime = currentTime;
COMN_MARK_BEAST_MESSY(&destNameMsg->curFile->FILEroot);
COMN_MARK_BEAST_XLOCAL((RootBeast_s*)hlBeast, xaction);
COMN_MARK_BEAST_XLOCAL((RootBeast_s*)destNameMsg->curFile, xaction);
COMN_MARK_BEAST_XLOCAL((RootBeast_s*)srcNameMsg->curFile, xaction);
COMN_MARK_BEAST_XLOCAL((RootBeast_s*)srcNameMsg->hlFile, xaction);
COMN_ForceBeastWrite(genMsg, hlBeast, xaction);
COMN_ForceBeastWrite(genMsg,destNameMsg->curFile,xaction);
COMN_ForceBeastWrite(genMsg,srcNameMsg->curFile,xaction);
COMN_ForceBeastWrite(genMsg,srcNameMsg->hlFile,xaction);
COMN_EndXLocal(destNameMsg->curFile,&xaction);
if (!(linkMsg->linkFlags & zLF_KEEP_VFS_CACHE))
{
if ( COMN_LsaCleanupSetup( &hlBeast->file.auth.named,
//cnt linkMsg->retNameUniquifier,
&aStack->lsaInv) == zOK)
{
aStack->lsaInv.lid_zid = zINVALID_ZID;
aStack->lsaInv.lid_pZid = destNameMsg->curFile->FILEzid;
if (Ptr_lsa_invalidate_dentry)
{
COMN_UNLATCH_NAMEMSG_BEASTS(srcNameMsg, &srcLatchType);
COMN_UNLATCH_NAMEMSG_BEASTS(destNameMsg, &destLatchType);
Ptr_lsa_invalidate_dentry(&aStack->lsaInv);
}
}
}
COMN_UnlatchAndRelease(&hlBeast, XLATCHED);
status = zOK;
sendExitEvent:
if (NEBEventInfo[EVENT_Link_Exit].consumers)
{
COMN_UNLATCH_NAMEMSG_BEASTS(srcNameMsg, &srcLatchType);
COMN_UNLATCH_NAMEMSG_BEASTS(destNameMsg, &destLatchType);
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk, EVENT_Link_Exit, aStack->exit, id);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
if(primaryDirIsLatched)
{
UNX_LATCH(&primaryDir->FILEbeastLatch);
}
if(primaryDir)
{
COMN_Release(&primaryDir);
}
STACK_FREE();
RTN_STATUS(status);
/*=========================================================================*/
error_cleanupTransaction:
COMN_EndXLocal(destNameMsg->curFile,&xaction);
status = zFAILURE;
goto sendExitEvent;
/*** Already released resources.. This is an error path from events **/
error_cleanupAndReturn:
error_cleanupExit:
STACK_FREE();
RTN_STATUS(zFAILURE);
}
/**************************************************************************
* Modify file information for the given file by name
***************************************************************************/
STATUS COMN_ModifyInfo(
GeneralMsg_s *genMsg,
NamingMsg_s *nameMsg,
ModifyInfoMsg_s *infoMsg)
{
struct ModifyStructure modifyVector;
STATUS status;
NINT id;
NINT latchType;
BOOL modifyMatchAttr = FALSE;
Xaction_s *xaction;
File_s *curDir = NULL;
NINT oldFileAttributes = 0;
NINT hlInfoMask;
NINT inodeInfoMask;
typedef struct Stack_s {
SearchMsg_s srchMsg;
FSHooks_ModifyInfo_s parms;
struct EventBlock evBlk;
EventModifyInfoEnter_s enter;
EventModifyInfoExit_s exit;
} Stack_s;
STACK_ALLOC();
ASSERT_MPKNSS_LOCK();
COMN_ENABLE();
ENTER(TCOMMON, COMN_ModifyInfo);
RESERVE_RSRC(NAME_RESERVE);
/* Defect #306666. We can't allow unlicensed connection access */
if (!ConnectionIsLoggedIn(genMsg->pssConn.id))
{
/* They are not logged in (LICENCED), so limit their access */
SetErrno(genMsg, zERR_NO_SET_PRIVILEGE);
goto cleanup_error;
}
/*---------------------------------------------------------------------------
* Resolve up to, but not including the leaf name
*---------------------------------------------------------------------------*/
if (nameMsg->parseMode == NAMPMODE_Undefined)
COMN_SET_NAMING_MSG_PARSEMODE(nameMsg, NAMPMODE_DoNotResolveLeafName);
zASSERT(nameMsg->latchType == XLATCHED);
if (COMN_Lookup(genMsg, nameMsg) != zOK)
{
goto cleanup_error;
}
ASSERT_XLATCH(&nameMsg->curFile->FILEbeastLatch);
/* Check if the volume is READ-ONLY */
if (nameMsg->curvol->VOLenabledAttributes & zATTR_READONLY)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_error;
}
/* If the file is in the salvage system, then don't allow modifications */
if (nameMsg->retParseFlags & NAMRETPFL_hasDeletedNameType)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_error;
}
if ((infoMsg->modifyInfoMask & zMOD_FILE_ATTRIBUTES) &&
(infoMsg->modifyInfo->std.fileAttributesModMask & MA_VALID_MASK))
{
/* One or more of the zFA_HIDDEN, zFA_SYSTEM, or zFA_SUBDIRECTORY bit
* is being changed in the file attributes. This requires a
* corresponding update to the matchAttributes that are stored in
* the directory (nametree). These two updates must not get out of
* sync or we have problems. Because of this, we create a transaction
* and tie the two updates together.
*/
modifyMatchAttr = TRUE;
}
if (!COMN_IgnoreDoNotModifyMask)
{
infoMsg->modifyInfo->std.fileAttributesModMask &= ~DO_NOT_MODIFY_MASK;
}
if (!(nameMsg->scanMsg.retScanFlags & NSRETSFL_hasWildcardChars))
{
/*---------------------------------------------------------------------------
* There were no wildcard characters, Modify the file directly by name
*---------------------------------------------------------------------------*/
COMN_SET_NAMING_MSG_PARSEMODE(nameMsg, NAMPMODE_FullyResolveAny);
if (COMN_Lookup(genMsg, nameMsg) != zOK)
{
goto cleanup_error;
}
infoMsg->parentZid =
nameMsg->hlFile ? nameMsg->hlParentZid : nameMsg->fileParentZid;
/* cnt if (nameMsg->curDataStream == (NamedBeast_s *)nameMsg->curFile)
{
infoMsg->nameUniquifier = nameMsg->fileNameUniquifier;
}
else
{
infoMsg->nameUniquifier = nameMsg->curDataStream->NAMEDfirstParentNameUniquifier;
}
*/
INIT_EVENT_ID_STATUS(id, aStack->exit.enterRetStatus);
if (FSHOOKS_OR_EVENTS(ModifyDirectoryEntry_Hook,EVENT_ModifyInfo_Enter))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
RELEASE_RSRC(NAME_RESERVE);
CHECK_FSHOOKS_MODIFY_ENTER(ModifyDirectoryEntry_Hook, &aStack->parms,
status, justReturnExit, genMsg, nameMsg, infoMsg, &modifyVector);
if (NEBEventInfo[EVENT_ModifyInfo_Enter].consumers)
{
INIT_ENTER_EVENT(genMsg, nameMsg, aStack->evBlk,
EVENT_ModifyInfo_Enter, aStack->enter, id);
INIT_MODIFY_ENTER_EVENT(infoMsg, aStack->enter);
ZOS_ProduceEvent(status,&aStack->evBlk);
CHECK_ENTER_RETURN(genMsg, status, aStack->evBlk, aStack->exit, justReturnExit);
}
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
/* If we are modifying match Attributes, and this is a file beast, and
* the beast is not rootdir, then attempt to latch the parent directory
* and start a transaction */
if ((modifyMatchAttr) &&
(nameMsg->curDataStream == (NamedBeast_s *)nameMsg->curFile) &&
(nameMsg->curFile != NULL) && (nameMsg->curFile->FILEzid != zROOTDIR_ZID))
{
oldFileAttributes = nameMsg->curFile->FILEattributes;
/* Get a pointer to the curent directory for use in the xaction */
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
if ((curDir = COMN_LookupByZid(genMsg, nameMsg->curvol,
nameMsg->fileParentZid, XLATCHED, TRUE)) == NULL)
{
status = zFAILURE;
xaction = NULL;
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
else
{
status = zOK;
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
xaction = COMN_BeginXLocal(curDir);
}
}
else
{
modifyMatchAttr = FALSE; /* In case beast is rootdir, ExtAttr or DataStream */
status = zOK;
xaction = NULL;
}
/* Check if the file is exclusive locked */
if ((status == zOK) &&
(!(infoMsg->modifyFlags & MF_ALLOW_MODIFY_WITH_DENY_READ_WRITE)) &&
((nameMsg->curDataStream->NAMEDdenyReaderCount != 0) ||
(nameMsg->curDataStream->NAMEDdenyWriterCount != 0)))
{
if (COMN_IsDenyReaderWriterNotMe(genMsg,nameMsg->curDataStream))
{
SetErrno(genMsg,zERR_ALL_FILES_IN_USE);
status = zFAILURE;
}
}
/* Modify the info in the beast */
if (status == zOK)
{
/*---------------------------------------------------------------------------
* Check for presence of BOTH zFA_COMPRESS_FILE_IMMEDIATELY and
* zFA_DO_NOT_COMPRESS_FILE. If both are here, clear the
* zFA_COMPRESS_FILE_IMMEDIATELY bit and continue on. This functionality
* matches what occurs in the legacy file system.
*---------------------------------------------------------------------------*/
if((infoMsg->modifyInfo->std.fileAttributes & zFA_COMPRESS_FILE_IMMEDIATELY) &&
(infoMsg->modifyInfo->std.fileAttributes & zFA_DO_NOT_COMPRESS_FILE))
{
infoMsg->modifyInfo->std.fileAttributes &= ~zFA_COMPRESS_FILE_IMMEDIATELY;
}
/* 1/28/05 - This used to call BST_modifyInfo with curDataStream
* which would get an error if the beast really was a datastream.
* You cannot modify info on a datastream anyway. Today, I'm
* merging MSG_ModifyInfo back into COMN_ModifyInfo, and the only
* functional difference was that MSG_ModifyInfo modified file info
* even if it was a data stream that was open. This was changed to
* use curFile so we will modify file info if we are pointing at the
* datastream ... same functionality as current zAPI, and will
* not break existing apps ... just allow them to do more.
*/
/* If this is a hardlink name, and they are modifying
* zMOD_PRIMARY_NAMESPACE or zMOD_DELETED_INFO, these both
* need to be modified on the hard link beast itself. All other
* modification need to be made to the primary "iNode" beast.
* The only way to do this without changing the LSS interface
* is to call BST_modifyInfo twice - Once for the hard link and
* once for the rest.
*/
if ((nameMsg->hlFile) &&
(infoMsg->modifyInfoMask & (zMOD_PRIMARY_NAMESPACE|zMOD_DELETED_INFO)))
{
hlInfoMask = infoMsg->modifyInfoMask &
(zMOD_PRIMARY_NAMESPACE|zMOD_DELETED_INFO);
inodeInfoMask = infoMsg->modifyInfoMask &
~(zMOD_PRIMARY_NAMESPACE|zMOD_DELETED_INFO);
/* Modify some info using the hard link beast */
infoMsg->modifyInfoMask = hlInfoMask;
status = (nameMsg->curFile->FILEcomnOps.BST_modifyInfo(
genMsg,&nameMsg->hlFile->HARDLroot,infoMsg,xaction));
if ((status == zOK) && (inodeInfoMask))
{
/* Modify the rest of the info using the inode beast */
infoMsg->modifyInfoMask = inodeInfoMask;
status = (nameMsg->curFile->FILEcomnOps.BST_modifyInfo(
genMsg,&nameMsg->curFile->FILEroot,infoMsg,xaction));
}
/* Restore the original mask */
infoMsg->modifyInfoMask = hlInfoMask | inodeInfoMask;
}
else
{
status = (nameMsg->curFile->FILEcomnOps.BST_modifyInfo(
genMsg,&nameMsg->curFile->FILEroot,infoMsg,xaction));
}
}
/* Now modify the match attributes in the directory only if we really
* changed the values of one or more matchAttribute bits*/
if (modifyMatchAttr)
{
if ((status == zOK) &&
((nameMsg->curFile->FILEattributes & MA_VALID_MASK) !=
(oldFileAttributes & MA_VALID_MASK)))
{
/* The beast was modified, it was marked XLOCAL,
* and COMN_ForceBeastWrite was called inside of BST_modifyInfo */
if (nameMsg->hlFile)
{
/* If we are a hardlink, we must do this for all other
* hard link beast (with the same primary) too.
*/
status = NAME_HardLinkSetMatchAttributesInDirectory(genMsg,
nameMsg->curFile, curDir, // cnt nameMsg->fileNameUniquifier,
nameMsg->curFile->FILEattributes,
oldFileAttributes, xaction);
}
else
{
status = NAME_SetMatchAttributesInDirectory(genMsg,
nameMsg->curFile, curDir, // cnt nameMsg->fileNameUniquifier,
nameMsg->curFile->FILEattributes,
oldFileAttributes, xaction);
}
if (status != zOK)
{
STATUS status2;
inodeInfoMask = infoMsg->modifyInfoMask;
infoMsg->modifyInfoMask = zMOD_FILE_ATTRIBUTES;
infoMsg->modifyInfo->std.fileAttributes = oldFileAttributes;
/* See 1/28/05 comment above about curDataStream vs curFile. */
status2 = (nameMsg->curFile->FILEcomnOps.BST_modifyInfo(
genMsg,&nameMsg->curFile->FILEroot,infoMsg,xaction));
infoMsg->modifyInfoMask = inodeInfoMask;
if ((status2 != zOK) &&
((nameMsg->curFile->FILEattributes & MA_VALID_MASK) !=
(oldFileAttributes & MA_VALID_MASK)))
{
/* We are in a transaction where we modified the
* attributes in the beast but not in the name tree.
* We tried to restore the attributes in the beast
* but we failed. The only way to clean this up is to
* abort the transaction. Today, this deactivates the
* volume and prevents further writes. In the future
* this will be a real transaction abort (backout). */
/* FixFixFix6 -- When we implement real transaction
* abort, we need to revisit this code */
zASSERT("Unable to restore attributes after failed SetMatchAttributesInDirectory" == NULL);
SetErrno(genMsg,zERR_NAMING_INCONSISTENCY);
COMN_AbortXLocal(genMsg,curDir->FILEvolume,xaction, WHERE);
}
}
}
}
if (xaction)
{
COMN_EndXLocal(curDir,&xaction);
}
if (curDir)
{
COMN_UnlatchAndRelease(&curDir, XLATCHED);
}
if (FSHOOKS_OR_EVENTS(ModifyDirectoryEntry_Hook,EVENT_ModifyInfo_Exit))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
RELEASE_RSRC(NAME_RESERVE);
CHECK_FSHOOKS_MODIFY_EXIT(ModifyDirectoryEntry_Hook, &aStack->parms,
genMsg, nameMsg, infoMsg, &modifyVector);
if (NEBEventInfo[EVENT_ModifyInfo_Exit].consumers)
{
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk,
EVENT_ModifyInfo_Exit, aStack->exit, id);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
goto justReturn;
}
/*---------------------------------------------------------------------------
* Wildcard was specified, are wildcards allowed
*---------------------------------------------------------------------------*/
else if (!(nameMsg->parseFlags & NAMPFL_allowWildcardChars))
{
SetErrno(genMsg,zERR_INVALID_NAME);
goto cleanup_error;
}
else
{
/*---------------------------------------------------------------------------
* Do a wildcard Modify.
*---------------------------------------------------------------------------*/
NINT modifyCnt = 0;
BOOL evOp = FALSE;
STATUS evError = zOK;
BOOL locked = FALSE;
BOOL continueAfterExitEvent = FALSE;
zASSERT(nameMsg->curFile->FILEattributes & zFA_SUBDIRECTORY);
COMN_INIT_SEARCH_MSG(&aStack->srchMsg);
COMN_SETUP_WILDOPEN_SEARCH_MSG(&aStack->srchMsg,zNTYPE_FILE);
if (COMN_WildOpen(genMsg,nameMsg,&aStack->srchMsg) != zOK)
goto cleanup_error;
for (;;)
{
genMsg->flags |= DO_NOT_SEND_FSHOOKS;
if (COMN_WildRead(genMsg,nameMsg,&aStack->srchMsg) != zOK)
{
genMsg->flags &= ~DO_NOT_SEND_FSHOOKS;
if (GetErrno(genMsg) != zERR_NAME_NOT_FOUND_IN_DIRECTORY)
goto cleanup_closedir;
ClearErrno(genMsg);
break;
}
genMsg->flags &= ~DO_NOT_SEND_FSHOOKS;
infoMsg->parentZid =
nameMsg->hlFile ? nameMsg->hlParentZid : nameMsg->fileParentZid;
INIT_EVENT_ID_STATUS(id, aStack->exit.enterRetStatus);
if (FSHOOKS_OR_EVENTS(ModifyDirectoryEntry_Hook,
EVENT_ModifyInfo_Enter))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
RELEASE_RSRC(NAME_RESERVE);
CHECK_FSHOOKS_MODIFY_ENTER(ModifyDirectoryEntry_Hook, &aStack->parms,
status, skipOperation, genMsg, nameMsg,
infoMsg, &modifyVector);
if (NEBEventInfo[EVENT_ModifyInfo_Enter].consumers)
{
INIT_ENTER_EVENT(genMsg, nameMsg, aStack->evBlk,
EVENT_ModifyInfo_Enter, aStack->enter, id);
INIT_MODIFY_ENTER_EVENT(infoMsg, aStack->enter);
ZOS_ProduceEvent(status,&aStack->evBlk);
CHECK_ENTER_RETURN(genMsg, status, aStack->evBlk, aStack->exit,
skipOperation);
}
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
/* If we are modifying match Attributes, and this is a file beast,
* then attempt to latch the parent directory and start a transaction */
if ((modifyMatchAttr) &&
(nameMsg->curDataStream == (NamedBeast_s*)nameMsg->curDataStream))
{
oldFileAttributes = nameMsg->curFile->FILEattributes;
/* Get a pointer to the curent directory for use in the xaction */
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
if ((curDir = COMN_LookupByZid(genMsg, nameMsg->curvol,
nameMsg->fileParentZid, XLATCHED, TRUE)) == NULL)
{
status = zFAILURE;
xaction = NULL;
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
else
{
status = zOK;
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
xaction = COMN_BeginXLocal(curDir);
}
}
else
{
modifyMatchAttr = FALSE; /* In case beast is rootdir, ExtAttr or DataStream */
status = zOK;
xaction = NULL;
}
/* For now, just check if the file is exclusive locked */
if ((status == zOK) &&
(!(infoMsg->modifyFlags & MF_ALLOW_MODIFY_WITH_DENY_READ_WRITE)) &&
((nameMsg->curDataStream->NAMEDdenyReaderCount != 0) ||
(nameMsg->curDataStream->NAMEDdenyWriterCount != 0)))
{
if (COMN_IsDenyReaderWriterNotMe(genMsg,nameMsg->curDataStream))
{
locked = TRUE;
SetErrno(genMsg,zERR_ALL_FILES_IN_USE);
status = zFAILURE;
continueAfterExitEvent = TRUE;
}
}
/* Modify the info in the beast */
if (status == zOK)
{
/*---------------------------------------------------------------------------
* Check for presence of BOTH zFA_COMPRESS_FILE_IMMEDIATELY and
* zFA_DO_NOT_COMPRESS_FILE. If both are here, clear the
* zFA_COMPRESS_FILE_IMMEDIATELY bit and continue on. This functionality
* matches what occurs in the legacy file system.
*---------------------------------------------------------------------------*/
if((infoMsg->modifyInfo->std.fileAttributes & zFA_COMPRESS_FILE_IMMEDIATELY) &&
(infoMsg->modifyInfo->std.fileAttributes & zFA_DO_NOT_COMPRESS_FILE))
{
infoMsg->modifyInfo->std.fileAttributes &= ~zFA_COMPRESS_FILE_IMMEDIATELY;
}
/* If this is a hardlink name, and they are modifying
* zMOD_PRIMARY_NAMESPACE or zMOD_DELETED_INFO, these both
* need to be modified on the hard link beast itself. All other
* modification need to be made to the primary "iNode" beast.
* The only way to do this without changing the LSS interface
* is to call BST_modifyInfo twice - Once for the hard link and
* once for the rest.
*/
if (nameMsg->curFile->FILEattributes & zFA_HARDLINK)
{
/* Force this to go through the hardlink to the inode NOW */
COMN_Lookup(genMsg,nameMsg);
}
if ((nameMsg->hlFile) &&
(infoMsg->modifyInfoMask & (zMOD_PRIMARY_NAMESPACE|zMOD_DELETED_INFO)))
{
hlInfoMask = infoMsg->modifyInfoMask &
(zMOD_PRIMARY_NAMESPACE|zMOD_DELETED_INFO);
inodeInfoMask = infoMsg->modifyInfoMask &
~(zMOD_PRIMARY_NAMESPACE|zMOD_DELETED_INFO);
/* Modify some info using the hard link beast */
infoMsg->modifyInfoMask = hlInfoMask;
status = (nameMsg->curDataStream->NAMEDcomnOps.BST_modifyInfo(
genMsg,&nameMsg->hlFile->HARDLroot,infoMsg,xaction));
if ((status == zOK) && (inodeInfoMask))
{
/* Modify the rest of the info using the inode beast */
infoMsg->modifyInfoMask = inodeInfoMask;
status = (nameMsg->curDataStream->NAMEDcomnOps.BST_modifyInfo(
genMsg,&nameMsg->curDataStream->NAMEDroot,infoMsg,xaction));
}
/* Restore the original mask */
infoMsg->modifyInfoMask = hlInfoMask | inodeInfoMask;
}
else
{
status = (nameMsg->curDataStream->NAMEDcomnOps.BST_modifyInfo(
genMsg,&nameMsg->curDataStream->NAMEDroot,infoMsg,xaction));
}
}
/* Now modify the match attributes in the directory only if we really
* changed the values of one or more matchAttribute bits*/
if (modifyMatchAttr)
{
if ((status == zOK) &&
((nameMsg->curFile->FILEattributes & MA_VALID_MASK) !=
(oldFileAttributes & MA_VALID_MASK)))
{
/* The beast was modified, it was marked XLOCAL,
* and COMN_ForceBeastWrite was called inside of BST_modifyInfo */
if (nameMsg->hlFile)
{
/* If we are a hardlink, we must do this for all other
* hard link beast (with the same primary) too.
*/
status = NAME_HardLinkSetMatchAttributesInDirectory(genMsg,
nameMsg->curFile, curDir, // cnt nameMsg->fileNameUniquifier,
nameMsg->curFile->FILEattributes,
oldFileAttributes, xaction);
}
else
{
status = NAME_SetMatchAttributesInDirectory(genMsg,
nameMsg->curFile, curDir, // cnt nameMsg->fileNameUniquifier,
nameMsg->curFile->FILEattributes,
oldFileAttributes, xaction);
}
if (status != zOK)
{
STATUS status2;
inodeInfoMask = infoMsg->modifyInfoMask;
infoMsg->modifyInfoMask = zMOD_FILE_ATTRIBUTES;
infoMsg->modifyInfo->std.fileAttributes = oldFileAttributes;
status2 = (nameMsg->curDataStream->NAMEDcomnOps.BST_modifyInfo(
genMsg,&nameMsg->curDataStream->NAMEDroot,infoMsg,xaction));
infoMsg->modifyInfoMask = inodeInfoMask;
if ((status2 != zOK) &&
((nameMsg->curFile->FILEattributes & MA_VALID_MASK) !=
(oldFileAttributes & MA_VALID_MASK)))
{
/* We are in a transaction where we modified the
* attributes in the beast but not in the name tree.
* We tried to restore the attributes in the beast
* but we failed. The only way to clean this up is to
* abort the transaction. Today, this deactivates the
* volume and prevents further writes. In the future
* this will be a real transaction abort (backout). */
/* FixFixFix6 -- When we implement real transaction
* abort, we need to revisit this code */
zASSERT("Unable to restore attributes after failed SetMatchAttributesInDirectory" == NULL);
SetErrno(genMsg,zERR_NAMING_INCONSISTENCY);
COMN_AbortXLocal(genMsg,curDir->FILEvolume,xaction, WHERE);
}
}
}
}
if (xaction)
{
COMN_EndXLocal(curDir,&xaction);
}
if (curDir)
{
COMN_UnlatchAndRelease(&curDir, XLATCHED);
}
if (FSHOOKS_OR_EVENTS(ModifyDirectoryEntry_Hook,
EVENT_ModifyInfo_Exit))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
RELEASE_RSRC(NAME_RESERVE);
CHECK_FSHOOKS_MODIFY_EXIT(ModifyDirectoryEntry_Hook, &aStack->parms,
genMsg, nameMsg, infoMsg, &modifyVector);
if (NEBEventInfo[EVENT_ModifyInfo_Exit].consumers)
{
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk,
EVENT_ModifyInfo_Exit, aStack->exit, id);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
if (continueAfterExitEvent)
{
continueAfterExitEvent = FALSE;
ClearErrno(genMsg);
status = zOK;
goto doNextModify;
}
if (status != zOK)
{
goto cleanup_closedir;
}
modifyCnt++;
doNextModify:
/* reset the nameMessage back to a search pattern type for the
* next loop of WildRead */
if (COMN_ResetNameMsgToSearchPattern(genMsg,nameMsg) != zOK)
goto cleanup_closedir;
continue;
skipOperation:
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
if (status != zOK)
{
if (evOp == FALSE)
{
evOp = TRUE;
evError = GetErrno(genMsg);
}
ClearErrno(genMsg);
}
goto doNextModify;
}
if (COMN_WildClose(genMsg,&aStack->srchMsg) != zOK)
{
zASSERT("Error calling COMN_WildClose on wildcard modify info"==NULL);
}
if (evOp)
{
SetErrno(genMsg, evError);
status = zFAILURE;
goto justReturn;
}
if (modifyCnt <= 0)
{
if (locked)
SetErrno(genMsg,zERR_ALL_FILES_IN_USE);
else
SetErrno(genMsg,zERR_NO_FILES_FOUND);
status = zFAILURE;
goto justReturn;
}
else
{
if (locked)
{
SetErrno(genMsg,zERR_SOME_FILES_IN_USE);
status = zFAILURE;
goto justReturn;
}
}
}
status = zOK;
justReturn:
RELEASE_RSRC(NAME_RESERVE);
STACK_FREE();
RTN_STATUS(status);
/*===========================================================================*/
cleanup_closedir:
COMN_WildClose(genMsg,&aStack->srchMsg);
cleanup_error:
status = zFAILURE;
goto justReturn;
/*** Comes from fshooks and events already released resources **/
justReturnExit:
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
STACK_FREE();
RTN_STATUS(status);
}
/* user should free waiting if necessary */
void resumeOpen(
OpenWaiting_s *waiting)
{
ASSERT_MPKNSS_LOCK();
zASSERT((waiting->state & OPEN_WAITING) != 0);
waiting->state |= OPEN_RESUMING;
if (QMEMBER(&waiting->link))
{
DQ_RMV(waiting, link);
}
waiting->xaction->waitFor = NULL;
waiting->xaction = NULL;
waiting->openResumeCallback(&waiting);
return;
}
void cancelOpen(
OpenWaiting_s **candidate)
{
OpenWaiting_s *waiting = *candidate;
ASSERT_MPKNSS_LOCK();
zASSERT((waiting->state & OPEN_WAITING) != 0);
waiting->state |= OPEN_CANCELLED;
if (waiting->state & OPEN_RESUMING)
{
/* The thread who is resuming open will check this flag
* and close the file if it is set
*/
return;
}
if (QMEMBER(&waiting->link))
{
DQ_RMV(waiting, link);
}
waiting->xaction->waitFor = NULL;
waiting->xaction = NULL;
waiting->openResumeCallback(candidate);
}
/**************************************************************************
* Common internal interface to OPEN a file
***************************************************************************/
STATUS COMN_Open(
GeneralMsg_s *genMsg,
NamingMsg_s *nameMsg,
OpenMsg_s *openMsg,
FileHandleIDP_s *retFileHandle)
{
NINT requestedRights;
STATUS status;
STATUS result;
NINT latchType = NOTLATCHED;
NINT id;
NINT i;
BOOL doRightsCheck;
BOOL cleanupSnapBeast = FALSE;
NINT fixUpSnapReaders = 0;
BYTE fhState = 0;
FileHandle_s *fh;
Zid_t rightsCheckParentZid;
typedef struct Stack_s {
FSHooks_OpenFile_s parms;
struct EventBlock evBlk;
EventOpenEnter_s enter;
EventOpenExit_s exit;
} Stack_s;
STACK_ALLOC();
ASSERT_MPKNSS_LOCK();
COMN_ENABLE();
ENTER(TCOMMON, COMN_Open);
RESERVE_RSRC(NAME_RESERVE);
zASSERT(!(openMsg->requestedRights & ~zVALID_FILE_REQ_RIGHTS));
openMsg->requestedRights &= zVALID_FILE_REQ_RIGHTS;
if (openMsg->requestedRights & zRR_ALLOW_SECURE_DIRECTORY_ACCESS)
{
genMsg->flags |= ALLOW_SECURE_ACCESS;
}
doRightsCheck = ((nameMsg->parseFlags & NAMPFL_dontDoRightsChecks) == 0);
if ((!ConnectionIsLoggedIn(genMsg->pssConn.id)) && (doRightsCheck))
{
/* They are not logged in (LICENCED), so limit their access */
if (openMsg->requestedRights & (zRR_READ_ACCESS | zRR_SCAN_ACCESS))
{
openMsg->requestedRights &= ~zRR_WRITE_ACCESS;
}
else
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_errorAndReturn;
}
}
retFileHandle->key = 0;
retFileHandle->ptr = NULL;
if (nameMsg->parseMode == NAMPMODE_Undefined)
nameMsg->parseMode = NAMPMODE_FullyResolveNonDirectory;
zASSERT(nameMsg->latchType == XLATCHED);
/* Don't do "MayISeeTheObject" checks in COMN_Lookup. We don't need
* file scan visibility to open, only read access */
COMN_SET_NAMING_MSG_DONT_DO_VISIBILITY_CHECKS(nameMsg);
if (COMN_Lookup(genMsg,nameMsg) != zOK)
{
goto cleanup_freeOpenFileAndReturn;
}
ASSERT_XLATCH(&nameMsg->curFile->FILEbeastLatch);
ASSERT_XLATCH(&nameMsg->curDataStream->NAMEDbeastLatch);
requestedRights = (openMsg->requestedRights & (zRR_READ_ACCESS | zRR_WRITE_ACCESS)) ?
0 : zRR_PREVENT_FS_HOOKS;
INIT_EVENT_ID_STATUS(id, aStack->exit.enterRetStatus);
if (!(requestedRights & zRR_PREVENT_FS_HOOKS) &&
FSHOOKS_OR_EVENTS(OpenFile_Hook, EVENT_Open_Enter))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
RELEASE_RSRC(NAME_RESERVE);
CHECK_FSHOOKS_OPEN_ENTER(OpenFile_Hook, &aStack->parms, status,
cleanup_freeOpenFileExit, genMsg, nameMsg,
openMsg, retFileHandle);
if (NEBEventInfo[EVENT_Open_Enter].consumers)
{
INIT_ENTER_EVENT(genMsg, nameMsg, aStack->evBlk, EVENT_Open_Enter,
aStack->enter, id);
INIT_OPEN_ENTER_EVENT(aStack->enter, openMsg);
ZOS_ProduceEvent(status,&aStack->evBlk);
CHECK_ENTER_RETURN(genMsg, status, aStack->evBlk, aStack->exit,
cleanup_freeOpenFileExit);
}
RESERVE_RSRC(NAME_RESERVE);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
if (nameMsg->curDataStream->NAMEDcomnOps.BST_beastNotify(genMsg,
&nameMsg->curDataStream->NAMEDroot, BST_NOTIFY_OPEN) != zOK)
{
goto cleanup_freeOpenFile;
}
if (nameMsg->curDataStream->NAMEDattributes & zFA_SUBDIRECTORY)
{
if (genMsg->saID == zSAGENT_NETWARE)
{
SetErrno(genMsg, zERR_DIR_CANNOT_BE_OPENED);
goto cleanup_freeOpenFile;
}
}
requestedRights |= openMsg->requestedRights;
if (nameMsg->curvol->VOLenabledAttributes & zATTR_READONLY)
{
// zASSERT(!(requestedRights & ~zVALID_READ_ONLY_FILE_REQ_RIGHTS));
if (openMsg->requestedRights & ~zVALID_READ_ONLY_FILE_REQ_RIGHTS)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
}
if (doRightsCheck && nameMsg->curFile->opLockControl)
{
status = OPLOCK_BreakExclusive(genMsg, nameMsg->curFile, FALSE);
if (status != zOK)
{
if (GetErrno(genMsg) != zERR_OPLOCK_MUST_WAIT)
{
goto cleanup_freeOpenFile;
}
ClearErrno(genMsg);
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
status = OPLOCK_WaitForBreak(genMsg, nameMsg->curFile);
if (status != zOK)
{
goto sendExitEvent;
}
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
if (!(requestedRights & zRR_PSA_CACHE))
{
OPLOCK_BreakPSA(nameMsg->curFile);
}
}
rightsCheckParentZid =
nameMsg->hlFile ? nameMsg->hlParentZid : nameMsg->fileParentZid;
if (requestedRights & (zRR_DELETE_FILE_ON_CLOSE |
zRR_PURGE_IMMEDIATE_ON_CLOSE))
{
if (((doRightsCheck) &&
(nameMsg->curFile->FILEmayIDoThis(genMsg,
nameMsg->curFile, rightsCheckParentZid, MAY_I_DELETE) != zOK)) ||
(nameMsg->curFile->FILEattributes & zFA_DELETE_INHIBIT) )
/* FixFixFix6 -- The Legacy code checked the transaction bit here too
* When we do TTS we need to fix this */
{
SetErrno(genMsg, zERR_NO_FILES_FOUND);
goto cleanup_freeOpenFile;
}
}
if (requestedRights & zRR_READ_ACCESS_TO_SNAPSHOT)
{
if (!(nameMsg->curvol->VOLenabledAttributes & zATTR_COW))
{
/* COW has to be enabled for read snapshot to be valid */
SetErrno(genMsg, zERR_NO_READ_PRIVILEGE);
goto cleanup_freeOpenFile;
}
/*
* If read snapshot is requested then:
* 1) READ_ACCESS is a must. We OR it in case the user did not
* 2) WRITE_ACCESS is illegal. We return an error
* 3) DENY_READ is illegal. We return an error
* 4) DENY_WRITE leave it as is. ENABLE_IO needs it (defect 299784)
*/
requestedRights |= zRR_READ_ACCESS;
if (!(requestedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA))
{
requestedRights &= ~zRR_DENY_WRITE;
}
if (requestedRights & (zRR_WRITE_ACCESS | zRR_DENY_READ))
{
SetErrno(genMsg, zERR_NO_WRITE_PRIVILEGE);
goto cleanup_freeOpenFile;
}
}
if (requestedRights & zRR_READ_ACCESS)
{
if ((doRightsCheck) &&
(nameMsg->curFile->FILEmayIDoThis(genMsg,
nameMsg->curFile, rightsCheckParentZid, MAY_I_READ_DATA) != zOK))
{
if (nameMsg->curFile->FILEmayIDoThis(genMsg,
nameMsg->curFile, rightsCheckParentZid, MAY_I_CREATE) == zOK)
{ /* The file can be open with create rights if:
* 1. It was created by the current user
* 2. It is empty
*/
if (LB_GUIDCompare(&nameMsg->curFile->FILEownerID,
&genMsg->pssConn.ptr->authInfo.authenticatedIDs[0]) != 0)
{
goto cantRead;
}
if (nameMsg->curDataStream->NAMEDeof != 0)
{
goto cantRead;
}
}
else
{
cantRead:
SetErrno(genMsg, zERR_ACCESS_DENIED);
goto cleanup_freeOpenFile;
}
}
if (requestedRights & zRR_READ_ACCESS_TO_SNAPSHOT)
{
if (nameMsg->curDataStream->NAMEDroot.fileSnapshotBeast != NULL)
{
if (SetupSnapshotBeast(genMsg,
&nameMsg->curDataStream->NAMEDroot, nameMsg->curvol) != zOK)
{
goto cleanup_freeOpenFile;
}
}
else
{
if (nameMsg->curDataStream->NAMEDwriterCount != 0)
{
/* The file is open for write but there is no snapshot
* beast. The assumption is that the last closed state
* of this file is it did not exist. The file was created
* and opened for write as part of the create
*/
SetErrno(genMsg, zERR_LAST_STATE_UNKNOWN);
goto cleanup_freeOpenFile;
}
}
fhState |= FH_READ_BACKUP;
cleanupSnapBeast = TRUE;
}
}
if (requestedRights & zRR_WRITE_ACCESS)
{
/* Check if the volume is READ-ONLY */
if (nameMsg->curvol->VOLenabledAttributes & zATTR_READONLY)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
/* If the file is in the salvage system, don't allow write access */
if (nameMsg->retParseFlags & NAMRETPFL_hasDeletedNameType)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
if ((doRightsCheck) &&
(nameMsg->curFile->FILEmayIDoThis(genMsg,
nameMsg->curFile, rightsCheckParentZid, MAY_I_WRITE_DATA) != zOK))
{
if (nameMsg->curFile->FILEmayIDoThis(genMsg,
nameMsg->curFile, rightsCheckParentZid, MAY_I_CREATE) == zOK)
{ /* The file can be open with create rights if:
* 1. It was created by the current user
* 2. It is empty
*/
if (LB_GUIDCompare(&nameMsg->curFile->FILEownerID,
&genMsg->pssConn.ptr->authInfo.authenticatedIDs[0]) != 0)
{
goto cantWrite;
}
if (nameMsg->curDataStream->NAMEDeof != 0)
{
goto cantWrite;
}
}
else
{
cantWrite:
if (nameMsg->curFile->FILEattributes & zFA_READ_ONLY)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
}
else
{
SetErrno(genMsg, zERR_NO_OPEN_PRIVILEGE);
}
goto cleanup_freeOpenFile;
}
}
if (nameMsg->curFile->FILEattributes & zFA_READ_ONLY)
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
if (nameMsg->curvol->VOLenabledAttributes & zATTR_COW)
{
if ((nameMsg->curDataStream->NAMEDroot.fileSnapshotBeast == NULL) &&
(nameMsg->curDataStream->NAMEDsnapReaderCount != 0))
{
fixUpSnapReaders = nameMsg->curDataStream->NAMEDsnapReaderCount;
}
if (SetupSnapshotBeast(genMsg,
&nameMsg->curDataStream->NAMEDroot, nameMsg->curvol) != zOK)
{
goto cleanup_freeOpenFile;
}
for (i = 0; i < fixUpSnapReaders; i++)
{
/* Increment the use count on the snapbeast so that when
* the snapshot reader closes the file they can decrement it
*/
SetupSnapshotBeast(genMsg,
&nameMsg->curDataStream->NAMEDroot, nameMsg->curvol);
}
fhState |= FH_WRITE_SNAPSHOT;
cleanupSnapBeast = TRUE;
}
}
/* Compression-related checks */
if (CM_COMPRESSION_ENABLED(nameMsg->curvol))
{
/* If this is the first open on this beast, then we check if
* all compression related flags are compatible
*/
if (!(nameMsg->curDataStream->NAMEDattributes & zFA_SUBDIRECTORY)
&& nameMsg->curDataStream->NAMEDopenCount == 0)
{
if (fixCompFlags(genMsg, nameMsg->curDataStream) != zOK)
{
goto cleanup_freeOpenFile;
}
}
}
if (requestedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA)
{
if (!(nameMsg->curDataStream->NAMEDattributes & zFA_SUBDIRECTORY))
{
if (! CM_COMPRESSION_ENABLED(nameMsg->curvol))
{
COMN_EnableIOWarning(nameMsg->curvol);
SetErrno(genMsg, zERR_NOT_SUPPORTED);
goto cleanup_freeOpenFile;
}
if (requestedRights & zRR_WRITE_ACCESS)
{
/*
* To write compressed one must be writing to a NEW file and one
* must be requesting exclusive access.
*/
if (!(requestedRights & zRR_DENY_READ) ||
!(requestedRights & zRR_DENY_WRITE))
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
}
else
{
if (!(requestedRights & zRR_DENY_WRITE))
{
SetErrno(genMsg, zERR_ALL_FILES_READ_ONLY);
goto cleanup_freeOpenFile;
}
}
}
else
{
SetErrno(genMsg, zERR_ACCESS_DENIED);
goto cleanup_freeOpenFile;
}
}
if (FH_OpenFile(genMsg, retFileHandle, nameMsg, fhState) != zOK)
{
goto cleanup_freeOpenFile;
}
if (FH_SetOpenFileGrantedRights(genMsg, openMsg, retFileHandle,
requestedRights) != zOK)
{
if (FILE_LOCK_ERROR(GetErrno(genMsg)) && (openMsg->openWaiting))
{
/* open request can be blocked, don't clean up things
* we've done so far.
* Note: The blocked open implementation is only targeted
* for NFS LkMgr locking request (when locking request is
* conflicting with certain file handles' deny mode
* previously held by other CIFS/PC NFS/NLM clients).
* LkMgr's locking request should ONLY come throught
* COMN_Open path.
*/
zASSERT(genMsg->saID == zSAGENT_NFS);
}
goto cleanup_freeOpenFile;
}
fh = retFileHandle->ptr;
if (nameMsg->curvol->VOLenabledAttributes & zATTR_CFS_SLAVE)
{
if (CRO_AccessLeaseGet(genMsg, fh) != zOK)
{
goto cleanup_freeOpenFile;
}
}
if (fh->grantedRights & zRR_SCAN_ACCESS)
{
#if NSS_DEBUG IS_ENABLED
extern NINT DBG_NumberOfSearchMapExplicitFrees;
++DBG_NumberOfSearchMapExplicitFrees;
#endif
fh->smap = SMAP_NewSearchMap(genMsg, nameMsg);
fh->smap->options |= SMAPOPT_notReusable;
}
/* Perform compression-related processing */
if (!(nameMsg->curDataStream->NAMEDattributes & zFA_SUBDIRECTORY))
{
fh->compBeast = 0;
fh->parentIsImmCompress = 0;
if (nameMsg->retParseFlags & NAMRETPFL_parentIsImmCompress)
{
fh->parentIsImmCompress = 1;
}
if (!(nameMsg->curDataStream->NAMEDattributes & zFA_DATA_STREAM_IS_COMPRESSED))
{
fh->grantedRights &= ~zRR_LEAVE_FILE_COMPRESSED;
}
if (fh->grantedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA)
{
/* Prepare to access compressed version of the datastream. */
RootBeast_s *compBeast = 0;
if (CM_prepareToAccessCompFile(genMsg,
&nameMsg->curDataStream->NAMEDroot, &compBeast) != zOK)
{
goto cleanup_freeOpenFile;
}
if (compBeast)
{
fh->compBeast = compBeast;
UN_LATCH(&compBeast->ROOTbeastLatch);
}
}
else if (CM_COMPRESSION_ENABLED(nameMsg->curvol))
{
BOOL keep_comp_version =
(((fh->grantedRights & zRR_LEAVE_FILE_COMPRESSED) ||
fh->parentIsImmCompress)
? TRUE : FALSE);
NINT access_mode =
(fh->grantedRights & zRR_WRITE_ACCESS)
? CACHE_UPDATE : CACHE_READ;
if (CM_prepareToAccessUncompFile(genMsg, nameMsg->curFile,
&nameMsg->curDataStream->NAMEDroot, access_mode,
&keep_comp_version) != zOK)
{
goto cleanup_freeOpenFile;
}
if (keep_comp_version)
fh->grantedRights |= zRR_LEAVE_FILE_COMPRESSED;
else fh->grantedRights &= ~zRR_LEAVE_FILE_COMPRESSED;
}
}
/* Set the accessed time. We no longer set accessed time on reads and
* writes. It is only set when a file is opened or created */
{
BOOL noAtime;
noAtime = (nameMsg->curvol->VOLenabledAttributes & zATTR_NO_ATIME) != 0? TRUE : FALSE;
if (!(nameMsg->curvol->VOLenabledAttributes & zATTR_READONLY) &&
!noAtime &&
!(retFileHandle->ptr->grantedRights & zRR_DONT_UPDATE_ACCESS_TIME))
{
nameMsg->curFile->FILEaccessedTime = GetUTCTime();
nameMsg->curFile->FILEaccessedDOSTime = INVALID_DOS_TIME;
COMN_MARK_BEAST_MESSY( &nameMsg->curFile->FILEroot);
}
}
RELEASE_RSRC(NAME_RESERVE); /* Must release resources before read ahead */
/* For every open file, I want to make sure that:
* 1) There is an inode associated with this file.
* This is to ensure that pages read and written will come from
* the Linux cache. Without an inode this cannot happen.
* Open can also happen from zOpen or zCreate.
* 2) There is an useCount on the inode. Close will do iput.
* 3) lsa_get_inode does an iget which will call lsa_read_inode which
* will do a lookup which will set rb_inode for the beast.
*/
if (Ptr_lsa_get_inode)
{
struct inode *fh_inode = NULL;
BOOL needLsaInode = FALSE;
if (nameMsg->curFile->FILEroot.rb_inode)
{
if (!(nameMsg->parseFlags & NAMPFL_noExtraInodeCount))
{
fh_inode = igrab(nameMsg->curFile->FILEroot.rb_inode);
if (!fh_inode)
{
needLsaInode = TRUE;
}
}
}
else
{
needLsaInode = TRUE;
}
if (needLsaInode)
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
fh_inode = Ptr_lsa_get_inode(
&nameMsg->curFile->FILEvolume->VOLvolumeID,
nameMsg->curFile->FILEzid);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
#if 0
// if (!(nameMsg->curFile->FILEroot.rb_inode) ||
// (!(nameMsg->parseFlags & NAMPFL_noExtraInodeCount) &&
// ((fh_inode = igrab(nameMsg->curFile->FILEroot.rb_inode)) == NULL)))
// {
// COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
// fh_inode = Ptr_lsa_get_inode(
// &nameMsg->curFile->FILEvolume->VOLvolumeID,
// nameMsg->curFile->FILEzid);
// COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
// }
#endif
retFileHandle->ptr->fh_inode = fh_inode;
}
if ( retFileHandle->ptr->dataStream->NAMEDbstState & BST_STATE_DIOMODE)
{
NamedBeast_s *lDataStream = retFileHandle->ptr->dataStream;
if (( lDataStream->NAMEDdioModeCount < lDataStream->NAMEDopenCount) &&
( DQ_NOT_EMPTY(&lDataStream->NAMEDmycache.bufList)))
{
/** File is in DFS mode, so don't do read ahead **/
/** Since the file is in DFS mode, only DFSWrites are allowed, and
** those are not cached. So all cache buffers associated with this
** file in memory are read only, and we can just toss them without
** a flush. Legacy File system invalidates the cache everytime
** someone opens a file that is already in DFS mode
**
** SetDataSize on the file can dirty the buffers, so we need to
** flush before tossing. Also we should only flush and toss
** user data and not metadata.
**/
ASSERT_XLATCH(&lDataStream->NAMEDbeastLatch);
cacheFlushAndTossMyCacheUserData(&lDataStream->NAMEDmycache);
}
}
else if ((requestedRights & zRR_READ_ACCESS) &&
(retFileHandle->ptr->dataStream->NAMEDeof > 0))
{
/**do read ahead if file is open for reading and is non zero in size**/
asyncReadAhead(retFileHandle->ptr);
}
status = zOK;
sendExitEvent:
if (!(requestedRights & zRR_PREVENT_FS_HOOKS) &&
FSHOOKS_OR_EVENTS(OpenFile_Hook, EVENT_Open_Exit))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
CHECK_FSHOOKS_OPEN_EXIT(OpenFile_Hook, &aStack->parms, genMsg, nameMsg,
openMsg, retFileHandle);
if (NEBEventInfo[EVENT_Open_Exit].consumers)
{
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk, EVENT_Open_Exit, aStack->exit, id);
INIT_OPEN_EXIT_EVENT(aStack->exit, status, nameMsg, retFileHandle);
ZOS_ProduceEvent(result, &aStack->evBlk);
}
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
STACK_FREE();
RTN_STATUS(status);
/*===========================================================================*/
cleanup_freeOpenFile:
if (cleanupSnapBeast)
{
CleanupSnapshotBeast(&nameMsg->curDataStream->NAMEDroot);
}
/* check for presence of a hardlink beast whose usecount needs to decrement */
if(retFileHandle->ptr && retFileHandle->ptr->hlFile)
{
COMN_Release(&retFileHandle->ptr->hlFile);
}
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
MSG_DestroyKey(retFileHandle->key);
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
status = zFAILURE;
RELEASE_RSRC(NAME_RESERVE);
goto sendExitEvent;
cleanup_freeOpenFileAndReturn:
if ((nameMsg->curFile) || (nameMsg->curDataStream) || (nameMsg->hlFile))
{
COMN_UNLATCH_NAMEMSG_BEASTS(nameMsg, &latchType);
}
MSG_DestroyKey(retFileHandle->key);
if ((nameMsg->curFile) || (nameMsg->curDataStream)|| (nameMsg->hlFile))
{
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
}
cleanup_errorAndReturn:
RELEASE_RSRC(NAME_RESERVE);
STACK_FREE();
RTN_STATUS(zFAILURE);
/** We come here when fshooks or events cancels an operation, resources
** have already been released.
**/
cleanup_freeOpenFileExit:
MSG_DestroyKey(retFileHandle->key);
if ((nameMsg->curFile) || (nameMsg->curDataStream)|| (nameMsg->hlFile))
COMN_LATCH_NAMEMSG_BEASTS(nameMsg, latchType);
STACK_FREE();
RTN_STATUS(zFAILURE);
}
/**************************************************************************
* Truncate or expand the file to the given byte offset
***************************************************************************/
STATUS COMN_SetDataSize(
GeneralMsg_s *genMsg,
FileHandleIDP_s *fileHandleIDP,
Xid_t xid,
QUAD newEOF,
NINT setSizeFlags)
{
FileHandle_s *fileHandle;
NamedBeast_s *dataStream;
RootBeast_s *beast;
File_s *file;
QUAD curEOF;
Blknum_t blkNum, oldBlkNum, totalBlks;
STATUS status;
DataStreamCounts_s *dstrCounts = NULL;
NINT retryCount;
NINT flags;
NINT id = 0;
NINT sentEvent = FALSE;
typedef struct Stack_s {
GeneralMsg_s localGenMsg;
IoMsg_s io;
Buffer_s *cblk;
GetStorageInfo_s stInfo;
NINT startOffset;
QUAD totalLength;
NINT lenToWrite;
NINT blockSize;
struct EventBlock evBlk;
EventSetDataSizeEnter_s enter;
EventSetDataSizeExit_s exit;
} Stack_s;
STACK_ALLOC();
ASSERT_MPKNSS_LOCK();
COMN_ENABLE();
ENTER(TCOMMON, COMN_SetDataSize);
/* Don't call ConnectionIsLoggedIn on this API */
if ((fileHandle = COMN_RESOLVE_FILEHANDLE(genMsg,fileHandleIDP)) == NULL)
{
status = zFAILURE;
goto justReturn;
}
dataStream = fileHandle->dataStream;
file = fileHandle->file;
/*
* If this is a virtual data file then ignore changes to the size.
*/
if (fileHandle->virtInfo != NULL)
{
status = zOK;
goto justReturn;
}
if (newEOF > file->FILEvolume->VOLmaximumFileSize)
{
SetErrno(genMsg,zERR_FILE_TOO_LARGE);
status = zFAILURE;
goto justReturn;
}
#if BLKNUM_64 IS_DISABLED
if (newEOF > zMAX_FILE_SIZE)
{
SetErrno(genMsg,zERR_FILE_TOO_LARGE);
status = zFAILURE;
goto justReturn;
}
#endif
beast = &dataStream->NAMEDroot;
if (setSizeFlags & zSETSIZE_COMPRESSED)
{
if ((fileHandle->grantedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA) &&
(fileHandle->grantedRights & zRR_WRITE_ACCESS) &&
// (fileHandle->compBeast != 0) &&
/* No bits other than zSETSIZE_LOGICAL_ONLY
* and zSETSIZE_COMPRESSED are set */
((setSizeFlags &
~(zSETSIZE_LOGICAL_ONLY|zSETSIZE_COMPRESSED)) == 0))
{
X_LATCH(&dataStream->NAMEDbeastLatch);
if (CM_prepareToWriteCompFile(genMsg, &dataStream->NAMEDroot,
&fileHandle->compBeast) != zOK)
{
UNX_LATCH(&dataStream->NAMEDbeastLatch);
status = zFAILURE;
goto justReturn;
}
beast = fileHandle->compBeast;
UNX_LATCH(&beast->ROOTbeastLatch);
UNX_LATCH(&dataStream->NAMEDbeastLatch);
}
else
{
SetErrno(genMsg, zERR_ACCESS_DENIED);
status = zFAILURE;
goto justReturn;
}
}
/* See if we are setting the size of a dataStream. If so, we need
* to see if the parent container is currently tracking the sizes... */
if ((beast == &dataStream->NAMEDroot) &&
(dataStream != (NamedBeast_s *)file) && (file->FILEdataStreamInfo))
{
/* Check if the file beast is currently tracking data stream info.
* if it is, setup local pointers for simpler code later on */
switch (fileHandle->dataStreamNameType)
{
case zNTYPE_DATA_STREAM:
if (file->FILEdataStreamInfo->dataStream.count != DSI_COUNT_INVALID)
dstrCounts = &file->FILEdataStreamInfo->dataStream;
break;
case zNTYPE_EXTENDED_ATTRIBUTE:
if (file->FILEdataStreamInfo->extAttr.count != DSI_COUNT_INVALID)
dstrCounts = &file->FILEdataStreamInfo->extAttr;
break;
default:
SetErrno(genMsg,zERR_INVALID_NAME_TYPE);
status = zFAILURE;
goto justReturn;
}
zASSERT((dstrCounts == NULL) || (dstrCounts->count >= 1));
}
if (setSizeFlags & (zSETSIZE_PHYSICAL_ONLY | zSETSIZE_LOGICAL_ONLY))
{
/* Both of these options require the ability to set physical EOF
* independantly of logical EOF. This ability is characterized
* by the zATTR_PHYSICAL_EOF attributes. Changing either of these
* independantly of the other can also leave logical EOF greater
* than physical EOF, which requires zATTR_SPARSE_FILES to be set
*/
if ((file->FILEvolume->VOLenabledAttributes &
(zATTR_PHYSICAL_EOF|zATTR_SPARSE_FILES)) !=
(zATTR_PHYSICAL_EOF|zATTR_SPARSE_FILES))
{
/* Can't change just one of the EOF's unless the LSS supports both
* sparse files and changing physical EOF independently */
SetErrno(genMsg,zERR_PHYSICAL_EOF_NOT_ENABLED);
status = zFAILURE;
goto justReturn;
}
}
if (!(setSizeFlags & zSETSIZE_NON_SPARSE_FILE))
{
/* If the volume does not support sparse files, require that the
* this setSizeFlag be set */
/* The user is specifying "Sparse File" */
if (!(file->FILEvolume->VOLenabledAttributes & zATTR_SPARSE_FILES))
{
/* Can't change in SPARSE mode unless LSS supports it */
SetErrno(genMsg,zERR_SPARSE_FILES_NOT_ENABLED);
status = zFAILURE;
goto justReturn;
}
}
aStack->blockSize = (1 << beast->ROOTblkSizeShift);
if (setSizeFlags & zSETSIZE_PHYSICAL_ONLY)
setSizeFlags |= zSETSIZE_NON_SPARSE_FILE;
if (setSizeFlags & zSETSIZE_NON_SPARSE_FILE)
{
COMN_GetStorageInfo(genMsg, beast, &aStack->stInfo);
curEOF = aStack->stInfo.physicalEOF;
/* We are using physical EOF. Some LSS's such as DOSFAT maintain
* a physical EOF on cluster boundaries rather than on NSS block
* boundaries. In these LSS's, it is the responsibility of the LSS
* to do the partial block expands when NEW NSS blocks are added to
* the file. Also, in these LSS's, it is possible for Logical EOF
* to be greater than physical EOF if the final clusters in a block
* have been logically filled, but not yet physically populated.
*
* If logical EOF is greater than physical EOF, (which it can be if
* the LSS has not yet added physical clusters to fill up an NSS block),
* then we round the physical EOF up to the next block boundary. If we
* don't round up here, then we might end up zeroing out data that
* has been added to the last block of the file, but which has not
* yet been written physically to the file.
*/
if (beast->ROOTeof > curEOF)
{
aStack->startOffset = (NINT)(curEOF & (QUAD)(aStack->blockSize-1));
if (aStack->startOffset != 0)
{
curEOF += (aStack->blockSize - aStack->startOffset);
}
}
}
else
{
curEOF = beast->ROOTeof;
}
if (!sentEvent)
{
INIT_EVENT_ID_STATUS(id, aStack->exit.enterRetStatus);
if (NEBEventInfo[EVENT_SetDataSize_Enter].consumers)
{
INIT_SIZE_ENTER_EVENT(genMsg, aStack->evBlk, EVENT_SetDataSize_Enter,aStack->enter,
fileHandle, setSizeFlags, curEOF, newEOF,id);
ZOS_ProduceEvent(status,&aStack->evBlk);
CHECK_ENTER_RETURN(genMsg, status, aStack->evBlk, aStack->exit, justReturn);
}
sentEvent = TRUE;
}
if ((fileHandle->grantedRights & zRR_WRITE_ACCESS) == 0)
{ /* if no access granted for write then fail */
SetErrno(genMsg, zERR_NO_WRITE_PRIVILEGE);
status = zFAILURE;
goto sendExitEvent;
}
/*
* Check if there is a task based transaction that needs
* to be applied to this beast. This should really be in the
* the semantic agent but it would require restructuring the code.
*/
/*
* Ying - Nov 5, 2003. This section may not work correctly if file is
* compressed (curEOF is wrong if
* (setSizeFlags & zSETSIZE_NON_SPARSE_FILE) != 0). But Paul said
* he preferred not to attemp to fix it unless something is proven
* to be broken
*/
if ((newEOF < curEOF)
&& ((fileHandle->grantedRights & zRR_TRANSACTION_ACTIVE)
|| (file->FILEattributes & zFA_TRANSACTION)))
{
UserXaction_s *userXaction;
userXaction = resolveXaction(xid, fileHandle, NULL, NULL, 0, FALSE);
if ((userXaction != NULL)
&& ((userXaction->state == BEGIN_UXACTION)
|| (userXaction->state == ACTIVE_UXACTION)))
{
/* Flush the cache */
BST_flush( &dataStream->NAMEDroot);
/* Log the truncate */
status = LogTruncateData(genMsg, fileHandleIDP, newEOF,
curEOF, userXaction->xid);
if ((status != zOK)
&& (GetErrno(genMsg) != zERR_NO_XACTION))
{
goto sendExitEvent;
}
}
}
retryCount = 0;
RetrySetDataSizeAgain:
RESERVE_RSRC(NAME_RESERVE);
/* We need to latch the Beast when we are modifying its curEOF */
if (dataStream != (NamedBeast_s *)file)
X_LATCH(&file->FILEbeastLatch);
X_LATCH(&dataStream->NAMEDbeastLatch);
if (beast != &dataStream->NAMEDroot)
{
X_LATCH(&beast->ROOTbeastLatch);
}
if ((beast == &dataStream->NAMEDroot) &&
CM_COMPRESSION_ENABLED(beast->ROOTvolume))
{
BOOL done = FALSE;
NINT grantedRights = 0;
/*
* If file is not opened with zRR_ENABLE_IO_ON_COMPRESSED_DATA,
* decompress file if:
* 1. file has snapshot beast
* 2. or newEOF is not zero
* 3. or set logical size only.
*/
if (((fileHandle->grantedRights & zRR_ENABLE_IO_ON_COMPRESSED_DATA) == 0)
&&
( ((dataStream->NAMEDvolume->VOLenabledAttributes & zATTR_COW) &&
(fileHandle->fhState & FH_WRITE_SNAPSHOT) &&
(dataStream->NAMEDroot.fileSnapshotBeast != NULL))
|| (newEOF != 0 )
|| (setSizeFlags & zSETSIZE_LOGICAL_ONLY)))
{
/*
* Note we don't pass in beast->ROOTeof as its decomp size.
* This is because here assess mode is CACHE_WRITE (can't use
* CACHE_READ since this mode doesn't necessarily throw away
* compBeast, depending on the file idle interval), if decomp
* size is beast->ROOTeof, decomp routine thinks the write intends
* to cover the whole data range - thus all compressed data will be
* tossed away directly instead of decompressing it.
* Hence we pass in beast->ROOTeof -1 as its decompress size. This way
* all data will be decompressed. This should work fine since
* if file is compressed, beast->ROOTeof has to be bigger than 8k.
*/
status = CM_uncompressFileRange(genMsg, beast, 0,
beast->ROOTeof - 1, CACHE_WRITE, XLATCHED,
0, &grantedRights, TRUE, &done);
if (status != zOK)
{
goto cleanup_error;
}
}
}
/* Reset the curEOF, after we have gotten the latch on the file.
* It may have changed.
*/
if ((setSizeFlags & zSETSIZE_NON_SPARSE_FILE) && (newEOF != 0))
{
COMN_GetStorageInfo(genMsg, beast, &aStack->stInfo);
curEOF = aStack->stInfo.physicalEOF;
if (beast->ROOTeof > curEOF)
{
aStack->startOffset = (NINT)(curEOF & (QUAD)(aStack->blockSize-1));
if (aStack->startOffset != 0)
{
curEOF += (aStack->blockSize - aStack->startOffset);
}
}
}
else
{
curEOF = beast->ROOTeof;
}
if (file->opLockControl)
{
OPLOCK_BreakShared(file);
}
if (setSizeFlags & zSETSIZE_LOGICAL_ONLY)
{
curEOF = beast->ROOTeof;
if (dstrCounts)
dstrCounts->dataSize += (newEOF - beast->ROOTeof);
beast->ROOTeof = newEOF;
fileHandle->fhState |= FH_MODIFIED;
if (dataStream->NAMEDroot.csaBeast != NULL)
{
dataStream->NAMEDroot.csaBeast->CB_dataSeqNum++;
}
COMN_MARK_BEAST_DIRTY(beast);
}
else if (newEOF > curEOF)
{
/*---------------------------------------------------------------------------
* Expanding the file...
*---------------------------------------------------------------------------*/
if (setSizeFlags & zSETSIZE_NON_SPARSE_FILE)
{
/* expand the file by allocating blocks */
/* Calc total number of bytes to be allocated */
aStack->totalLength = newEOF - curEOF;
/* Find the blkNum of the first byte to be written */
blkNum = curEOF >> beast->ROOTblkSizeShift;
/* If the first byte is on a block boundary, start at 0, else
* start in the middle of the block */
aStack->startOffset = curEOF & (aStack->blockSize-1);
aStack->lenToWrite = aStack->blockSize - aStack->startOffset;
totalBlks = 1 + ((aStack->lenToWrite >= aStack->totalLength) ? 0 :
(1 + ((aStack->totalLength - aStack->lenToWrite - 1) >> beast->ROOTblkSizeShift)));
flags = 0;
if (setSizeFlags & zSETSIZE_NO_ZERO_FILL)
{
flags |= ALLOC_NO_ZERO_FILL;
}
if (setSizeFlags & zSETSIZE_CONTIGUOUS)
{
flags |= ALLOC_BLOCKS_CONTIGUOUS;
}
FILEBLK_IO_MSG_FLAG(aStack->io, beast, blkNum, totalBlks,
((aStack->startOffset != 0) ? CACHE_UPDATE : CACHE_WRITE), flags);
aStack->cblk = COMN_GetFileBlk(genMsg, &aStack->io);
if (aStack->cblk == NULL)
{
if (GetErrno(genMsg) == zERR_PURGED_SPACE_UNAVAILABLE)
{
retryCount++;
if (retryCount <= RETRY_WRITE_DELAY_COUNT)
{
ClearErrno(genMsg);
if (dataStream != (NamedBeast_s *)file)
UNX_LATCH(&file->FILEbeastLatch);
if (beast != &dataStream->NAMEDroot)
{
UNX_LATCH(&beast->ROOTbeastLatch);
}
UNX_LATCH(&dataStream->NAMEDbeastLatch);
RELEASE_RSRC(NAME_RESERVE);
LB_delay(USER_DELAY_TIME);
goto RetrySetDataSizeAgain;
}
}
/* An error occurred in zero-fill expanding */
if (setSizeFlags & zSETSIZE_UNDO_ON_ERR)
{
COMN_SETUP_GENERAL_MSG_NOSA(&aStack->localGenMsg);
/* physically shrink back the file to the original EOF.
* No zeroing needs to be done in the EOF block, but
* all allocated blocks beyond EOF need to be discarded */
blkNum = (curEOF + (aStack->blockSize-1)) >> dataStream->NAMEDblkSizeShift;
BST_truncate(&aStack->localGenMsg, beast, blkNum, -1, 0); /* ignore the error*/
/* After beast is truncated, its related compBeast is thrown away.
* we need to decrement compBeast's useCount that we add in
* CM_prepareAccessCompFile
*/
if (beast == &dataStream->NAMEDroot && fileHandle->compBeast)
{
COMN_Release(&fileHandle->compBeast);
}
}
newEOF = curEOF;
goto cleanup_error;
}
if (!(setSizeFlags & zSETSIZE_NO_ZERO_FILL))
{
mapBufferPage(aStack->cblk);
bzero((aStack->cblk->pBuf.data + aStack->startOffset), aStack->lenToWrite);
unmapBufferPage(aStack->cblk);
CACHE_DIRTY_RELEASE(aStack->cblk);
}
else
{
CACHE_RELEASE(aStack->cblk);
}
}
if (!(setSizeFlags & zSETSIZE_PHYSICAL_ONLY))
{
if (dstrCounts)
dstrCounts->dataSize += (newEOF - dataStream->NAMEDeof);
beast->ROOTeof = newEOF;
fileHandle->fhState |= FH_MODIFIED;
if (dataStream->NAMEDroot.csaBeast != NULL)
{
dataStream->NAMEDroot.csaBeast->CB_dataSeqNum++;
}
COMN_MARK_BEAST_DIRTY(beast);
}
else
{
/* Get file blk sets this whenever we allocate more than
* one block to extend a file. Closing the file will truncate
* it back to the logical eof. In this case we do not want
* that since we have done a set physical size only.
*/
beast->bstState &= ~BST_STATE_TRUNCATE_CLOSE;
COMN_MARK_BEAST_DIRTY(beast);
}
}
else if (newEOF < curEOF)
{
/*---------------------------------------------------------------------------
* If truncating to the middle of a block, we need to zero the end of the
* block if it is allocated to the file.
*---------------------------------------------------------------------------*/
oldBlkNum = (curEOF + ((1 << beast->ROOTblkSizeShift) -1 )) >> beast->ROOTblkSizeShift;
blkNum = newEOF >> beast->ROOTblkSizeShift;
if ((newEOF & (aStack->blockSize-1)) != 0)
{
/* Truncating to middle of the block */
/* expand the file with zero filled data */
if (beast->ROOTcomnOps.BST_isBlockInBeast(beast, blkNum))
{
if ((dataStream->NAMEDvolume->VOLenabledAttributes &
zATTR_COW) &&
(fileHandle->fhState & FH_WRITE_SNAPSHOT) &&
(dataStream->NAMEDroot.fileSnapshotBeast != NULL))
{
if (COMN_CopyTheBlockToSnapshot(genMsg,
&dataStream->NAMEDroot, blkNum) != zOK)
{
goto cleanup_error;
}
}
FILEBLK_IO_MSG(aStack->io, beast, blkNum, 1, CACHE_UPDATE);
aStack->cblk = COMN_GetFileBlk(genMsg, &aStack->io);
zASSERT(aStack->cblk != NULL);
if (aStack->cblk == NULL)
{
goto cleanup_error;
}
aStack->startOffset = newEOF & (aStack->blockSize-1);
aStack->lenToWrite = aStack->blockSize - aStack->startOffset;
mapBufferPage(aStack->cblk);
bzero((aStack->cblk->pBuf.data + aStack->startOffset), aStack->lenToWrite);
unmapBufferPage(aStack->cblk);
CACHE_DIRTY_RELEASE(aStack->cblk);
}
blkNum++;
}
if (blkNum <= oldBlkNum)
{
if ((dataStream->NAMEDvolume->VOLenabledAttributes & zATTR_COW) &&
(fileHandle->fhState & FH_WRITE_SNAPSHOT) &&
(dataStream->NAMEDroot.fileSnapshotBeast != NULL))
{
if (COMN_CopyFilemapToSnapshot(genMsg, &dataStream->NAMEDroot,
blkNum) != zOK)
{
goto cleanup_error;
}
}
/* physically remove all whole blocks beyond newEOF */
if (BST_truncate(genMsg, beast, blkNum, -1, 0) != zOK)
{
//FixFixFix6 -- need to roll back this transaction
goto cleanup_error;
}
/* After beast is truncated, its related compBeast is thrown away.
* we need to decrement compBeast's useCount that we add in
* CM_prepareAccessCompFile
*/
if (beast == &dataStream->NAMEDroot && fileHandle->compBeast)
{
COMN_Release(&fileHandle->compBeast);
}
}
/*---------------------------------------------------------------------------
* Truncating the file -- NOTE--There is some strange behavior here. If
* zSETSIZE_NON_SPARSE_FILE is set, curEOF is physical and newEOF is logical.
* We could actually be growing logical EOF here. This code deals with it
*---------------------------------------------------------------------------*/
if (!(setSizeFlags & zSETSIZE_PHYSICAL_ONLY))
{
if (dstrCounts)
dstrCounts->dataSize += (newEOF - beast->ROOTeof);
dataStream->NAMEDeof = newEOF;
fileHandle->fhState |= FH_MODIFIED;
if (dataStream->NAMEDroot.csaBeast != NULL)
{
dataStream->NAMEDroot.csaBeast->CB_dataSeqNum++;
}
COMN_MARK_BEAST_DIRTY(beast);
}
}
else
{
/*---------------------------------------------------------------------------
* Sizes are the same -- NOTE--There is some strange behavior here. If
* zSETSIZE_NON_SPARSE_FILE is set, curEOF is physical and newEOF is logical.
* We could actually be growing logical EOF here, even though curEOF == newEOF
*---------------------------------------------------------------------------*/
if (!(setSizeFlags & zSETSIZE_PHYSICAL_ONLY))
{
/* We need to set logical EOF if the newEOF does not equal the
* current logical EOF */
if (dataStream->NAMEDeof != newEOF)
{
if (dstrCounts)
dstrCounts->dataSize += (newEOF - dataStream->NAMEDeof);
dataStream->NAMEDeof = newEOF;
fileHandle->fhState |= FH_MODIFIED;
if (dataStream->NAMEDroot.csaBeast != NULL)
{
dataStream->NAMEDroot.csaBeast->CB_dataSeqNum++;
}
COMN_MARK_BEAST_DIRTY(&dataStream->NAMEDroot);
}
}
}
if (dataStream->NAMEDbstState & BST_STATE_DIOMODE)
{
/* We do this first, while we have the X_LATCH, and then later
* we will do a BST_flush, after unlatching the beast
*/
cacheFlushAndTossMyCacheUserData(&dataStream->NAMEDmycache);
}
if (dataStream != (NamedBeast_s *)file)
UNX_LATCH(&file->FILEbeastLatch);
if (beast != &dataStream->NAMEDroot)
{
UNX_LATCH(&beast->ROOTbeastLatch);
}
UNX_LATCH(&dataStream->NAMEDbeastLatch);
if (dataStream->NAMEDbstState & BST_STATE_DIOMODE)
{
/** We absolutely want to do a BST_flush here, because we want to
** guarantee that the metadata of the beast is committed when we
** do a setDataSize
**/
BST_flush( &dataStream->NAMEDroot);
}
RELEASE_RSRC(NAME_RESERVE);
status = zOK;
sendExitEvent:
if (NEBEventInfo[EVENT_SetDataSize_Exit].consumers)
{
INIT_EXIT_EVENT(status, genMsg, aStack->evBlk,
EVENT_SetDataSize_Exit, aStack->exit, id);
INIT_SIZE_EXIT_EVENT(aStack->exit, newEOF);
ZOS_ProduceEvent(status,&aStack->evBlk);
}
justReturn:
STACK_FREE();
RTN_STATUS(status);
/*========================================================================*/
cleanup_error:
if (dataStream != (NamedBeast_s *)file)
UNX_LATCH(&file->FILEbeastLatch);
if (beast != &dataStream->NAMEDroot)
{
UNX_LATCH(&beast->ROOTbeastLatch);
}
UNX_LATCH(&dataStream->NAMEDbeastLatch);
RELEASE_RSRC(NAME_RESERVE);
status = zFAILURE;
goto sendExitEvent;
}
STATUS COMN_SetEOF (zNSSMsg_s *msg)
{
FileHandle_s *fileHandle = (FileHandle_s *)msg->sys.door;
GeneralMsg_s genMsg;
FileHandleIDP_s fileHandleIDP;
STATUS status;
COMN_SETUP_GENERAL_MSG( &genMsg, fileHandle->connID, fileHandle->taskID,
zSAGENT_NONE);
fileHandleIDP.ptr = fileHandle;
status = COMN_SetDataSize( &genMsg, &fileHandleIDP,
msg->body.id.xid,
msg->body.rw.startingOffset,
msg->body.id.externalFlags);
if (status != 0)
{
SetStatus(msg, GetErrno( &genMsg));
}
return status;
}
/**************************************************************************
* Get the physical extent map for the file
***************************************************************************/
STATUS COMN_GetPhysicalFileMap(
GeneralMsg_s *genMsg,
FileMapMsg_s *mapMsg,
FileHandle_s *fileHandle)
{
zNSSMsg_s msg;
STATUS status;
msg.sys.status = zOK;
msg.sys.owner = NULL;
msg.sys.door = (QUAD)(uintptr_t)&fileHandle->door;
msg.sys.numDataAreas = 1;
msg.sys.data[MAP_DATA].start = (QUAD)(uintptr_t)mapMsg->ret_extentList;
msg.sys.data[MAP_DATA].length = mapMsg->extentListCount *
sizeof(zPhysicalExtent_s);
msg.body.id.xid = 0;
msg.body.id.internalFlags = 0;
msg.body.id.externalFlags = 0;
msg.body.map.offset = mapMsg->startingOffset;
msg.body.map.extentListFormat = zFILEMAP_PHYSICAL;
status = MSG_GetFileMap(&msg);
mapMsg->ret_endingOffset = msg.body.map.retEndingOffset;
mapMsg->ret_extentListCount = msg.body.map.retExtentListCount;
if (status != zOK)
{
SetErrnoFromStatus(genMsg, &msg);
}
return status;
}
/**************************************************************************
* Get a file extent map for the file
***************************************************************************/
STATUS COMN_GetFileMap(
GeneralMsg_s *genMsg,
FileMapMsg_s *mapMsg,
FileHandleIDP_s *fileHandleIDP)
{
#define EXTENT_LIST_SIZE 128
typedef struct Stack_s {
Blknum_t extentList[EXTENT_LIST_SIZE];
Blknum_t startBlock;
Blknum_t nextBlock;
Blknum_t sBlkNum;
Blknum_t sBlkCount;
Blknum_t blk;
Blknum_t allocBlks;
} Stack_s;
QUAD nextOffset;
QUAD len64;
QUAD eof;
NINT startAdjustment;
NINT extentListCount;
NINT numExtents;
NINT blockSize;
NINT blockShift;
NINT idx;
zAllocationExtentElement_s *nextRetElem;
FileHandle_s *fileHandle;
NamedBeast_s *dataStream;
RootBeast_s *beast;
RootBeast_s *fileSnapBeast = NULL;
BOOL useSnapshot = FALSE;
BOOL processBothMaps = FALSE;
RootBeast_s *fileMapBeast;
STACK_ALLOC();
COMN_ENABLE();
ENTER(TCOMMON, COMN_GetFileMap);
fileHandle = COMN_RESOLVE_FILEHANDLE(genMsg, fileHandleIDP);
if (fileHandle == NULL)
{
STACK_FREE();
RTN_STATUS(zFAILURE);
}
/* Check if requested format is supported */
switch (mapMsg->extentListFormat)
{
case zFILEMAP_LOGICAL:
SetErrno(genMsg, zERR_NOT_SUPPORTED);
STACK_FREE();
RTN_STATUS(zFAILURE);
case zFILEMAP_PHYSICAL:
STACK_FREE();
RTN_STATUS (COMN_GetPhysicalFileMap(genMsg, mapMsg, fileHandle));
case zFILEMAP_ALLOCATION:
break;
default:
SetErrno(genMsg, zERR_NOT_SUPPORTED);
STACK_FREE();
RTN_STATUS(zFAILURE);
}
RESERVE_RSRC(NAME_RESERVE);
// if (nameMsg->parseMode == NAMPMODE_Undefined)
// nameMsg->parseMode = NAMPMODE_FullyResolveAny;
if (mapMsg->extentListCount < 1)
{
SetErrno(genMsg, zERR_BUFFER_TOO_SMALL);
RELEASE_RSRC(NAME_RESERVE);
STACK_FREE();
RTN_STATUS(zFAILURE);
}
// if (COMN_Lookup(genMsg,nameMsg) != zOK)
// {
// RELEASE_RSRC(NAME_RESERVE);
// STACK_FREE();
// RTN_STATUS(zFAILURE);
// }
// ASSERT_LATCH(&nameMsg->curFile->FILEbeastLatch);
dataStream = fileHandle->dataStream;
S_LATCH( &dataStream->NAMEDbeastLatch);
if ((beast = fileHandle->compBeast) != NULL)
{
S_LATCH(&beast->ROOTbeastLatch);
eof = beast->ROOTeof;
}
else
{
beast = &dataStream->NAMEDroot;
if (CM_COMPRESSION_ENABLED(beast->ROOTvolume))
{
decompressNoRights(beast, mapMsg->startingOffset, beast->ROOTeof);
}
if ((beast->ROOTvolume->VOLenabledAttributes & zATTR_COW) &&
(fileHandle->fhState & FH_READ_BACKUP) &&
((fileSnapBeast = beast->fileSnapshotBeast) != NULL))
{
/* If snapShots are enabled, and there exists a snapshot, and the
* file handle is for backup */
useSnapshot = TRUE;
S_LATCH(&fileSnapBeast->ROOTbeastLatch);
eof = fileSnapBeast->eof;
}
else
{
eof = beast->ROOTeof;
}
}
/*---------------------------------------------------------------------------
* In a loop, call the beast op to get the extent list pieces until we have
* enough to satify the callers request.
*---------------------------------------------------------------------------*/
blockShift = beast->ROOTblkSizeShift;
blockSize = 1 << blockShift;
aStack->startBlock = mapMsg->startingOffset >> beast->ROOTblkSizeShift;
nextOffset = mapMsg->startingOffset & ~((QUAD)blockSize-1);
startAdjustment = mapMsg->startingOffset & ((QUAD)blockSize-1);
extentListCount = 0;
nextRetElem = mapMsg->ret_extentList;
if (mapMsg->startingOffset >= eof)
{
SetErrno(genMsg,zERR_END_OF_FILE);
goto finished;
}
/* FileMapBeast is a pointer to the real beast or the snapshot beast or
* compressed beast. If we are getting information on compressed beast
* then there is no snapshot beast
*/
fileMapBeast = beast;
if ((useSnapshot) && (fileSnapBeast->bitCount != 0))
{
/* There is a snapshot & at least some blocks have been written to
* in the snapshot beast
*/
if (beast->bitCount == fileSnapBeast->bitCount)
{
/* The whole filemap was copied to the snapshot beast, so
* all the info can be gotten from the snapshot beast
*/
fileMapBeast = fileSnapBeast;
}
else
{
/* We will have to get info from both the beasts to decide if
* block is allocated
*/
processBothMaps = TRUE;
}
}
for (;;)
{
if (beast->ROOTcomnOps.BST_getExtentList(genMsg,
fileMapBeast, aStack->startBlock, EXTENT_LIST_SIZE,
&aStack->extentList[0], &numExtents, &aStack->nextBlock) != zOK)
{
if (GetErrno(genMsg) != zERR_END_OF_FILE)
{
RELEASE_RSRC(NAME_RESERVE);
if (useSnapshot)
UNS_LATCH( &fileSnapBeast->ROOTbeastLatch);
if (beast != &dataStream->NAMEDroot)
UNS_LATCH(&beast->ROOTbeastLatch);
UNS_LATCH( &dataStream->NAMEDbeastLatch);
STACK_FREE();
RTN_STATUS(GetErrno(genMsg));
}
/* if processBothMaps is set, filemapbeast points to datastream */
if ((processBothMaps) && (fileMapBeast->eof < fileSnapBeast->eof))
{
/* We got an EOF error on the original beast, but if it was
* truncated, then we need to continue to get info from the
* snapshot beast. The file map of the snapshot beast beyond
* the truncation point will be complete
*/
fileMapBeast = fileSnapBeast;
processBothMaps = FALSE;
ClearErrno(genMsg);
continue;
}
goto finished;
}
/*---------------------------------------------------------------------------
* Process the extent list. There is one entry for every logical extent
* and an entry for every hole. For logical extents, the entries are positive
* numbers indicating how many blocks are in the extent. For holes, the
* entries are negative numbers, the complement of which indicates how many
* blocks make up the hole.
*---------------------------------------------------------------------------*/
for (idx=0; idx<numExtents; idx++)
{
/* Igore remaining exent if it is beyond the logical eof */
if (nextOffset >= eof)
goto finished;
if (aStack->extentList[idx] < 0)
{
/* We encountered a negative extent, which indicates a hole
* in the file...skip over the hole */
if (processBothMaps)
{
/* Before we skip over the hole, we will look to make sure
* it is not allocated in the snapshot beast. Since
* processBothMaps is set we know we are currently
* processing the dataStream beast
*/
aStack->sBlkNum = nextOffset >> blockShift;
aStack->sBlkCount = -aStack->extentList[idx];
for (aStack->blk = aStack->sBlkNum; aStack->blk < (aStack->sBlkNum + aStack->sBlkCount); aStack->blk++)
{
if (nextOffset > eof)
goto finished;
if (fileMapBeast->ROOTcomnOps.BST_isBlockInBeast(
fileSnapBeast, aStack->blk))
{
/* The block is allocated in the snapshot beast */
nextRetElem->offset = nextOffset;
nextOffset += blockSize;
if (nextOffset > eof)
{
nextRetElem->length = eof-nextRetElem->offset;
}
else
{
nextRetElem->length = blockSize;
}
if (startAdjustment)
{
nextRetElem->offset += startAdjustment;
nextRetElem->length -= startAdjustment;
startAdjustment = 0;
}
nextRetElem++;
/* inc the count, and if we just filled up the
* callers buffer, get out now... */
if (++extentListCount == mapMsg->extentListCount)
goto finished;
}
else
{
nextOffset += blockSize;
}
startAdjustment = 0;
}
}
else
{
startAdjustment = 0;
/* Switched to a local to avoid a mod 4GB problem with QUAD += (LONG << LONG) */
len64 = -aStack->extentList[idx];
len64 <<= blockShift;
nextOffset += len64;
}
}
else if (aStack->extentList[idx] != 0)
{
/* A positive extent indicates an allocated portion */
if (processBothMaps)
{
aStack->sBlkNum = nextOffset >> blockShift;
aStack->sBlkCount = aStack->extentList[idx];
aStack->allocBlks = 0;
continueProcessing:
for (aStack->blk = aStack->sBlkNum; aStack->blk < (aStack->sBlkNum + aStack->sBlkCount); aStack->blk++)
{
if (nextOffset > eof)
goto finished;
if ((fileMapBeast->bitMap != NULL) &&
(TST_BIT(fileMapBeast->bitMap, aStack->blk)) &&
(!(fileMapBeast->ROOTcomnOps.
BST_isBlockInBeast(fileSnapBeast, aStack->blk))) )
{
/* There was a hole in the file when the copy
* was made. We need to return hole info
*/
if (aStack->allocBlks == 0)
{
startAdjustment = 0;
nextOffset += blockSize;
aStack->sBlkNum++;
aStack->sBlkCount--;
aStack->allocBlks = 0;
continue;
}
else
{
break;
}
}
aStack->allocBlks++;
}
if (aStack->allocBlks == 0)
goto doneWithThisPiece;
/* write out the extent info... */
nextRetElem->offset = nextOffset;
len64 = aStack->allocBlks;
len64 <<= blockShift;
nextOffset += len64;
if (nextOffset > eof)
{
/* Truncate this extent to the logical eof */
nextRetElem->length = (eof - nextRetElem->offset);
}
else
{
/* Keep the full extent */
len64 = aStack->allocBlks;
len64 <<= blockShift;
nextRetElem->length = len64;
}
if (startAdjustment)
{
nextRetElem->offset += startAdjustment;
nextRetElem->length -= startAdjustment;
startAdjustment = 0;
}
nextRetElem++;
/* inc the count, and if we just filled up the callers
* buffer, get out now... */
if (++extentListCount == mapMsg->extentListCount)
goto finished;
if (aStack->allocBlks < aStack->sBlkCount)
{
if (nextOffset > eof)
goto finished;
aStack->sBlkNum+= aStack->allocBlks;
aStack->sBlkCount-= aStack->allocBlks;
aStack->allocBlks = 0;
goto continueProcessing;
}
doneWithThisPiece:;
}
else
{
/* write out the extent info... */
nextRetElem->offset = nextOffset;
len64 = aStack->extentList[idx];
len64 <<= blockShift;
nextOffset += len64;
if (nextOffset > eof)
{
/* Truncate this extent to the logical eof */
nextRetElem->length = (eof - nextRetElem->offset);
}
else
{
/* Keep the full extent */
len64 = aStack->extentList[idx];
len64 <<= blockShift;
nextRetElem->length = len64;
}
if (startAdjustment)
{
nextRetElem->offset += startAdjustment;
nextRetElem->length -= startAdjustment;
startAdjustment = 0;
}
nextRetElem++;
/* inc the count, and if we just filled up the callers
* buffer, get out now... */
if (++extentListCount == mapMsg->extentListCount)
goto finished;
}
}
}
if (numExtents < EXTENT_LIST_SIZE)
{
/* We can safely assume that if the list returned to us was not
* full, then there are no more extents to get from the file... */
/* except if there is a snapshot beast and its eof is greater
* than the original beast */
if ((processBothMaps) && (fileMapBeast->eof < fileSnapBeast->eof))
{
/* We need to continue to get info from the snapshot beast
* if orig beast is smaller than snapshot beast.
* The filemap of the snapshot beast will be complete from
* the truncation point, so just set startBlock to nextBlock
* and continue processing the snapshot file.
*/
fileMapBeast = fileSnapBeast;
processBothMaps = FALSE;
aStack->startBlock = aStack->nextBlock;
continue;
}
break;
}
/* Setup to get more extents from the file*/
aStack->startBlock = aStack->nextBlock;
}
finished:
if (GetErrno(genMsg) != zOK)
{
if (GetErrno(genMsg) != zERR_END_OF_FILE)
{
mapMsg->ret_endingOffset = 0;
mapMsg->ret_extentListCount = 0;
RELEASE_RSRC(NAME_RESERVE);
if (useSnapshot)
UNS_LATCH( &fileSnapBeast->ROOTbeastLatch);
if (beast != &dataStream->NAMEDroot)
UNS_LATCH(&beast->ROOTbeastLatch);
UNS_LATCH( &dataStream->NAMEDbeastLatch);
STACK_FREE();
RTN_STATUS(zFAILURE);
}
mapMsg->ret_endingOffset = 0;
}
else
{
mapMsg->ret_endingOffset = nextOffset;
}
mapMsg->ret_extentListCount = extentListCount;
RELEASE_RSRC(NAME_RESERVE);
if (useSnapshot)
UNS_LATCH( &fileSnapBeast->ROOTbeastLatch);
if (beast != &dataStream->NAMEDroot)
UNS_LATCH(&beast->ROOTbeastLatch);
UNS_LATCH( &dataStream->NAMEDbeastLatch);
STACK_FREE();
RTN_STATUS(zOK);
}
/*****************************************************************************
*
* These routines setup and cleanup the snapshot beast used by COW
*
****************************************************************************/
STATUS SetupSnapshotBeast(
GeneralMsg_s *genMsg,
RootBeast_s *beast,
Volume_s *volume)
{
RootBeast_s *snapBeast;
Xaction_s *xaction;
PurgeLogMsg_s purgeLogMsg;
ASSERT_MPKNSS_LOCK();
ASSERT_XLATCH(&beast->ROOTbeastLatch);
if (beast->fileSnapshotBeast == NULL)
{
if (beast->bstState & (BST_STATE_DIRTY | BST_STATE_MESSY))
{
xaction = COMN_BeginXLocal(beast);
COMN_MARK_BEAST_XLOCAL(beast, xaction);
if (COMN_ForceBeastWrite(genMsg, beast, xaction) != zOK)
{
COMN_EndXLocal(beast, &xaction);
return zFAILURE;
}
COMN_EndXLocal(beast, &xaction);
}
genMsg->flags |= DO_NOT_UNPACK_FMAP;
snapBeast = BST_read(genMsg, beast->zid, volume);
genMsg->flags &= ~DO_NOT_UNPACK_FMAP;
if (snapBeast == NULL)
{
return zFAILURE;
}
/* We do not want the snapshot beast to be on the volumes link
* list of all beasts. Only the real beast should have a pointer
* to it, because when the volume is being deactivated it can
* pre-maturely toss the beast away.
*/
/* 10/28/03 - Vandana - ROOT_BST_Construct no longer puts on volLink*/
// DQ_RMV(snapBeast, volLink);
X_LATCH(&snapBeast->ROOTbeastLatch);
snapBeast->zid |= SNAPSHOT_ZID_FLAG;
snapBeast->bstState |= BST_STATE_NEW;
snapBeast->useCount++;
xaction = COMN_BeginXLocal(snapBeast);
SETUP_BEAST_DELETE_PURGE_LOG(&purgeLogMsg, snapBeast, NULL);
if (snapBeast->ROOTcomnVolOps.VOL_addPurgeLogEntry(genMsg,
volume, PLOG_BEAST_DELETE, &purgeLogMsg, xaction) != zOK)
{
COMN_EndXLocal(snapBeast, &xaction);
UNX_LATCH(&snapBeast->ROOTbeastLatch);
BST_free(snapBeast);
return zFAILURE;
}
beast->fileSnapshotBeast = snapBeast;
COMN_EndXLocal(snapBeast, &xaction);
if (CreateSnapshotBitMap(genMsg, beast) != zOK)
{
UNX_LATCH(&snapBeast->ROOTbeastLatch);
CleanupSnapshotBeast(beast);
return zFAILURE;
}
UNX_LATCH(&snapBeast->ROOTbeastLatch);
}
else
{
snapBeast = beast->fileSnapshotBeast;
X_LATCH(&snapBeast->ROOTbeastLatch);
snapBeast->useCount++;
UNX_LATCH(&snapBeast->ROOTbeastLatch);
}
return zOK;
}
void CleanupSnapshotBeast(
RootBeast_s *beast)
{
RootBeast_s *snapBeast;
ASSERT_MPKNSS_LOCK();
ASSERT_XLATCH(&beast->ROOTbeastLatch);
if (beast->fileSnapshotBeast == NULL)
{
return;
}
snapBeast = beast->fileSnapshotBeast;
X_LATCH(&snapBeast->ROOTbeastLatch);
snapBeast->useCount--;
if (snapBeast->useCount > 0)
{
UNX_LATCH(&snapBeast->ROOTbeastLatch);
return;
}
CANCEL_ALARM(snapBeast->ROOTmycache.agent.timer);
DestroySnapshotBitMap(beast);
beast->fileSnapshotBeast = NULL;
UNX_LATCH(&snapBeast->ROOTbeastLatch);
BST_SignalPurge(&snapBeast->ROOTagent);
}