Is there linux system call similar to fsync but uses file path instead of file descriptor?
I modify file modification time using utime, the file is in USB, I need to make sure the time is flushed to usb after setting utime.
As far as I know there isn't.
To do so you need to
open() the file
call fsync() on the fd returned by 1.
close() the fd returned by 1.
open() the containing directory
call fsync() on the fd returned by 4.
close() the fd returned by 4.
As you are after updating the file's meta-data the steps 4 to 6 are necessary to get the directory's entries (which carry the file's meta data) to disk.
As per Throwback1986's comment please note that this is not working recursivly in any way.
Here is a snippet of code I lifted from some of my utility routines in a POSIX-like system. The code includes the synching helper function, as well as the recursive caller. It may not compile "out of the box" since it was from a specialized system, but it's close enough to get you on the right track.
static int sync_path(const char *path)
{
int fd = 0;
int ret = 0;
if (path == NULL)
{
printf("Invalid path (NULL)");
return -1;
}
fd = open(path, O_RDONLY);
if (fd < 0)
{
printf("Failed to open dir [%d:%s] (%s)",
errno, strerror(errno), path);
return errno;
}
ret = fsync(fd);
if (ret < 0)
{
printf("Failed to sync dir [%d:%s] (%s)",
errno, strerror(errno), path);
close(fd);
return errno;
}
ret = close(fd);
if (ret < 0)
{
printf("Failed to close dir [%d:%s] (%s)",
errno, strerror(errno), path);
return errno;
}
return 0;
}
int sync_tree(const char *path)
{
static size_t depth = 0;
struct dirent *entry = NULL;
DIR *dir = NULL;
const char *p = NULL;
int ret = 0;
if (path == NULL)
{
printf("Invalid path (NULL)");
return -1;
}
depth++;
if (depth > MAX_RECURSION_DEPTH)
{
printf("Recursion limit reached [%d]",
MAX_RECURSION_DEPTH);
return -1;
}
ret = chdir(path);
if (ret)
{
printf("Unable to chdir [%d:%s] (%s)",
errno, strerror(errno), path);
return errno;
}
// Operate on the current dir (after the chdir above)
dir = opendir("./");
if (dir == NULL)
{
printf("Unable to open dir [%d:%s] (%s)",
errno, strerror(errno), path);
return errno;
}
entry = readdir(dir);
while (entry && (ret == Success))
{
p = entry -> d_name;
if (!p)
{
break;
}
if (entry -> d_type != DT_DIR)
{
ret = sync_path(p);
}
else
{
// If not dir is not . or .., then process it (depth-first)
if (strnicmp(p, ".", 1) && strnicmp(p, "..", 2))
{
// Recursion here...
ret = sync_tree(p);
if (ret)
{
// Do nothing - any error message should already be handled
}
else
{
// Restore current dir
ret = chdir("../");
if (ret)
{
printf("Unable to chdir [%d:%s] (%s)",
errno, strerror(errno), "../");
}
else
{
// All is well...
}
}
}
}
if (ret == 0)
{
entry = readdir(dir);
}
}
closedir(dir);
// Sync this level
ret = sync_path("./");
if (ret)
{
// Any error message should already be printed, so just return
return -1;
}
else
{
// All is well..
}
depth--;
return Success;
}
Related
I am making a small shell and I am having some problems with two of my functions.
They are kind of out of context but I hope you can understand what I am trying to do so I don't have to post my whole code.
My dupPipe function:
I want to duplicate a pipe to a std I/O file descriptor and close both of the pipe ends. It looks like this: int dupPipe(int pip[2], int end, int destinfd);. Where end tells which pipe to duplicate, either READ_END or WRITE_END and destinfd tells which std I/O file descriptor to replace.
My Redirect function:
It's supposed to redirect a std I/O file descriptor to a file.
It looks like this, int redirect(char *file, int flag, int destinfd);.
Where flag indicate if the file should be read from or written to and destinfd is the std I/O file descriptor I want to redirect.
What I have done:
int dupPipe(int pip[2], int end, int destinfd)
{
if(end == READ_END)
{
dup2(pip[0], destinfd);
close(pip[0]);
}
else if(end == WRITE_END)
{
dup2(pip[1], destinfd);
close(pip[1]);
}
return destinfd;
}
Second function:
int redirect(char *filename, int flags, int destinfd)
{
if(flags == 0)
{
return destinfd;
}
else if(flags == 1)
{
FILE *f = fopen(filename, "w");
if(! f)
{
perror(filename);
return -1;
}
}
else if(flags == 2)
{
FILE *f = fopen(filename, "r");
if(! f)
{
perror(filename);
return -1;
}
}
return destinfd;
}
I appreciate any help given, what have I done wrong or haven't done with the function that I wrote wanted to? Thanks.
The redirect function doesn't appear to be doing what you want. You're opening a file using fopen, but you're not linking it to destinfd in any way. You probably want to use open instead, then use dup2 to move the file descriptor to where you want.
int redirect(char *filename, int flags, int destinfd)
{
int newfd;
if(flags == 0) {
return -1;
} else if(flags == 1) {
newfd = open(filename, O_WRONLY);
if (newfd == -1) {
perror("open for write failed");
return -1;
}
} else if(flags == 2) {
newfd = open(filename, O_RDONLY);
if (newfd == -1) {
perror("open for read failed");
return -1;
}
} else {
return -1;
}
if (dup2(newfd, destinfd) == -1) {
perror("dup2 failed");
close(newfd);
return -1;
}
close(newfd);
return destinfd;
}
I'm trying to open files at random from my disk. Problem Is Most have Spaces in their path and won't
open unless I add backslashes on the fly, which is proving more difficult than I thought. While doing research I saw
how does unix handle full path name with space and arguments?
which say's that you can use backslashes or quotes but that was referring to passing arguments to the shell. Is it possible to do something like...
static int removeSpaces(char **inBuf, char **outBuf)
{
/* Declarations */
int rtrn;
char *pos;
/* Null terminate incoming buffer */
if((pos=strchr(*inBuf, '\n')) != NULL)
{
*pos = '\0';
}
rtrn = asprintf(outBuf, "\"%s\"", *inBuf);
if(rtrn < 0)
{
printf("Memory Error\n");
return -1;
}
return 0;
}
int main(void)
{
int rtrn;
char *file, *parsed;
rtrn = getFile(&file);
if(rtrn < 0)
{
printf("Can't Get File\n");
return -1;
}
rtrn = removeSpaces(&file, &parsed);
if(rtrn < 0)
{
printf("Can't Get File\n");
return -1;
}
printf("Parsed: %s\n", parsed);
fd = open(parsed, O_RDONLY);
if(fd < 0)
{
perror("Can't Open File\n");
return -1;
}
return 0;
}
I currently get no such file or directory, but i was wondering can I pass quoted file paths to open() or fopen(), or is that only the case with passing file paths to the shell. I'm asking because I saw very little information on the web specific to unix and unix-like systems, most was about windows (at least what i saw), and what i did find for unix was about the shell and not system calls.
So I've written a short C program that explores the files on my computer to look for a certain file. I wrote a simple function that takes a directory, opens it an looks around:
int exploreDIR (char stringDIR[], char search[])
{
DIR* dir;
struct dirent* ent;
if ((dir = opendir(stringDIR)) == NULL)
{
printf("Error: could not open directory %s\n", stringDIR);
return 0;
}
while ((ent = readdir(dir)) != NULL)
{
if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
continue;
if (strlen(stringDIR) + 1 + strlen(ent->d_name) > 1024)
{
perror("\nError: File path is too long!\n");
continue;
}
char filePath[1024];
strcpy(filePath, stringDIR);
strcat(filePath, "/");
strcat(filePath, ent->d_name);
if (strcmp(ent->d_name, search) == 0)
{
printf(" Found it! It's at: %s\n", filePath);
return 1;
}
struct stat st;
if (lstat(filePath, &st) < 0)
{
perror("Error: lstat() failure");
continue;
}
if (st.st_mode & S_IFDIR)
{
DIR* tempdir;
if ((tempdir = opendir (filePath)))
{
exploreDIR(filePath, search);
}
}
}
closedir(dir);
return 0;
}
However, I keep getting the output:
Error: could not open directory /Users/Dan/Desktop/Box/Videos
Error: could not open directory /Users/Dan/Desktop/compilerHome
The problem is, I have no idea what it is about these files that could cause opendir() to fail. I don't have them open in any program. They're just simple folders I created on my desktop. Does anyone have any idea what the problem could be?
You are calling opendir() twice for each closedir(). Maybe you are running out of resources.
I wrote a function below to read the content of a file to memory.
It works well on my local machine(Ubuntu 32bit), but it produces wrong result on server(CentOS 64bit).
Wrong case:
With a 40 byte file, the content is below, on the 64bit os, it gave me wrong result.
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
The code:
char* file_get_contents(const char *filename) {
FILE *stream = NULL;
char *content = NULL;
size_t ret;
struct stat st;
if ((stream = fopen(filename,"r")) == NULL) {
fprintf(stderr, "Failed to open file %s\n", filename);
exit(1002);
}
if(stat(filename, &st) < 0) {
fprintf(stderr, "Failed to stat file %s\n", filename);
exit(1002);
}
content = malloc(st.st_size);
ret = fread(content, 1, st.st_size, stream);
if (ret != st.st_size) {
fprintf(stderr, "Failed to read file %s\n", filename);
exit(1002);
}
fclose(stream);
return content;
}
Your file_get_contents cannot be correctly used by its caller. It returns a char * but not its lenght, nor does it return a string (i.e. it isn't null terminated.).
As long as you're reading text, do e.g.
content = malloc(st.st_size + 1); // + 1 here for the nul terminator
ret = fread(content, 1, st.st_size, stream);
if (ret != st.st_size) {
fprintf(stderr, "Failed to read file %s\n", filename);
exit(1002);
}
content[st.st_size] = 0; //nul terminate
Code that I am running now:
int main(int argc, char *argv[])
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE;
DWORD dwError;
LPSTR DirSpec;
size_t length_of_arg;
int i,j;
char cd[256],schar[500];
FILE *fp;
DirSpec = (LPSTR) malloc (BUFSIZE);
// Check for command-line parameter; otherwise, print usage.
if(argc != 2)
{
printf("Usage: Test <dir>\n");
return 2;
}
// Check that the input is not larger than allowed.
//scanf("%s",argv[1]);
StringCbLength(argv[1], BUFSIZE, &length_of_arg);
if (length_of_arg > (BUFSIZE - 2))
{
printf("Input directory is too large.\n");
return 3;
}
printf ("Target directory is %s.\n", argv[1]);
StringCbCopyN (DirSpec, BUFSIZE, argv[1], length_of_arg+1);
StringCbCatN (DirSpec, BUFSIZE, "\\namefile.b11", 18);
hFind = FindFirstFile(DirSpec, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("Invalid file handle. Error is %u.\n", GetLastError());
return (-1);
}
else
{
printf ("First file name is %s.\n", FindFileData.cFileName);
fp=fopen(DirSpec,"rb");
for(i=0;i< 8;i++)
{
schar[i]= fgetc(fp);//get each character from file
}
if ( i > 7 )
{
cd[i]=schar[6]*65336+schar[5]*256+schar[4];
printf("%d",cd[i]);
}
// List all the other files in the directory.
while (FindNextFile(hFind, &FindFileData) != 0)
{
printf ("Next file name is %s.\n", FindFileData.cFileName);
}
dwError = GetLastError();
FindClose(hFind);
if (dwError != ERROR_NO_MORE_FILES)
{
printf ("FindNextFile error. Error is %u.\n", dwError);
return (-1);
}
}
free(DirSpec);
getchar();
return (0);
}
This is working fine. If I concatinate the file name directly by using StringCbCatN().
But for every file I need to change the file name.which I don't want. Is it possible to print the file with file extension?