45 Using Apis in the Os Package Part 2

45 Using APIs in the os Package - Part 2 #

Hello, I’m Haolin. Today we will continue sharing about using APIs in the os package.

In the previous article, we started with the question “Which interfaces in the io package are implemented by the os.File type?” and introduced a series of related content. Today, we will continue to expand on this knowledge point.

Knowledge Expansion #

Question 1: What are the available operation modes for File values? #

The operation modes for File values mainly include read-only mode, write-only mode, and read-write mode.

These modes are represented by the constants os.O_RDONLY, os.O_WRONLY, and os.O_RDWR, respectively. When we create or open a file, we must set one of these three modes as the operation mode for the file.

In addition, we can also set additional operation modes for the file, which are optional and listed as follows:

  • os.O_APPEND: When writing content to the file, append the new content to the existing content.
  • os.O_CREATE: Create a new file if the file does not exist at the given path.
  • os.O_EXCL: Should be used together with os.O_CREATE to indicate that there should not be an existing file at the given path.
  • os.O_SYNC: Implement synchronous I/O on the opened file. It guarantees that the content read or written is always synchronized with the data on the disk.
  • os.O_TRUNC: If the file already exists and is a regular file, clear any existing content in the file first.

Regarding the usage of these operation modes, the os.Create function and os.Open function are ready examples.

func Create(name string) (*File, error) {
 return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

The os.Create function, when calling the os.OpenFile function, uses the combination of os.O_RDWR, os.O_CREATE, and os.O_TRUNC as the operation mode.

This basically determines its behavior, which is: if the file does not exist at the path specified by the name parameter, then create a new file; otherwise, clear all existing content in the file.

The File value returned by os.Create has both read and write methods available. Note that multiple operation modes are combined using the bitwise OR operator |.

func Open(name string) (*File, error) {
 return OpenFile(name, O_RDONLY, 0)
}

As I mentioned earlier, the os.Open function opens an existing file in read-only mode. Its underlying implementation is to only provide the single operation mode os.O_RDONLY when calling the os.OpenFile function.

These are my simple explanations of the operation modes that can be applied to File values. There are a few examples in the demo88.go file for your reference.

Question 2: How to set the access permissions for regular files? #

We already know that the third parameter perm in the os.OpenFile function represents the permission mode, which has a type of os.FileMode. However, in practice, os.FileMode represents more than just permission modes; it can also represent file modes (or file types).

Since os.FileMode is a redefined type based on the uint32 type, each value of os.FileMode contains 32 bits. Each bit has its specific meaning.

For example, if the binary number on the highest bit is 1, then the file mode represented by the value is equivalent to os.ModeDir, indicating that the file represents a directory.

Similarly, if the 26th bit is 1, then the corresponding value represents the file mode equivalent to os.ModeNamedPipe, indicating that the file represents a named pipe.

In reality, only the lowest 9 bits in an os.FileMode value are used to represent file permissions. When we have such a value, we can perform a bitwise AND operation with the value of the constant os.ModePerm.

The value of this constant is 0777, an octal unsigned integer, with all the lowest 9 bits being 1 and the higher 23 bits being 0.

By performing this bitwise AND operation, we can obtain the bits in the os.FileMode value that represent the file permissions. This will be consistent with the result obtained from calling the Perm method of the FileMode value.

Among these 9 bits used to represent file permissions, every 3 bits form a group, resulting in 3 groups in total.

From high to low, these 3 groups represent the file owner (the user who created the file), the user group to which the file owner belongs, and the access permissions for other users. For each group, the 3 bits in it represent the read, write, and execute permissions from high to low.

If a bit in one of these groups is 1, it means that the corresponding permission is enabled, otherwise, it means that the permission is disabled.

Therefore, the octal integer 0777 means that all users in the operating system have read, write, and execute permissions on the current file, while the octal integer 0666 means that all users have read and write permissions on the current file, but no execute permission.

When we call the os.OpenFile function, we can set its third parameter based on the above explanation. However, note that this parameter value is only effective when creating a new file. In other cases, even if we set this parameter, it will not have any effect on the target file.

Summary #

In order to focus on the os.File type itself, I mainly discussed how to apply the os.File type to regular files in these two articles. The pointer type of this type implements many interfaces in the io package, so its specific functions are self-evident.

With the value of this type, we can not only perform various operations such as reading, writing, and closing on files, but also set the starting index position for the next read or write.

Before using a value of this type, we must first create it. So, I highlighted several functions that can create and obtain values of this type for you.

These include: os.Create, os.NewFile, os.Open, and os.OpenFile. The way we create a File value determines what we can do with it.

With the os.Create function, we can create a brand new file in the operating system, or empty an existing file and reuse it.

With the corresponding File value, we can perform any read or write operations on the file. Although the os.NewFile function is not used to create a new file, it can wrap a usable File value based on a valid file descriptor.

The os.Open function is used to open an existing file. However, we can only perform read operations on the file using the returned File value.

os.OpenFile is the most flexible among these functions. With it, we can set the operation mode and permission mode of the opened file. In fact, the os.Create function and the os.Open function are just simple encapsulations of it.

When using the os.OpenFile function, we must understand the true meaning of the operation mode and permission mode, and set them correctly.

In the expansion questions in this article, I explained them in detail. At the same time, I wrote some code in the corresponding example file.

You need to carefully read and understand this code, and gain insight into the true meaning of these two modes while running them.

What I discussed in this article is only the tip of the iceberg for the os package. This code package contains a lot of knowledge, and it has strong extensibility.

If you want to fully understand them, you may need to refer to documentation and tutorials on operating systems and other aspects. Due to the length limitations, I can only provide a guide here to help you get started with some important program entities in the package, and give you a starting point for further exploration. I hope you are on your way.

Reflection Questions

Today’s reflection question is: How to create and manipulate a system process through the APIs in the os package?

Click here to view the detailed code accompanying the Go language column article.