The Linux reboot function invoked in program C results in the loss of the file created by the program on disk

I developed the C program (Linux), this program created a new file and wrote it down, after which it reboots the computer.

After rebooting, I lost the file created by my program. When I deactivate the reload function, the file created by my program is still present.

This behavior is observed in Linux: - OpenWrt (Backfire 10.03) on VirtualBox (ext2 file system) - Linux (Ubuntu) (ext4 file system)

Do you have an explanation for this behavior and how to fix it?

#include <stdio.h> #include <sys/reboot.h> int main () { FILE *pFile; char mybuffer[80]; pFile = fopen ("/home/user/Desktop/example.txt","w"); if (pFile == NULL) perror ("Error opening file"); else { fputs ("test",pFile); fclose (pFile); } rename("/home/user/Desktop/example.txt","/home/user/Desktop/example123.txt"); reboot(RB_AUTOBOOT); return 0; } 
+6
source share
4 answers

The man page for fclose says:

Note that fclose () only flushes the user-space buffers provided by the C library. To guarantee the physical safety of data on disk, kernel buffers must also be flushed, for example, with synchronization (2) or FSYNC (2).

This means that you need to call fsync before closing the file descriptor.

+7
source

The immediate problem is that you do not sync the file before performing a reboot. The actual problem is that you are calling syscall reboot directly, not paying attention to what else is going on in the system. What you are doing is very similar to simply pressing the HW reset button; you just give the kernel an opportunity to clean the cleaning a bit, but then everything will be killed. This is a dead, surefire way to end up messing up file systems and file structures. Do not do this!

Instead, you should ask init to do a graceful reboot. Calling syscall reboot requires privileged access. Therefore, you can simply ask to restart the init system. Most systems have a symbolic link /sbin/reboot , which points to a program that initiates a correct reboot if called through this symbolic link. Therefore, I recommend that you replace your dirty reboot(RB_AUTOBOOT) (note the double specification "/sbin/reboot" in execlp - this is important).

 pid_t reboot_pid; if( 0 == (reboot_pid = fork()) ) { execlp("/sbin/reboot", "/sbin/reboot", NULL); exit(1); /* never reached if execlp succeeds. */ } if( -1 == reboot_pid ) { /* fork error... deal with it somehow */ } int reboot_status; waitpid(reboot_pid, &reboot_status, 0); if( !WIFEXITED(reboot_status) ) { /* reboot process did not exit sanely... deal with it somehow */ } if( 0 != WIFEXITSTATUS(reboot_status) ) { /* reboot process exited with error; * most likely the user lacks the required privileges */ } else { fputs("reboot call sucessfull -- system is about to shutdown."); /* The init system is now shutting down the system. It will signals all * programs to terminate by sending SIGTERM, followed by SIGKILL to * programs that didn't terminate gracefully. */ } 

In this way, the system can gracefully shut down, terminate all programs launched in a clean manner, and disable all file systems before performing a reboot, thereby reading the file system and data integrity.

Note that if you expect your program will not have root access, you will have to jump on some hoops; on systems with systemd, you can send a reboot request using D-Bus. But except that it fails if the user executing the command does not have reboot privileges.

+6
source

I think the important thing here is that the reboot never returns, so your program will never exit normally.

Under normal circumstances (that is, a program that crashes or even fails after calling fclose), the file descriptors underlying your FILE * will be closed and their kernel buffers will be flushed.

In this case, however, since the reboot will never return, I suspect that the kernel buffers are not flushed in the usual way, therefore, because of this, the material is not written to disk.

The fsync call will probably take care of this. If you want to be paranoid, run fsync, then use fileno () to get the file descriptor, and use sync () to make sure the buffers are cleared. At this point, there should be nothing from the file remaining in the process address space, and your call to reboot should not cause any problems.

+1
source

An alternative solution is to force synchronization according to the reboot work page.

LINUX_REBOOT_CMD_POWER_OFF (RB_POWER_OFF, 0x4321fedc, since Linux 2.1.30). The message "Turn off." is printed, the system stops, and all power is removed from the system, if possible. If sync (2) is not preceded, data will be lost.

0
source

Source: https://habr.com/ru/post/915691/


All Articles