# Aros/Developer/Docs/Libraries/DOS

* Navbar for the Aros wikibook
* Aros User
* Aros User Docs
* Aros User FAQs
* Aros User Applications
* Aros User DOS Shell
* Aros/User/AmigaLegacy
* Aros Dev Docs
* Aros Developer Docs
* Porting Software from AmigaOS/SDL
* For Zune Beginners
* Zune .MUI Classes
* For SDL Beginners
* Aros Developer BuildSystem
* Specific platforms
* Aros x86 Complete System HCL
* Aros x86 Audio/Video Support
* Aros x86 Network Support
* Aros Intel AMD x86 Installing
* Aros Storage Support IDE SATA etc
* Aros Poseidon USB Support
* x86-64 Support
* Motorola 68k Amiga Support
* Linux and FreeBSD Support
* Windows Mingw and MacOSX Support
* Android Support
* Arm Raspberry Pi Support
* PPC Power Architecture
* misc
* Aros Public License

## Introduction

A file-system controls the way files and folders are laid out on the disk, and handles all the details of creating, reading, writing and deleting those files. Without a file-system, your disk (or floppy, CD-ROM, USB key or SD card) is just a mass of raw unstructured data.
When you access a file, a file handler is created each time.
* dos.h contains Open, Close, Seek, Read, Write, etc.
* stdio.h contains fopen, fclose, fseek, fread, fwrite, etc.
All file routines go through dos.library even if you use fopen, those "non native" library functions are just some implementation at standardising file access with c, if you follow the code they will use Aros operation systems calls at the end.
Same thing for malloc etc. they end up in exec.library AllocMem/AllocVec/AllocPooled or what ever.
One thing to note for is when you call some operation system function your code is actually running the OS-code, there is no behind-the-wall-supercode doing it for you. Your apps PC counter goes through the calls.
If you use standard c library calls then you will have to link it in the binary or the binary has to open the clib at runtime. You would not want to have clib when coding parts of the operation system (user apps can do what they want). If one is used, c library calls on operating system calls then it gets just messy (clib code would convert the c standard to native calls, etc.)
When you go to generic routines from a compiler's standard c library or a library of posix type functions, you usually see specific limitations when it comes to AROS (or Amiga) system stuff. They can have incompatible ideas of file permission bits, incompatible ideas of how to handle multiple root volumes or when looking up things that are actually "multi-path assigns" or use file notes which Amiga native app writers took advantage of. The standard libs don't understand the relationships between programs and icons in the Amiga workbench environment.
Standard libs programming can be oblivious to user frustration at getting a bunch of "insert volume foo: requesters", popping up on their screen or worse, on the workbench screen when the app is running on its own screen blocking the view of those requesters, because they were written with a unix shell semantic in mind, where error handling was done differently.
It is probably safe to stick to the AROS native functions names, even though a standard call is internally remapped.
Alloc a large chunk of memory (Get size with AvailMem and flag MEMF_LARGEST and allocate with AllocDosObject (used to be AllocMem), filesize get with Lock and Examine (ExAll)), open the file with Open and read in the data with Read, Open the source file with Open and save data with Write. For total RAM use MEMF_TOTAL.
An easy way to check for existing files is: get data of one directory with Examine/ExNext/ExAll and try for every file name an Lock in the destination directory. To change a directory/create a directory CurrentDir and CreateDir are useful. The functions Rename and DeleteFile will be useful.
The only fields which are referred to by external code are fh_Device and fh_Unit. They are used for sending direct IOFS requests. In fact fh_Size and fh_Flags can also be aliased to fh_Funcs and fh_Func2, but this would affect m68k port if they decide to implement these callbacks for some reason. It might be better if fields that might hold pointers were upgraded to SIPRs rather than APTRs. This will give better source-level compatibility with AmigaOS code, avoiding compiler warnings for example.
Previous implementation, for non AROS_DOS_PACKET case field fh_Size was at offset 24 and field fh_Flags was at offset 28. After your change, field fh_Size is at offset 44 and field fh_Flags is at offset 48. Above the FH structure before and after the change for reference.

## Volumes

Info function gets information about a volume in the system.
* lock—a lock on any file on the volume for which information should be supplied
* parameterBlock—pointer to an InfoData structure
and results with != 0 if the operation was successful, == 0 if the operation was not successful Boolean indicating success or failure. If success (!= 0) the 'parameterBlock' is filled with information about the volume.
/* returned by Info, must be on a 4 byte boundary */ struct InfoData {
}; /* InfoData */
to find the address of the CON:/RAW: window the shell is running in; documented very scantily in the AmigaDOS manuals is the fact that if you send a DiskInfo packet to your console handler, the id_VolumeNode field in the InfoData structure holds the desired window address.

## Files
### Open Close Files

Before you can do anything with a file you have to ask the operating system to "open it". This is because AROS needs to know what you are going to do with the file. Are you going to create a new file (MODE_NEWFILE) or are you going to work with an already opened file (MODE_OLDFILE). You open a file by calling the function Open:
For example, if you want to create a new file called "Highscore.dat" you write:
Once you have finished reading/writing the file you need to close it. You do it by calling the function Close:
Close all files you have opened!
The use of any global FILE pointers are asking for trouble, so its best not to use them. Just declare their use in each function parsing if needed. global variables are a bad idea and will likely get you in trouble in the future if you get into the habit of using them without thinking about it. Make sure you understand variable scopes and their consequences before going on.

### Read, Write and Seek Files

Once you have successfully opened a file you can start to read or write. AmigaDOS(TM) and AROS consider files to be a stream of bytes, and when you read or write something you need to specify how many bytes you want to read or write.
When you what to write to a file you use the function Write:
And to Seek
See here also.

### Lock

flock calls DupLockFromFH with filehandle that is opened in MODE_NEWFILE mode. It is not allowed to be duplicated. Can be fixed by replacing O_CREAT = NEWFILE with following: first create the file with MODE_NEWFILE, close it and then re-open it with MODE_OLDFILE. ChangeMode is also possibility but it may not supported by all filesystems or it can be broken (UAE fs implementation CHANGE_FH variant was buggy until about two years ago. It is really rarely used in AOS programs).
Actually flock calls NameFromFH which itself calls DupLockFromFH. So the first question is if NameFromFH should fail on a handle opened with MODE_NEWFILE ? Does it on OS3.x ? NameFromFH must work with MODE_NEWFILE opened filehandles. It is also mentioned in Guru Book that says AOS NameFromFH internally uses ExamineFH and ParentOfFH to get the name string because DupLockFromFH can't work if handle is MODE_NEWFILE.
It looks like it does something like this:
* parentlock = ParentOfFH(fh);
* Path string = NameFromLock(parentlock);
* Unlock(parentlock)
* Append file name to path string from ExamineFH(fh)
What are the technical reasons for not making DupLockFromFH work on MODE_NEWFILE files? IIRC it's because MODE_NEWFILE uses an exclusive lock (on all filesystems, not just on AmberRAM).
Would there be any issue if we just made it work? UAE's filesystem would have to be reworked, but that's all about it, no? Wouldn't it break compatibility with AOS m68k filesystems? Yes. Exclusive lock duplication (which includes MODE_NEWFILE filehandles that have internal exclusive lock) attempts must fail. It is a documented behavior. NameFromFH is now rewritten and works with exclusive filehandles.
SameLock
dol_Name is a BCPL pointer to a BCPL string so you can't just print it with printf. Note that you shouldn't do so anyway, not while you're holding a lock on the dos list. Better to copy the information you need into your own buffers and then print it after unlocking the dos list.
You should never perform any DOS I/O with the doslist locked. Create an exec list of what you want, release the doslist, then print the list nodes out....
Assign steps thought the DOS list and displays all devices in the list. If you want to filter only those devices which have already been activated, you would have to write a new program which steps through the list (LockDosList / NextDosEntry) and check if dol_Task is filled (OS4 renamed it to dol_Port). The name is dol_Task (see dos/dosextens.h). It contains a pointer to a MsgPort. But note that this can change at any time. A device can be mounted but not activated at once. It is activated automatically when the user accesses it for the first time. This applies for example to RAM, SER, PAR and PRT.
Volume name
To translate the WBArg into a full path you can do:
However, it's usually more efficient to:

### Examine Files or Directories

The newest method require ExAll which replaced Examine and ExNext. As ExAll scans a directory (having been locked earlier), it an ExAllData structure into the buffer for each entry.
Examples - Find,
The old method required Examine. You give the function a pointer to a "lock" on the file or directory you want to examine, and it will set up a FileInfoBlock structure for you.
Is it safe to use memory allocated with AllocMem with the dos function Examine and use fib_SIZEOF as the size, or is it better to use AllocDosObject instead? AllocMem is safe for all currently existing AmigaOS versions but AllocDosObject is better for future compatibility. Note that AllocDosObject does not exist in Kickstart 1.3 and below, so for these OS versions you need to use AllocMem.
On PowerPC stack is always long-word-aligned and you are not forced to use AllocDosObject or AllocMem. If coding for AmigaOS/68k you must allocate long word-aligned FIB.
FileInfoBlock is not a system structure. It is just pointer to a buffer used to retrieve data. If you still insist FIB must be allocated using AllocDosObject (why?) you should also use system routines to allocate space for names passed to Lock, Open, etc.
As you see this idea to use AllocDosObject to allocate FIB a bad idea. For other structures like ExAllControl it is needed because structure must be initialized properly before passed to dos.library. The only real reason to use AllocDosObject is long word alignment requirement in AmigaOS (but not in MorphOS or AROS). In old days it was source of many bugs because developers did not know about long word alignment restrictions resulting in random behaviour/memory trashing.
How can I return the date in format DDMMYYYY in string format with this ? dos.library/DateToStr

### Protect Files And Directories

You can protect files and directory from being accidentally deleted or changed. You do it by calling the function SetProtection which will alter the protection bits as desired in the FIB FileInfoBlock struct.

### Names

The FileInfoBlock look like this:
At the moment can get the name of the 'double-clicked' file with some code
But cannot get the directory from which my file is 'double-clicked' using "GetCurrentDirName". If set CLI as ToolType, GetCurrentDirName works, but then get bogus data as filename. As your program is launched by Workbench, it doesn't have a CLI structure indeed, hence why GetCurrentDirName doesn't work without CLI tooltype. But you should be able to use NameFromLock and find what you need from there.
GetCurrentDirName gets the current path string from the program's CLI structure and returns an error if there is no CLI structure (IoErr == ERROR_OBJECT_WRONG_TYPE).

### Scanning

MatchFirst and passed to MatchNext and MatchEnd
ParsePattern function creates a case sensitive wildcard string, whereas the ParsePatternNoCase functions creates a case insensitive wildcard string. case sensitive MatchPattern or the case insensitive MatchPatternNoCase
Command line parsing through ReadArgs using a RDArgs structure
CompareDates

### Examples
## Filesystems

A device, handler and filesystem must be re-entrant.
The difference between a handler and a filesystem is that a filesystem is required to expose a directory structure and files. Implicitly this means a good 60% of dos packets.
The general rule of thumb was to implement 'virtual' devices (no underlying hardware) as a handler rather than a full-blown file system as in the case of ram-handler. Obviously that is not the written in stone but it is the established pattern for the OS.
Emulating a foreign filesystem falls into the class of a full blown fileystem, however given that there is no underlying hardware for a virtual device, the need for a device is probably a waste of effort and better implemented as fat-handler.
After "Assign DISMOUNT"? I don't know, but AROS's Assign command didn't call ACTION_END, which would be necessary to shut down the handler fully?
Filesystems can be locked with LDF_READ|LDF_DEVICES, but unlocked with LDF_READ|LDF_VOLUMES.
A device entry should not be removed from dl_DosList or the code unloaded until all associated locks either direct or indirect have been released. Specifically, if a device has medium mounted (aka volume), any attempt to remove the device, be it a hardware device or a virtual device MUST be denied. The best way to detect if a device is mounted without triggering a requester that says "Please insert volume BLAH:" is to use LockDosList \ AttemptLockDosList and then FindDOSEntry.
If the lock obtained is not (BPTR)NULL then the device/volume/assignment you tried is available, otherwise it's not. Note that this method does not specifically check for devices, you'd have to scan the DOSList for that.
Given that a dl_Volume and a dl_Device are 'paired', any attempt to remove the device should validate the device's volume and its associated locks and return a fail with the appropriate error code.
fm2000 source and docs
It is the filesystem which determines the max size, and >4GB is possible so long as you correctly speak to the filesystem.
They are only restricted by -:
* software being written before the 64-bit DOS extensions,
* software not using those extensions since there is no actual "API" call replacements, and they require direct communication to the filesystem.
First thing was to add 64-bit support, so that it can handle partitions larger than 4GB. Added new code in the cache to probe the underlying device to see if it supports 64-bit extensions, and then later if a request comes in for data that is over the 4GB boundary, use a 64-bit read or write operation rather than the standard one (or error, if the probe didn't find any 64-bit extensions). There's three commonly-used 64-bit extensions in the Amiga world - TD64 ie TrackDisk64, New-style TD64, and DirectSCSI. The first two are supported, but DirectSCSI shouldn't be hard to add.
Implementing write-back caching where the handler asks the cache to write some data, the cache reports success immediately but just marks the data as "to be written". Then at regular intervals (eg five seconds) it writes all of these "dirty" blocks out to disk in one go. This makes things feel faster for the user, and has the potential to reduce disk activity (== less wear and lower power consumption), at the risk of losing data in the event of a power failure or loss of the device (like pulling the disk out). Typically removable media uses write-through caching (ie write immediately), while fixed disks use write-back.
Since this requires a separate task that sits and waits and flushes the dirty blocks when called, it means the cache needs locking. Locking will also be needed in the future if a filesystem wanted to be multi-threaded (and the cache is actually in a cache.library, available to all). So far there is locking around cache operations, but not block operations.
To promote a read lock to a write lock. Usually you have to drop the original lock before taking the write lock, which means there's a moment where you're not holding any lock and someone can come and steal it out from under you. Workaround for POSIX threads requires condition variables which we don't currently have for AROS semaphores.

## References
### Packets

Packets (little messages wrapped within a Dos call) are sent to a file system or handler can be divided into categories as follows
AFS handler for example isn't fully "dos packetized" yet. ("real" dos packets and FSA stuff can't work together) They can. We discussed this with Rob Norris. The basic idea was to check what is pointed to by file_handle or lock's Port (or Process) member. It's either NT_PROCESS, or NT_DEVICE type node. This way you can distinguish what are you talking to. Additionally, your con.handler should be able to work nicely with packet.handler. If it doesn't, packet.handler needs to be fixed.
Some issues, all related to AROS's hardcoded 32-bit values, etc. (fib_Size,for starters). What is the best thing to do about this? MorphOS uses 64-bit versions of the DOS functions and structures, though I am not sure about OS4.
OS4 uses the following 64-bit-packet types because I was asked to support them in the WinUAE directory filesystem emulation years ago:
Old packets return file size = 2GB-1 if real size is >=2G. guess we need to alter DoPkt etc to return a QUAD? OK from what I can see DoPkt and the internal dopacket both need to be changed to return a QUAD and DosPacket->dp_Res1 needs to be a QUAD (which will break compatibility afaict). I think this is more desirable than trying to change all of the dos structures to use 64-bit values though?
Is it enough to say code accessing blocknr/size/etc should check if they equal 0x7FFFFFFF, and use 64bit packet calls directly if so?
MorphOS, phase5, Ralph Babel and some CBM engineers defined TD64 standard. MorphOS dosextens.h that describes their 64-bit packet extensions.
TD64 has nothing to do with 64-bit DOS file access. And yes, we have long supported TD64 (along with NSD). Without it, AROS couldn't access more than the first 4GB of any hard drive.

### Process

Plain tasks have limitations in as much they must not call a function of dos.library or a function that could call a function of dos.library. Processes do not have this limitation.
A process is an expanded task. Opposed to a task, it can use functions of dos.library, because a process structure contains some special fields, concerning files and directories.
pr_Flags Flags
It's cleaner than shoving everything in globals. Also, let's say you wish to run the same thread code on slightly different data (not much point unless you have multiple CPU cores, but anyway), NP_UserData allows you to use the same code over and over, just passing it different start data. You could, of course set up a message port and do it that way, but for simple tasks find passing a direct pointer to the data and using semaphores, easier. You can share just about any library pointer between processes.
Every process using bsdsocket.library must open that library on its own. Sharing bsdsocketbase is not permitted.
Generally speaking tasks should share the same address space, while processes not, and at least for that reason libraries should be reopened personally by each process.
Because people do not respect such rules, it is not possible to introduce memory-protection mechanisms to the system.
The easiest way is to call CreateNewProc with no tags for the NP_Input, NP_Output,NP_Error, these will then open a default "NIL:" stream for the child.
Properly (Semaphore) protect any resources such as IO handles etc. that you intend to share between threads and all should be OK.
CreateNewProc should not create BCPL stack frame and register setup (A5=BCPL_jsr and other related weirdness), only RunCommand needs to do it. Can see the reason for confusion because CreateNewProc needs to call BCPL_init which actually only does part of BCPL setup (stupid segarray and some globvec stuff) but it should not setup BCPL stack frames (which replaces A6 with BCPL_rts when all process entries expect SysBase = crash)
CreateNewProc->CallEntry should be as it was:
And RunProcess should be the wild & crazy BCPL stuff.
Why was this change made? Don't see how it helped other architectures, and it certainly did not help m68k. But it should still set pr_ReturnAddr, right? No, because pr_ReturnAddr is only used by exits from RunProcess/RunCommand.
dos/Exit does work with normal process started with Create(New)Proc. It is not BCPL specific. (but perhaps it works differently in "BCPL mode", I haven't tested this yet) AROS/DOS/Exit should be a no-op. The only callers of Exit were BCPL routines, and there is a BCPL specific implementation in arch/m68k-amiga/dos/bcpl.S to support those old programs.
Dos/Exit was a mistake in AmigaOS, and we should not be making it a first-class function. To implement a C-style exit, it should be in the AROS C library, implementing memory free and library closing, etc.
Read more here
The DateStamp structure look like this:

### DosBase

The four public structures, DosLibrary (dl_), RootNode (rn_), DosInfo (di_), and DevInfo (dvi_) are involved. revisited DOSBase and removed almost all AROS-specific fields from it
dl_DevInfo - replaced with di_DevInfo in struct DosInfo
dl_SegList - simply not used, dos.library is ROM resident and doesn't have own seglist.
dl_ResList - removed, now it's DosInfo->di_ResList
The only thing that is left over is dl_Flags. DirectoryOpus uses this on AROS. Can it be replaced correct rn_Flags right now, or this should be done in ABI v1. Other private fields are still there, they are really private.
After the FGetC-s come Seek-s and then a Read. all for that same file_handle. I haven't checked what the arguments are and what happens exactly, but that's similar to the pattern for precise memory allocation: With 'getc' you read the file buffer in steps of one and only count the characters or bytes, until you get to the end of the word or line or file or whatever you want to read. Then you 'seek' to back up to where you started, you 'alloc' the counted size of memory, and then you 'read' the whole thing in one go. It's a way to avoid memory-related problems, but it may be rather heavy on the i/o-side.
FGetC like C (f)getc is a function meant to be called a lot of time. It is often used as a function pointer passed to parsing functions. Programs using fscanf etc. will use a lot of FGetC. As it is buffered I doubt it is a performance bottleneck (unless you patch it of course and print out a line on a console with each call).