7275 lines
224 KiB
C
7275 lines
224 KiB
C
/****************************************************************************
|
||
|
|
||
| (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: 1comment
|
||
* 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);
|
||
}
|