File manipulation can be complex in software development. Different operating systems have different methods for file management, each with a list of different options. In this chapter, we will discuss file I/O for Linux systems; you will see in Chapter 43 that file I/O in Windows is entirely different.
In Linux, file management is complex and involves creating and opening a file for read-only or read/write, writing to a new file or appending to a file, and deleting files...not to mention the security settings for ‘user’, ‘group’, and ‘other’. Brush up your admin skills on the Linux filesystem if necessary, and dust off your Linux system administration manual to refresh your memory. In the code, we specified only the flags for the current ‘user’, but you can also add flags for ‘group’ and ‘other’. If you have no clue what we are talking about, it is time to study a bit about basic Linux file management.
Using syscalls
We will use the same names in our program. Note that there is also a file named unistd_32h for 32-bit legacy compatibility.
There these flags are given in octal notation (e.g., O_CREAT = 00000100). A value that starts with 0x is a hexadecimal value, and a value that starts with 0 without an x is an octal value. For readability, you can append the character q to an octal number.
The file permissions are also given in octal notation and are familiar to a Linux system administrator. For the sake of consistency, we will borrow the symbolic names used in these files.
The example program is quite lengthy, but we will analyze it step-by-step, which can be accomplished using conditional assembly. This gives you a chance to analyze the program piece by piece.
File Handling
- 1.
Create a file and then write data in the file.
- 2.
Overwrite part of the content of the file.
- 3.
Append data to the file.
- 4.
Write data at a certain position in the file.
- 5.
Read data from the file.
- 6.
Read data from a certain position in the file.
- 7.
Delete the file.
file.asm
Conditional Assembly
Because this is quite a long program, to make it easier to analyze, we use conditional assembly. We created different constants such as CREATE, WRITE, APPEND, and so on. If you set such a variable to 1, then certain code, enclosed by %IF 'variable' and %ENDIF, will be assembled. If that variable is set to 0, the assembler will ignore the code. The %IF and %ENDIF parts are called assembler preprocessor directives. Start with the variable CREATE equ 1, and set the other variables equal to 0, assemble, run, and analyze the program. Gradually work your way down. Continue with CREATE equ 1 and OVERWRITE equ 1 and set the other variables equal to 0 on the second build, and so on.
NASM gives you a considerable collection of preprocessor directives; here we use conditional assembly. To define macros, as we explained before, we also used preprocessor directives. In Chapter 4 of the NASM manual, you will find a complete description of preprocessor directives.
The File-Handling Instructions
Let’s begin with creating a file. Move the file name into rdi, and call createFile. In createFile, put the symbolic variable NR_create into rax, and specify in rsi the flags for creating the file. In this case, the user gets read and write permissions and then does a syscall.
When for some reason the file cannot be created, createFile returns a negative value in rax, and in this case we want an error message to be displayed. If you want more detail, the negative value in rax indicates what kind of error occurred. If the file is created, the function returns a file descriptor in rax. In the calling program, we save the file descriptor to the variable FD for further file manipulations. You can see that we have to be careful to preserve the content of rax before calling the printString function . A call to printString will destroy the content of rax, so we push rax to the stack before calling. According to the calling conventions, rax is a caller-saved register.
Next in the code, some text is written to the file, and then the file is closed. Note that when you create a file, a new file will be created; if a file exists with the same name, it will be deleted.
Build and run the program with CREATE equ 1; the other conditional assembly variables equal 0. Then go to the command prompt and verify that a testfile.txt file is created and that it has the message in it. If you want to see the content of the file in hexadecimal, which is sometimes useful, use xxd testfile.txt at the CLI prompt.
Continue by gradually putting the conditional assembly variables to 1, one at the time, and check in testfile.txt what happens.
Summary
File creation, opening, closing, deleting
Writing to a file, appending to a file, and writing to a file at a specific position
Reading from a file
The different parameters for file handling