PFSNames, PFSPrefixMap, RefText, Rope, UnixDirectory, UnixErrno, UnixFSPrivate, UnixSysCalls, UnixSysCallExtensions, UXStrings
Procs to provide unix view
unixProcs: PFSClass.FileManipulationProcs ¬
NEW[PFSClass.FileManipulationProcsObject ¬ [
delete~UXDelete,
enumerateForInfo~UXEnumerateForInfo,
enumerateForNames~UXEnumerateForNames,
lookupName: UXLookupName,
copy: UXCopy,
setAttributes: UXSetAttributes,
setByteCountAndUniqueID: UXSetByteCountAndUniqueID,
setClientProperty: UXSetClientProperty,
getClientProperty: UXGetClientProperty,
enumerateClientProperties: UXEnumerateClientProperties,
fileInfo~UXFileInfo,
rename~UXRename,
read~UXRead,
write~UXWrite,
open~UXOpen,
close~UXClose,
store~UXStore,
retrieve~UXRetrieve,
attach~UXAttach,
getInfo~UXGetInfo,
pfsNameToUnixName: UXPFSNameToUnixName,
caseSensitive: UXCaseSensitive
]];
UXDelete: PFSClass.DeleteProc
-- [h, file, wantedUniqueID, proc] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
selfName: ROPE ~ "UXDelete";
FailIfReadOnly[h, file];
file ¬ FixPathForUnix[file]; -- PFS.Error if it's a pattern
IF (proc =
NIL)
OR proc[file, UXGetInfoInner[h, file, wantedUniqueID].uniqueID]
THEN {
unixFile: ROPE ~ PFS.RopeFromPath[file];
cFile: CString ~ UXO[unixFile];
res: RES ~ UnixSysCalls.Unlink[cFile];
UXR[cFile];
IF res#success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF errno = EPERM OR errno = EINVAL THEN PFSBackdoor.ProduceError[accessDenied, MakeMsg[selfName, unixFile, " not deleted: can't delete a directory."]]
ELSE {
message: ROPE ~ MakeMsg[selfName, "Delete Failed for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
};
};
};
PFSFileTypeFromUnixType:
PROC [ uft: UnixTypes.Fmt ]
RETURNS [ pfs:
PFS.FileType ] ~ {
pfs ¬
SELECT uft
FROM
dir => PFS.tDirectory,
ENDCASE => PFS.tUnspecified;
};
UXEnumerateForInfo: PFSClass.EnumerateForInfoProc
-- [h: FSHandle, pattern: NAME, proc: InfoProc] -- ~
TRUSTED {
data: FSData ¬ NARROW[h.data]; -- sanity check
dirPath, unixDirPath: PATH;
listOfNames: LIST OF PATH ¬ NIL;
selfName: ROPE ~ "UXEnumerateForInfo";
[dirPath, unixDirPath, listOfNames] ¬ EnumerateInner[h, pattern];
FOR each:
LIST
OF
PATH ¬ listOfNames, each.rest
WHILE each #
NIL
DO
fileType: PFS.FileType ¬ PFS.tUnspecified;
attachedTo: PATH ¬ NIL;
uniqueID: PFS.UniqueID;
mutability: PFS.Mutability ¬ mutable;
unixFile: ROPE ¬ PFS.RopeFromPath[each.first];
cFile: CString ~ UXO[unixFile];
buf: UnixTypes.Stat;
bytes: INT;
res: RES ~ UnixSysCalls.LStat[cFile, @buf];
IF res # success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
SELECT errno
FROM
EPERM, ENOENT, ENXIO, EACCES, ENODEV, ENOTDIR, EISDIR => NULL; -- ignore things we can't look at
ENDCASE => {
message: ROPE ~ MakeMsg[selfName, "LStat failed (during enumeration) for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
};
IF buf.mode.fmt=lnk
THEN {
res: RES ~ UnixSysCalls.Stat[cFile, @buf];
IF res # success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
SELECT errno
FROM
EPERM, ENOENT, ENXIO, EACCES, ENODEV, ENOTDIR, EISDIR => bytes ¬ -1; -- ignore things we can't look at
ENDCASE => {
message: ROPE ~ MakeMsg[selfName, "Stat failed (during enumeration) for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
}
ELSE bytes ¬ buf.size;
attachedTo ¬ GetAttachment[cFile];
attachedTo ¬ ResolveAttachment[each.first, attachedTo];
}
ELSE bytes ¬ buf.size;
fileType ¬ PFSFileTypeFromUnixType[buf.mode.fmt];
uniqueID ¬ UIDFromMTime[buf.mtime];
UXR[cFile];
IF NOT proc[each.first, attachedTo, uniqueID, bytes, mutability, fileType] THEN EXIT;
ENDLOOP;
};
UXEnumerateForNames: PFSClass.EnumerateForNamesProc
-- [h: FSHandle, pattern: NAME, proc: NameProc] -- ~ {
dirPath: PATH;
listOfNames: LIST OF PATH ¬ NIL;
[dirPath~dirPath, listOfNames~listOfNames] ¬ EnumerateInner[h, pattern];
FOR each:
LIST
OF
PATH ¬ listOfNames, each.rest
WHILE each #
NIL
DO
IF NOT proc[each.first] THEN EXIT;
ENDLOOP;
};
EnumerateInner:
PROC [h: PFSClass.FSHandle, pattern:
PATH]
RETURNS [dirPath, unixDirPath:
PATH ¬
NIL, listOfNames:
LIST
OF
PATH ¬
NIL] ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
selfName: ROPE ~ "EnumerateInner";
patternPart: PATH;
[dirPath, patternPart] ¬ ParsePattern[pattern];
unixDirPath ¬ FixPathForUnix[dirPath];
{
dirRope: ROPE ~ PFS.RopeFromPath[unixDirPath];
dirString: CString ~ UXO[dirRope];
patternRope: ROPE ~ PFS.RopeFromPath[patternPart];
dirp: UnixDirectory.DirPtr ~ UnixDirectory.OpenDir[dirString];
errno: UnixErrno.Errno;
UXR[dirString];
IF dirp=NIL AND (errno ← UnixErrno.GetErrno[]) = $ENOENT THEN RETURN;
IF dirp=NIL THEN UnixFSPrivate.ReportFailure[errno, MakeMsg["EnumerateInner", "Directory open failed in EnumerateInner for ", dirRope]];
TRUSTED {
entryp: UnixDirectory.DirEntPtr ¬ UnixDirectory.ReadDir[dirp];
WHILE entryp #
NIL
DO
name: ROPE ¬ UnixDirectory.NameFromDirEntP[entryp];
IF
NOT Rope.Equal[name, ".",
TRUE]
AND
NOT Rope.Equal[name, "..",
TRUE]
THEN {
IF Rope.Match[patternRope, name,
TRUE]
THEN {
fullName: PATH ~ PFSNames.Cat[dirPath, PFS.PathFromRope[name]];
listOfNames ¬ CONS[fullName, listOfNames];
};
};
entryp ¬ UnixDirectory.ReadDir[dirp];
ENDLOOP;
};
[] ¬ UnixDirectory.CloseDir[dirp];
};
listOfNames ¬ SortNameList[listOfNames];
};
UXFileInfo: PFSClass.FileInfoProc
-- [h: FSHandle, file: PATH, wantedUniqueID: UniqueID] RETURNS [version: Version, attachedTo: PATH, bytes: INT, uniqueID: UniqueID, mutability: PFS.Mutability, fileType: PFS.FileType] -- ~ {
RETURN UXGetInfoInner[h, FixPathForUnix[file], wantedUniqueID];
};
UXGetInfoInner: PFSClass.FileInfoProc
-- [h: FSHandle, file: PATH, wantedUniqueID: UniqueID] RETURNS [version: Version, attachedTo: PATH, bytes: INT, uniqueID: UniqueID, mutability: PFS.Mutability, fileType: PFS.FileType] -- ~
TRUSTED {
data: FSData ¬ NARROW[h.data]; -- sanity check;
unixFile: ROPE ¬ PFS.RopeFromPath[file];
selfName: ROPE ~ "UXGetInfoInner";
buf: UnixTypes.Stat;
cFile: CString ~ UXO[unixFile];
res: RES ~ UnixSysCalls.LStat[cFile, @buf];
IF res # success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ Rope.Concat["Stat failed (in UXGetInfoInner) for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
IF buf.mode.fmt=lnk
THEN {
res: RES ~ UnixSysCalls.Stat[cFile, @buf];
IF res # success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
SELECT errno
FROM
EPERM, ENOENT, ENXIO, EACCES, ENODEV, ENOTDIR, EISDIR => bytes ¬ -1; -- ignore things we can't look at
ENDCASE => {
message: ROPE ~ MakeMsg[selfName, "Stat failed for ", unixFile];
UnixFSPrivate.ReportFailure[errno, message];
};
}
ELSE bytes ¬ buf.size;
attachedTo ¬ GetAttachment[cFile];
attachedTo ¬ ResolveAttachment[file, attachedTo];
}
ELSE {
bytes ¬ buf.size;
attachedTo ¬ NIL;
};
UXR[cFile];
fileType ¬ PFSFileTypeFromUnixType[buf.mode.fmt];
uniqueID ¬ UIDFromMTime[buf.mtime];
IF ( wantedUniqueID # PFS.nullUniqueID )
AND ( uniqueID # wantedUniqueID )
THEN PFSBackdoor.ProduceError[unknownUniqueID, "UniqueID wrong"];
mutability ¬ mutable;
version ¬ versionOfAllFiles;
fullFName ¬ file; -- for unix view, this is the fullFName
};
UXGetInfo: PFSClass.GetInfoProc
-- [h: FSHandle, file: OpenFile] RETURNS [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: PFS.Mutability, fileType: PFS.FileType] -- ~
TRUSTED {
data: REF UXData ~ NARROW[file.data];
fd: UnixTypes.FD ¬ data.fd;
buf: UnixTypes.Stat;
res: RES ~ UnixSysCalls.FStat[fd, @buf];
IF res # success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["UXGetInfo", "Stat failed for ", file.fullFName];
UnixFSPrivate.ReportFailure[errno, message];
};
RETURN[file.fullFName, file.attachedTo, file.uniqueID, buf.size, file.mutability, PFSFileTypeFromUnixType[buf.mode.fmt]];
};
UXRename: PFSClass.RenameProc
-- [h: FSHandle, fromFile: NAME, wantedUniqueID: UniqueID, toFile: NAME, keep: CARDINAL, proc: PFS.NameConfirmProc] RETURNS [done: BOOL ¬ FALSE] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check
fullName: PATH; uniqueID: PFS.UniqueID;
FailIfReadOnly[h, fromFile];
fromFile ¬ FixPathForUnix[fromFile]; -- PFS.Error if it's a pattern
toFile ¬ FixPathForUnix[toFile];
[uniqueID~uniqueID] ¬ UXGetInfoInner[h, fromFile, wantedUniqueID]; -- PFS.Error if wantedUniqueID not available
IF (proc =
NIL)
OR proc[fromFile, uniqueID]
THEN {
-- fromFile or toFile?
unixFromFile: ROPE ¬ PFS.RopeFromPath[fromFile];
unixToFile: ROPE ¬ PFS.RopeFromPath[toFile];
cFromFile: CString ~ UXO[unixFromFile];
cToFile: CString ~ UXO[unixToFile];
res: RES ¬ UnixSysCalls.Rename[cFromFile, cToFile];
UXR[cFromFile];
UXR[cToFile];
RETURN[res=success];
};
};
bufferBytes: CARDINAL ¬ 8192;
UXStore:
PUBLIC PFSClass.StoreProc
--
[h: FSHandle, file: NAME, wantedUniqueID: UniqueID, str: IO.STREAM, proc: PFS.StoreConfirmProc, createOptions: PFS.CreateOptions] -- ~ {
selfName: ROPE ~ "UXStore";
DoStore:
PROC [fd: UnixTypes.
FD] ~
TRUSTED {
buffer: REF TEXT;
TRUSTED {
ENABLE
UNWIND => {
IF buffer # NIL THEN RefText.ReleaseScratch[buffer];
};
buffer ¬ RefText.ObtainScratch[bufferBytes];
DO
bytesWritten: INT;
ptr: UnixTypes.CHARPtr ~ LOOPHOLE[LOOPHOLE[buffer, UnixTypes.CHARPtr] +SIZE[TEXT[0]]];
buffer.length ¬ IO.GetBlock[str, buffer, 0, buffer.maxLength];
IF buffer.length = 0 THEN EXIT;
bytesWritten ¬ UnixSysCalls.Write[fd, ptr, buffer.length];
IF bytesWritten # buffer.length
THEN {
IF bytesWritten < 0
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "Write failed for", file];
UnixFSPrivate.ReportFailure[errno, message];
}
ELSE PFSBackdoor.ProduceError[inconsistent, "Invalid assumption about UnixSysCalls.Write in Store"];
};
ENDLOOP;
{
res: RES ~ UnixSysCalls.FSync[fd];
errno: UnixErrno.Errno;
IF res#success AND (errno ← UnixErrno.GetErrno[]) # EBADF THEN UnixFSPrivate.ReportFailure[errno, MakeMsg[selfName, "File system full? FSync failed for", file]];
};
RefText.ReleaseScratch[buffer]; buffer ¬ NIL;
};
};
openFile: PFSClass.OpenFile ~ UXOpen[h, file, wantedUniqueID, create, FALSE, PFS.tUnspecified, createOptions];
data: REF UXData ~ NARROW[openFile.data];
fd: UnixTypes.FD ~ data.fd;
IF proc # NIL THEN [] ¬ proc[openFile.fullFName];
DoStore[fd];
{
Preserve "Execute" bits in Unix-based views
This is not pleasant...
ENABLE PFS.Error => CONTINUE;
oldMode: ROPE ¬ PFS.GetClientProperty[PFS.OpenFileFromStream[str], "UnixMode"];
newMode: ROPE ¬ UXGetClientProperty[h, openFile, "UnixMode"];
IF oldMode #
NIL
AND newMode #
NIL
THEN {
oldModeBits: CARD16 ~ Convert.CardFromRope[oldMode];
newModeBits: CARD16 ~ Convert.CardFromRope[newMode];
newerModeBits: CARD16 ~
Basics16.BITOR[newModeBits, Basics16.BITAND[oldModeBits, 111B]];
newerMode: ROPE ~ Convert.RopeFromCard[newerModeBits, 8];
UXSetClientProperty[h, openFile, "UnixMode", newerMode];
};
};
UXClose[h, openFile, FALSE];
};
UXRetrieve: PFSClass.RetrieveProc ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
PFSBackdoor.ProduceError[notImplemented, "Retrieve not yet implemented for -ux view"];
};
NotYetImpl: ERROR ~ CODE;
UXLookupName: PFSClass.LookupNameProc
-- [h: FSHandle, file: NAME] RETURNS[NAME] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
success: BOOL ¬ TRUE;
[] ¬ UXGetInfoInner[h, FixPathForUnix[file], PFS.nullUniqueID ! PFS.Error => IF error.code = $unknownFile THEN {success ¬ FALSE; CONTINUE} ELSE REJECT ];
IF success THEN RETURN[file] ELSE RETURN[NIL];
};
UXCopy: PFSClass.CopyProc
-- [h: FSHandle, fromFile: NAME, wantedUniqueID: UniqueID, toFile: NAME, keep: CARDINAL, proc: PFS.NameConfirmProc] RETURNS [done: BOOL ¬ FALSE] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
done ¬ FALSE; -- let the higher level do the copying
};
UXSetAttributes: PFSClass.SetAttributesProc
-- [h: FSHandle, file: OpenFile, attributes: PFS.CreateOptions] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
PFSBackdoor.ProduceError[notImplemented, "SetAttributes not implemented in UX view"];
};
UXSetByteCountAndUniqueID: PFSClass.SetByteCountAndUniqueIDProc
-- [h: FSHandle, file: OpenFile, bytes:
INT, uniqueID:
PFS.UniqueID
] -- ~ {
data: FSData ¬ NARROW[h.data]; -- sanity check;
fd: UnixTypes.FD ¬ NARROW[file.data, REF UXData].fd;
IF bytes>=0
THEN {
res: RES ~ UnixSysCalls.FTruncate[fd, bytes];
IF res#success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["UXSetByteCountAndUniqueID", "failed to change byteCount of", file];
UnixFSPrivate.ReportFailure[errno, message]
};
};
IF wantedUniqueID.egmt.time # BasicTime.nullGMT THEN TRUSTED {
bah! there's no string name available for the UTimes.
mTime: MTime ~ MTimeFromGMT[wantedUniqueID.egmt.time];
modTimeVal: UnixSysCalls.AccModTimes ¬
[accessTime: [sec: mTime, usec: 0], modTime: [sec: mTime, usec: 0]];
res: RES ~ UnixSysCalls.UTimes[stringName, @modTimeVal];
};
};
UXGetClientProperty: PFSClass.GetClientPropertyProc
-- [h: FSHandle, file: OpenFile, propertyName: ROPE] RETURNS [propertyValue: ROPE] -- ~
TRUSTED {
data: FSData ¬ NARROW[h.data]; -- sanity check;
fd: UnixTypes.FD ¬ NARROW[file.data, REF UXData].fd;
buf: UnixTypes.Stat;
res: RES ~ UnixSysCalls.FStat[fd, @buf];
IF res#success THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["UXGetClientProperty", "Stat failed for", file];
UnixFSPrivate.ReportFailure[errno, message];
};
SELECT
TRUE
FROM
Rope.Equal[propertyName, "UnixMode"] => {
RETURN[Convert.RopeFromCard[LOOPHOLE[buf.mode, CARD16], 8]];
};
Rope.Equal[propertyName, "UnixOwner"] => {
RETURN[Convert.RopeFromCard[buf.uid]];
};
Rope.Equal[propertyName, "UnixGroup"] => {
RETURN[Convert.RopeFromCard[buf.gid]];
};
ENDCASE => RETURN[NIL];
};
UXSetClientProperty:
PUBLIC PFSClass.SetClientPropertyProc
--
[h: FSHandle, file: OpenFile, propertyName: ROPE, propertyValue: ROPE] -- = {
data: FSData ¬ NARROW[h.data]; -- sanity check;
fd: UnixTypes.FD ¬ NARROW[file.data, REF UXData].fd;
SELECT
TRUE
FROM
Rope.Equal[propertyName, "UnixMode"] => {
cardMode: CARD16 ~ Convert.CardFromRope[propertyValue, 8];
res: RES ~ UnixSysCalls.FChMod[fd, LOOPHOLE[cardMode]];
};
Rope.Equal[propertyName, "UnixOwner"] => {
PFSBackdoor.ProduceError[notImplemented, "The UnixOwner cannot be changed in the UX view"];
};
Rope.Equal[propertyName, "UnixGroup"] => {
PFSBackdoor.ProduceError[notImplemented, "The UnixGroup cannot be changed in the UX view"];
};
ENDCASE => PFSBackdoor.ProduceError[notImplemented, Rope.Concat[propertyName, " is not an implemented property in the UX view"]];
};
UXEnumerateClientProperties: PFSClass.EnumerateClientPropertiesProc
-- [h: FSHandle, file: OpenFile, proc: PFS.PropProc] -- ~
TRUSTED {
data: FSData ¬ NARROW[h.data]; -- sanity check;
fd: UnixTypes.FD ¬ NARROW[file.data, REF UXData].fd;
buf: UnixTypes.Stat;
res: RES ~ UnixSysCalls.FStat[fd, @buf];
IF NOT proc["UnixGroup", Convert.RopeFromCard[buf.gid]] THEN RETURN;
IF NOT proc["UnixMode", Convert.RopeFromCard[LOOPHOLE[buf.mode, CARD16], 8]] THEN RETURN;
[] ¬ proc["UnixOwner", Convert.RopeFromCard[buf.uid]];
};
UXRead:
UNSAFE
PROC [h: PFSClass.FSHandle, file: PFSClass.OpenFile, filePosition, nBytes:
CARD, toPtr:
LONG
POINTER, toStart:
CARD]
RETURNS [bytesRead:
INT] ~ {
fs: FSData ~ NARROW[h.data]; -- sanity check
data: REF UXData ~ NARROW[file.data];
fd: UnixTypes.FD ¬ data.fd;
failure: ROPE;
errno: UnixErrno.Errno;
selfName: ROPE ~ "UXRead";
DoRead:
ENTRY
PROC [lock:
REF UXData]
RETURNS [failure:
ROPE ¬
NIL, errno: UnixErrno.Errno ¬ ok] ~
TRUSTED {
ENABLE UNWIND => NULL;
fdIndex: PFSBackdoor.FDIndex ~ data.fdIndex;
data.fdIndex ¬ PFSBackdoor.unreliableIndex;
IF filePosition # fdIndex
THEN {
res: INT ~ UnixSysCalls.LSeek[fd, filePosition, set];
IF res < 0
THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "LSeek failed for", file];
RETURN[failure, errno];
};
};
bytesRead ¬ UnixSysCalls.Read[d~fd, buf~LOOPHOLE[toPtr+toStart], nBytes~nBytes];
IF bytesRead < 0
THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "Read failed for", file];
RETURN[failure, errno];
};
IF fdIndex # PFSBackdoor.unreliableIndex THEN data.fdIndex ¬ filePosition+bytesRead;
};
bytesRead ¬ 0;
[failure, errno] ¬ DoRead[data];
IF failure # NIL THEN UnixFSPrivate.ReportFailure[errno, failure];
};
UXWrite:
PROC [h: PFSClass.FSHandle, file: PFSClass.OpenFile, filePosition, nBytes:
CARD, fromPtr:
LONG
POINTER, fromStart:
CARD]
RETURNS [bytesWritten:
INT] ~ {
fs: FSData ¬ NARROW[h.data]; -- sanity check
data: REF UXData ~ NARROW[file.data];
fd: UnixTypes.FD ~ data.fd;
failure: ROPE;
errno: UnixErrno.Errno;
selfName: ROPE ~ "UXWrite";
DoWrite:
ENTRY PROC [lock:
REF UXData]
RETURNS [failure:
ROPE ¬
NIL, errno: UnixErrno.Errno ¬ ok] ~ {
ENABLE UNWIND => NULL;
fdIndex: PFSBackdoor.FDIndex ~ data.fdIndex;
data.fdIndex ¬ PFSBackdoor.unreliableIndex;
IF filePosition # fdIndex
THEN {
res: INT ~ UnixSysCalls.LSeek[fd, filePosition, set];
IF res < 0
THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "LSeek failed for", file];
RETURN[failure, errno];
};
};
bytesWritten ¬ UnixSysCalls.Write[d~fd, buf~LOOPHOLE[fromPtr+fromStart], nBytes~nBytes];
IF bytesWritten < 0
THEN {
errno ¬ UnixErrno.GetErrno[];
failure ¬ MakeMsg[selfName, "Write failed for", file];
RETURN[failure, errno];
};
IF fdIndex # PFSBackdoor.unreliableIndex THEN data.fdIndex ¬ filePosition+bytesWritten;
};
bytesWritten ¬ 0;
[failure, errno] ¬ DoWrite[data];
IF failure # NIL THEN UnixFSPrivate.ReportFailure[errno, failure];
};
mutable: UnixTypes.Permission ~ [read: true, write: true, exec: false];
immutable: UnixTypes.Permission ~ [read: true, write: false, exec: false];
mutableMode: UnixTypes.Mode ~ [owner: mutable, group: mutable, others: mutable];
immutableMode: UnixTypes.Mode ~ [owner: immutable, group: immutable, others: immutable];
immutableMode: UnixTypes.Mode ~ [owner: mutable, group: mutable, others: mutable]; -- because, temporarily, the DF software doesn't make the necessary files mutable, and the compiler can't deal with immutable files
UXData: TYPE ~ PFSBackdoor.UXData;
maxAttachmentLength: CARD ~ 1000;
GetAttachment:
PROC [fHandle: CString]
RETURNS [attachedTo:
PATH] ~
TRUSTED {
buf: CString ~ UXStrings.ObtainScratch[1000];
linkSize: INT ~ UnixSysCalls.ReadLink[fHandle, buf, maxAttachmentLength];
IF linkSize<0
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["GetAttachment", "ReadLink failed for", UXStrings.ToRope[fHandle]];
UnixFSPrivate.ReportFailure[errno, message];
};
attachedTo ¬ PFS.PathFromRope[UXStrings.ToRope[buf, linkSize]];
UXR[buf];
};
ResolveAttachment:
PROC [attachment:
PATH, attachedTo:
PATH]
RETURNS [
PATH] ~ {
{
absolute: PATH ~ IF PFSNames.IsAbsolute[attachedTo] THEN attachedTo ELSE PFSNames.Cat[PFSNames.Directory[attachment], attachedTo];
RETURN[ PFSNames.ReplaceComponent[absolute, 0, ux0Component] ];
};
};
UXOpen: PFSClass.OpenProc
--
PROCEDURE [h: FSHandle, file: NAME, wantedUniqueID: UniqueID, access: PFS.AccessOptions, checkFileType: BOOL, fileType: PFS.FileType, createOptions: PFS.CreateOptions] RETURNS [OpenFile] -- ~ {
openFile: PFSClass.OpenFile ¬ NEW[PFSClass.OpenFileObject ¬
[fs~h, uniqueID~PFS.nullUniqueID, access~access, state~open, bytes: 0, mutability: mutable, fileType: PFS.tUnspecified]];
fileName: PATH ¬ FixPathForUnix[file];
unixFileName: ROPE ~ PFS.RopeFromPath[fileName];
fHandle: CString ¬ UXS[unixFileName];
fd: UnixTypes.FD;
mode: UnixTypes.Mode;
selfName: ROPE ~ "UXOpen";
SELECT access
FROM
read =>
TRUSTED {
buf: UnixTypes.Stat;
fd ¬ UnixSysCalls.Open[path~fHandle,
flags~[access~RDONLY],
mode~immutableMode
];
IF fd=error
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg["UXOpen", "Open failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
openFile.data ¬ NEW[UXData ¬ [fd~fd, fdIndex~0]];
};
write, append =>
TRUSTED {
buf: UnixTypes.Stat;
res: RES ¬ UnixSysCalls.LStat[fHandle, @buf];
IF res=success
THEN {
IF buf.mode.fmt=lnk
THEN {
PFSBackdoor.ProduceError[accessDenied, MakeMsg[selfName, "Can't open an attachment for writing or appending", unixFileName]];
};
};
fd ¬ UnixSysCalls.Open[path~fHandle,
flags~[excl~false, trunc~false, creat~true, access~RDWR],
mode~IF createOptions.mutability=mutable THEN mutableMode ELSE immutableMode
];
IF fd=error
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "Open failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
IF access=write THEN openFile.data ¬ NEW[UXData ¬ [fd~fd, fdIndex~0]] ELSE openFile.data ¬ NEW[UXData ¬ [fd]];
};
create =>
TRUSTED {
shortName: ROPE ~ PFSNames.ComponentRope[PFSNames.ShortName[fileName]];
backupLastComp: PFSNames.Component ~ [name: [base: shortName.Concat["~"], start: 0, len: shortName.Length[]+1]];
backupName: PATH ~ PFSNames.ReplaceShortName[fileName, backupLastComp];
backupUnixName: ROPE ~ PFS.RopeFromPath[backupName];
tempLastCompName: ROPE ~ CreateTempName[shortName];
tempLastComp: PFSNames.Component ~ [name: [base: tempLastCompName, start: 0, len: tempLastCompName.Length[]]];
tempName: PATH ~ PFSNames.ReplaceShortName[fileName, tempLastComp];
tempUnixName: ROPE ~ PFS.RopeFromPath[tempName];
tempFHandle: CString ~ UXS[tempUnixName];
cBackupName: CString ~ UXO[backupUnixName];
buf: UnixTypes.Stat;
res: RES ¬ UnixSysCalls.LStat[fHandle, @buf];
IF res#success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF errno #
ENOENT
THEN {
message: ROPE ~ MakeMsg[selfName, "Stat failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
};
IF buf.mode.fmt=dir
THEN {
message: ROPE ~ Rope.Cat["Creating a file with the same name (", unixFileName, " ) as an existing directory is not allowd."];
PFSBackdoor.ProduceError[accessDenied, message];
};
BEGIN
ok: BOOL ¬ FALSE;
fd: UnixTypes.FD ¬ UnixSysCalls.Open[path~fHandle,
flags~[access~RDONLY],
mode~immutableMode
];
IF fd#error
THEN {
res ¬ UnixSysCalls.FStat[fd, @buf];
IF res#success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "FStat failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
res ¬ UnixSysCalls.Close[fd];
ok ¬ TRUE;
};
IF ok
AND (createOptions.setMutability=inherit)
THEN mode ¬ buf.mode
ELSE {
SELECT
TRUE
FROM
createOptions.mutability=mutable => mode ¬ mutableMode;
ENDCASE => mode ¬ immutableMode;
IF ok
THEN {
-- fold in execute bits from buf.mode
mode.owner.exec ¬ buf.mode.owner.exec;
mode.group.exec ¬ buf.mode.group.exec;
mode.others.exec ¬ buf.mode.others.exec;
};
};
END;
IF createOptions.keep # 1
THEN {
res ¬ UnixSysCalls.Rename[fHandle, cBackupName];
UXR[cBackupName];
IF res#success
THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF err #
ENOENT
THEN {
message: ROPE ~ MakeMsg[selfName, Rope.Cat["Rename failed for ", unixFileName, " to"], backupUnixName];
UnixFSPrivate.ReportFailure[err, message];
};
};
}
ELSE {
UXR[cBackupName];
res ¬ UnixSysCalls.Unlink[fHandle];
IF res#success
THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF err #
ENOENT
THEN {
message: ROPE ~ MakeMsg[selfName, "Unlink failed for", unixFileName];
UnixFSPrivate.ReportFailure[err, message];
};
};
};
fd ¬ UnixSysCalls.Open[path~tempFHandle,
flags~[excl~true, creat~true, access~WRONLY],
mode~mode
];
IF fd=error
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "Open failed for", tempUnixName];
UnixFSPrivate.ReportFailure[errno, message];
};
openFile.data ¬ NEW[UXData ¬ [fd, tempFHandle, wantedUniqueID, fHandle, 0]];
fHandle ¬ tempFHandle;
};
ENDCASE => ERROR;
TRUSTED {
buf: UnixTypes.Stat;
res: RES ~ UnixSysCalls.Stat[fHandle, @buf];
IF res # success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "Stat failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
openFile.bytes ¬ buf.size;
IF openFile.uniqueID = PFS.nullUniqueID THEN openFile.uniqueID ¬ UIDFromMTime[buf.mtime];
};
{
res: RES ~ UnixSysCallExtensions.SetGetBlocking[fd, allData];
IF res # success
THEN {
errno: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, "SetGetBlocking failed for", unixFileName];
UnixFSPrivate.ReportFailure[errno, message];
};
};
openFile.fullFName ¬ fileName;
RETURN [openFile];
};
UXAttach: PFSClass.AttachProc
--
PROCEDURE [h: FSHandle, file: PATH, to: PATH, keep: CARDINAL, wantedUniqueID: UniqueID, remoteCheck: BOOL ¬ TRUE] RETURNS [toFName: PATH] -- ~
TRUSTED {
selfName: ROPE ~ "UXAttach";
fileName: PATH ¬ FixPathForUnix[file];
unixFileName: ROPE ~ PFS.RopeFromPath[fileName];
fHandle: CString ¬ UXS[unixFileName];
shortName: ROPE ~ PFSNames.ComponentRope[PFSNames.ShortName[fileName]];
backupLastComp: PFSNames.Component ~ [name: [base: shortName.Concat["~"], start: 0, len: shortName.Length[]+1]];
backupName: PATH ~ PFSNames.ReplaceShortName[fileName, backupLastComp];
backupUnixName: ROPE ~ PFS.RopeFromPath[backupName];
cBackupName: CString ~ UXO[backupUnixName];
buf: UnixTypes.Stat;
res: RES ¬ UnixSysCalls.LStat[fHandle, @buf];
IF res#success
THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF err #
ENOENT
THEN {
message: ROPE ~ MakeMsg[selfName, "Stat failed for", unixFileName];
UnixFSPrivate.ReportFailure[err, message];
};
};
IF buf.mode.fmt=dir
THEN {
message: ROPE ~ Rope.Cat["Creating a file with the same name (", unixFileName, " ) as an existing directory is not allowed."];
PFSBackdoor.ProduceError[accessDenied, message];
};
res ¬ UnixSysCalls.Rename[fHandle, cBackupName];
UXR[cBackupName];
IF res#success
THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
IF err #
ENOENT
THEN {
message: ROPE ~ MakeMsg[selfName, Rope.Cat["Rename failed for ", unixFileName, " to"], backupUnixName];
UnixFSPrivate.ReportFailure[err, message];
};
};
{
toFileName: ROPE ~ PFS.PFSNameToUnixName[to];
toFHandle: CString ~ UXO[toFileName];
IF toFileName=NIL THEN PFSBackdoor.ProduceError[unknownFile, Rope.Cat["Couldn't deduce a unix name for ", PFS.RopeFromPath[to], " in UXAttach."]];
res ¬ UnixSysCalls.SymLink[name1~toFHandle, name2~fHandle];
IF res#success
THEN {
err: UnixErrno.Errno ~ UnixErrno.GetErrno[];
message: ROPE ~ MakeMsg[selfName, Rope.Cat["SymLink failed from ", unixFileName, " to"], toFileName];
UnixFSPrivate.ReportFailure[err, message];
};
FixUID[fHandle, wantedUniqueID]; Unix won't let me set the times on a symlink.
UXR[toFHandle];
};
RETURN[file];
};
}.