A file which has just been created by Open or FOpen is exclusively locked and therfore cannot be accessed by anyone else. DirOpus and Workbench do not access the file but its parent directory. The directory entry has already been saved and can be accessed.
So if you Lock() the directory and read all entries with ExNext or ExAll or whatever OS 4 now offers, you'll see the file's properties.
Neither of both can work since they need an additional lock (internally) which isn't possible for files opened with an exclusive mode like MODE_NEWFILE, you have to use IDOS->ExamineObjectTags(EX_FileHandleInput, ...) instead. Or open the file in shared mode (MODE_READWRITE). Or open it in exclusive mode but change it to shared with IDOS->ChangeMode(CHANGE_FH, file, SHARED_LOCK) after it was opened successfully.
// @ToDo: Should probably parse EX_MatchString before using it, even without wildcards context = IDOS->ObtainDirContextTags((STRPTR)EX_StringNameInput,parent, EX_MatchString,(STRPTR)IDOS->FilePart(path), TAG_END);
if(context!=NULL) { data = IDOS->ExamineDir(context);
if(data!=NULL) { if(EXD_IS_LINK(data)) { if(EXD_IS_SOFTLINK(data)) { result = PATH_IS_SOFTLINK; } else { result = PATH_IS_HARDLINK; } } else { if(EXD_IS_FILE(data)) { result = PATH_IS_FILE; } else if(EXD_IS_DIRECTORY(data)) { result = PATH_IS_DIRECTORY; } else { result = PATH_IS_UNKNOWN; } } IDOS->FreeDosObject(DOS_EXAMINEDATA,data); } else // can't obtain examinedata for path { result = PATH_NON_EXISTANT; }
IDOS->ReleaseDirContext(context); } else // No context on parent { result = PATH_NON_EXISTANT; }
If you read the docs for ExamineObject() you will see that passing a string name implies a Lock(). That's why it fails.
If you already have the exclusive lock, just call ExamineObject() with EX_FileLockInput, that will work on exclusive locks as it is passed to the filesystem directly.
If you want to know the parent directory of the exclusive lock, then call ParentDir() of the exclusive lock, and don't forget to unlock what ParentDir() returns.
If you look carefully you'll notice that I was asking for an air tight way of solving the problem. I was not asking what creates a lock or not, that information is available in the autodocs.
Anyway, the ObtainDirContextTags()+ExamineDir() combo works, I just wanted to know if there was a better way to solve the problem since it's not very elegant. Way too many calls, buffers and parsing for what should be a simple task. But i guess there isn't one (yet, nudge nudge).
The issue is that an exclusive lock implies that it is exclusive, and that means no-one else is allowed to futz-around with it.
The best you can do is get a listing of it with a directory scan of the parent directory if you do not have access to the original lock itself.
The filesystem is serious about exclusivity, and if it's not your lock, you're out of luck trying to force access from another client.
The fact that you have limited access to these is by design and not actually a fault.
BTW: You really should first attempt a normal ExamineObject() with the string name before falling through to this fallback method of scanning this entire directory, only if you hit a ERROR_OBJECT_IN_USE error, it will be a hell of a lot faster.
True, here's what I use now in case someone else wants to do similar stuff.
/*
** Check if a path exists and what type it is.
** PATH_NON_EXISTANT
** PATH_IS_SOFTLINK
** PATH_IS_HARDLINK
** PATH_IS_FILE
** PATH_IS_DIRECTORY
** PATH_IS_UNKNOWN
** Passing null is ok
*/
uint32 PathType(STRPTR path)
{
uint32 result;
struct ExamineData *data;
if(path==NULL)
{
result = PATH_NON_EXISTANT;
}
else
{
data = IDOS->ExamineObjectTags(EX_StringNameInput, path, TAG_END);
if(data!=NULL)
{
result = ExaminePathType(data);
IDOS->FreeDosObject(DOS_EXAMINEDATA,data);
}
else
{
int32 ioerr = IDOS->IoErr();
if(ioerr==ERROR_OBJECT_IN_USE)
{ // Fallback to expensive examinedir function
result = PathTypeFallback(path);
}
else
{
result = PATH_NON_EXISTANT;
}
}
}
return result;
}
/*
** Local function, expensive ObtainDirContext()+ExamineDir() combo used if
** the requested path is in use and cannot be locked
*/
uint32 PathTypeFallback(STRPTR path)
{
uint32 result;
STRPTR strptr;
char parent[1024];
char parsed[2050];
APTR context;
struct ExamineData *data;
// It's crap that I have to parse a pattern for this simple function.
// Why can't DOS just have a simple ExaminePath() function that doesn't use locks?
IDOS->ParsePatternNoCase((STRPTR)IDOS->FilePart(path), parsed, 2050);
context = IDOS->ObtainDirContextTags((STRPTR)EX_StringNameInput,parent,
EX_MatchString,parsed,
TAG_END);
if(context!=NULL)
{
data = IDOS->ExamineDir(context);
if(data!=NULL)
{
result = ExaminePathType(data);
// No need to free ExamineData here, ReleaseDirContext does that for you
}
else // can't obtain examinedata for path
{
result = PATH_NON_EXISTANT;
}
IDOS->ReleaseDirContext(context);
}
else // No context on parent
{
result = PATH_NON_EXISTANT;
}
return result;
}
/*
** Check if a path exists and what type it is
** PATH_NON_EXISTANT
** PATH_IS_SOFTLINK
** PATH_IS_HARDLINK
** PATH_IS_FILE
** PATH_IS_DIRECTORY
** PATH_IS_UNKNOWN
** You must free the ExamineData yourself, passing NULL is ok
*/
uint32 ExaminePathType(struct ExamineData *data)
{
uint32 result;
if(data==NULL)
{
result = PATH_NON_EXISTANT;
}
else
{
if(EXD_IS_LINK(data))
{
if(EXD_IS_SOFTLINK(data))
{
result = PATH_IS_SOFTLINK;
}
else
{
result = PATH_IS_HARDLINK;
}
}
else
{
if(EXD_IS_FILE(data))
{
result = PATH_IS_FILE;
}
else if(EXD_IS_DIRECTORY(data))
{
result = PATH_IS_DIRECTORY;
}
else
{
result = PATH_IS_UNKNOWN;
}
}
}
The FreeDosObject() of the data block from ExamineDir() is not appropriate in your PathTypeFallback() function.
The data blocks are allocated from a memory pool and stored in the internal list and the whole lot is freed when you call ReleaseDirContext().
However, as you are not looping here, you'd probably never know anything was up.
Because ExamineDir() uses a memory pool and can reuse the nodes on subsequent calls, if you were looping, you'd likely be visited by the reaper for putting it back into the pool but leaving it still in the list without calling IExec->Remove() first. However, seeing you only call ExamineDir() once, you got away with it.
Only ExamineObject() requires you free the data blocks individually when you're done with it.
I've just updated the autodocs to make this point much clearer.
Btw on the topic of things in the autodoc, in dos.doc there is a code example that says:
if( EXD_IS_SOFTLINK(dat) ) { linkedobj=IDOS->ExamineObject(EX_StringNameInput,dat->Link,TAG_END); .... /* Use linkedobj data then free it. - See; ExamineObject() */