Synchronizing I/O with file descriptors
[Posted September 4, 2011 by corbet]
/*
* Copyright 2011, Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include "sync-samples.h"
const char *message = "This is very important data!\n";
int
main(int argc, char **argv)
{
int ret;
size_t message_len;
int fd, dir_fd;
mode_t old_mode;
char *containing_dir;
if (argc < 2) {
fprintf(stderr, "Usage: %s <filename>\n", basename(argv[0]));
exit(USER_ERR);
}
/*
* Note that this will truncate the file.
*/
old_mode = umask((mode_t)0);
fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
perror("open");
exit(SYS_ERR);
}
umask(old_mode);
message_len = strlen(message);
ret = full_write(fd, message, message_len);
if (ret != (int)message_len) {
if (ret < 0) {
perror("write");
exit(SYS_ERR);
}
/*
* Short write. This can happen if the file system is
* full, for example. In our case, we can't use the
* partial data, so just unlink the file. If the
* unlink fails, report this to the user.
*/
if (unlink(argv[1]) < 0)
perror("unlink");
exit(SYS_ERR);
}
/*
* Now the data is in the kernel's page cache. The next steps
* flush the page cache pages associated with this file to disk.
*/
if (fsync(fd) < 0) {
perror("fsync");
exit(SYS_ERR);
}
/*
* Because we just created this file, we also need to ensure that
* the new directory entry gets flushed to disk.
*/
/*
* Basename and dirname may modify the string passed in. We
* are not reusing argv[1], though, so we won't worry about
* it.
*/
containing_dir = dirname(argv[1]);
/*
* You can't write directly to a directory. fsync, however
* is allowed on the directory, even when opened read-only.
*/
dir_fd = open(containing_dir, O_RDONLY);
if (dir_fd < 0) {
perror("open");
exit(SYS_ERR);
}
if (fsync(dir_fd) < 0) {
perror("fsync2");
exit(SYS_ERR);
}
/*
* There really shouldn't be any errors returned from close,
* here. However, in the case of memory corruption
* (overwriting the fd, for example), you can get a failure.
* Also, the close call can be interrupted, which we don't
* specifically handle. The exit will take care of finishing
* the job.
*/
if (close(dir_fd) < 0) {
perror("close dir_fd");
exit(SYS_ERR);
}
if (close(fd) < 0) {
perror("close fd");
exit(SYS_ERR);
}
exit(0);
}
(
Log in to post comments)