You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
687 lines
15 KiB
687 lines
15 KiB
/* |
|
* Copyright (C) 2016 The Android Open Source Project |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <sys/time.h> |
|
#include <sys/types.h> |
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <signal.h> |
|
#include <string.h> |
|
#include <sys/stat.h> |
|
#include <sys/errno.h> |
|
#include <fcntl.h> |
|
#include <ctype.h> |
|
#include "ioshark.h" |
|
#include "compile_ioshark.h" |
|
|
|
char *progname; |
|
|
|
char in_buf[2048]; |
|
|
|
struct flags_map_s { |
|
char *flag_str; |
|
int flag; |
|
}; |
|
|
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
|
|
|
struct flags_map_s open_flags_map[] = { |
|
{ "O_RDONLY", O_RDONLY }, |
|
{ "O_WRONLY", O_WRONLY }, |
|
{ "O_RDWR", O_RDWR }, |
|
{ "O_CREAT", O_CREAT }, |
|
{ "O_SYNC", O_SYNC }, |
|
{ "O_TRUNC", O_TRUNC }, |
|
{ "O_EXCL", O_EXCL }, |
|
{ "O_APPEND", O_APPEND }, |
|
{ "O_NOATIME", O_NOATIME }, |
|
{ "O_ASYNC", O_ASYNC }, |
|
{ "O_CLOEXEC", O_CLOEXEC }, |
|
{ "O_DIRECT", O_DIRECT }, |
|
{ "O_DIRECTORY", O_DIRECTORY }, |
|
{ "O_LARGEFILE", O_LARGEFILE }, |
|
{ "O_NOCTTY", O_NOCTTY }, |
|
{ "O_NOFOLLOW", O_NOFOLLOW }, |
|
{ "O_NONBLOCK", O_NONBLOCK }, |
|
{ "O_NDELAY", O_NDELAY }, |
|
{ "O_PATH", O_PATH } |
|
}; |
|
|
|
struct flags_map_s lseek_action_map[] = { |
|
{ "SEEK_SET", SEEK_SET }, |
|
{ "SEEK_CUR", SEEK_CUR }, |
|
{ "SEEK_END", SEEK_END } |
|
}; |
|
|
|
struct flags_map_s fileop_map[] = { |
|
{ "lseek", IOSHARK_LSEEK }, |
|
{ "_llseek", IOSHARK_LLSEEK }, |
|
{ "pread64", IOSHARK_PREAD64 }, |
|
{ "pwrite64", IOSHARK_PWRITE64 }, |
|
{ "read", IOSHARK_READ }, |
|
{ "write", IOSHARK_WRITE }, |
|
{ "mmap", IOSHARK_MMAP }, |
|
{ "mmap2", IOSHARK_MMAP2 }, |
|
{ "openat", IOSHARK_OPEN }, |
|
{ "fsync", IOSHARK_FSYNC }, |
|
{ "fdatasync", IOSHARK_FDATASYNC }, |
|
{ "close", IOSHARK_CLOSE }, |
|
{ "ftrace", IOSHARK_MAPPED_PREAD } |
|
}; |
|
|
|
struct in_mem_file_op { |
|
struct ioshark_file_operation disk_file_op; |
|
struct in_mem_file_op *next; |
|
}; |
|
|
|
struct in_mem_file_op *in_mem_file_op_head = NULL, *in_mem_file_op_tail = NULL; |
|
|
|
void usage(void) |
|
{ |
|
fprintf(stderr, "%s in_file out_file\n", progname); |
|
} |
|
|
|
void |
|
init_prev_time(struct timeval *tv) |
|
{ |
|
tv->tv_sec = tv->tv_usec = 0; |
|
} |
|
|
|
/* |
|
* delta ts is the time delta from the previous IO in this tracefile. |
|
*/ |
|
static u_int64_t |
|
get_delta_ts(char *buf, struct timeval *prev) |
|
{ |
|
struct timeval op_tv, tv_res; |
|
|
|
sscanf(buf, "%lu.%lu", &op_tv.tv_sec, &op_tv.tv_usec); |
|
/* First item */ |
|
if (prev->tv_sec == 0 && prev->tv_usec == 0) |
|
tv_res.tv_sec = tv_res.tv_usec = 0; |
|
else |
|
timersub(&op_tv, prev, &tv_res); |
|
*prev = op_tv; |
|
return (tv_res.tv_usec + (tv_res.tv_sec * 1000000)); |
|
} |
|
|
|
void |
|
get_tracetype(char *buf, char *trace_type) |
|
{ |
|
char *s, *s2; |
|
|
|
*trace_type = '\0'; |
|
s = strchr(buf, ' '); |
|
if (s == NULL) { |
|
fprintf(stderr, |
|
"%s Malformed Trace Type ? %s\n", |
|
progname, __func__); |
|
exit(EXIT_FAILURE); |
|
} |
|
while (*s == ' ') |
|
s++; |
|
if (sscanf(s, "%s", trace_type) != 1) { |
|
fprintf(stderr, |
|
"%s Malformed Trace Type ? %s\n", |
|
progname, __func__); |
|
exit(EXIT_FAILURE); |
|
} |
|
if (strcmp(trace_type, "strace") != 0 && |
|
strcmp(trace_type, "ftrace") != 0) { |
|
fprintf(stderr, |
|
"%s Unknown/Missing Trace Type (has to be strace|ftrace) %s\n", |
|
progname, __func__); |
|
exit(EXIT_FAILURE); |
|
} |
|
/* |
|
* Remove the keyword "strace"/"ftrace" from the buffer |
|
*/ |
|
s2 = strchr(s, ' '); |
|
if (s2 == NULL) { |
|
fprintf(stderr, |
|
"%s Malformed Trace Type ? %s\n", |
|
progname, __func__); |
|
exit(EXIT_FAILURE); |
|
} |
|
while (*s2 == ' ') |
|
s2++; |
|
if (*s2 == '\0') { |
|
/* |
|
* Premature end of input record |
|
*/ |
|
fprintf(stderr, |
|
"%s Mal-formed strace/ftrace record %s:%s\n", |
|
progname, __func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
/* strcpy() expects non-overlapping buffers, but bcopy doesn't */ |
|
bcopy(s2, s, strlen(s2) + 1); |
|
} |
|
|
|
void |
|
get_pathname(char *buf, char *pathname, enum file_op file_op) |
|
{ |
|
char *s, *s2, save; |
|
|
|
if (file_op == IOSHARK_MAPPED_PREAD) { |
|
s = strchr(buf, '/'); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s2 = strchr(s, ' '); |
|
if (s2 == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
} else { |
|
if (file_op == IOSHARK_OPEN) |
|
s = strchr(buf, '"'); |
|
else |
|
s = strchr(buf, '<'); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 1; |
|
if (file_op == IOSHARK_OPEN) |
|
s2 = strchr(s, '"'); |
|
else |
|
s2 = strchr(s, '>'); |
|
if (s2 == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
} |
|
save = *s2; |
|
*s2 = '\0'; |
|
strcpy(pathname, s); |
|
*s2 = save; |
|
} |
|
|
|
void |
|
get_syscall(char *buf, char *syscall) |
|
{ |
|
char *s, *s2; |
|
|
|
s = strchr(buf, ' '); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 1; |
|
s2 = strchr(s, '('); |
|
if (s2 == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
*s2 = '\0'; |
|
strcpy(syscall, s); |
|
*s2 = '('; |
|
} |
|
|
|
void |
|
get_mmap_offset_len_prot(char *buf, int *prot, off_t *offset, u_int64_t *len) |
|
{ |
|
char *s, *s1; |
|
int i; |
|
char protstr[128]; |
|
|
|
s = strchr(buf, ','); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 2; |
|
sscanf(s, "%ju", len); |
|
s1 = strchr(s, ','); |
|
if (s1 == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s1 += 2; |
|
s = strchr(s1, ','); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
*s = '\0'; |
|
strcpy(protstr, s1); |
|
*prot = 0; |
|
if (strstr(protstr, "PROT_READ")) |
|
*prot |= IOSHARK_PROT_READ; |
|
if (strstr(protstr, "PROT_WRITE")) |
|
*prot |= IOSHARK_PROT_WRITE; |
|
*s = ','; |
|
s += 2; |
|
for (i = 0 ; i < 2 ; i++) { |
|
s = strchr(s, ','); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 2; |
|
} |
|
sscanf(s, "%jx", offset); |
|
} |
|
|
|
void |
|
get_lseek_offset_action(char *buf, enum file_op op, |
|
off_t *offset, char *action) |
|
{ |
|
char *s, *s2; |
|
|
|
s = strchr(buf, ','); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 2; |
|
sscanf(s, "%ju", offset); |
|
s = strchr(s, ','); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 2; |
|
if (op == IOSHARK_LLSEEK) { |
|
s = strchr(s, ','); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 2; |
|
} |
|
s2 = strchr(s, ')'); |
|
if (s2 == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
*s2 = '\0'; |
|
strcpy(action, s); |
|
*s2 = ')'; |
|
} |
|
|
|
void |
|
get_rw_len(char *buf, |
|
u_int64_t *len) |
|
{ |
|
char *s_len; |
|
|
|
s_len = strrchr(buf, ','); |
|
if (s_len == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
sscanf(s_len + 2, "%ju", len); |
|
} |
|
|
|
void |
|
get_prw64_offset_len(char *buf, |
|
off_t *offset, |
|
u_int64_t *len) |
|
{ |
|
char *s_offset, *s_len; |
|
|
|
s_offset = strrchr(buf, ','); |
|
if (s_offset == NULL) { |
|
fprintf(stderr, "%s: Malformed line 1: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
*s_offset = '\0'; |
|
s_len = strrchr(buf, ','); |
|
if (s_len == NULL) { |
|
fprintf(stderr, "%s: Malformed line 2: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
*s_offset = ','; |
|
sscanf(s_len + 2, "%ju", len); |
|
sscanf(s_offset + 2, "%ju", offset); |
|
} |
|
|
|
|
|
void |
|
get_ftrace_offset_len(char *buf, |
|
off_t *offset, |
|
u_int64_t *len) |
|
{ |
|
char *s_offset; |
|
|
|
s_offset = strchr(buf, '/'); |
|
if (s_offset == NULL) { |
|
fprintf(stderr, "%s: Malformed line 1: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s_offset = strchr(s_offset, ' '); |
|
if (s_offset == NULL) { |
|
fprintf(stderr, "%s: Malformed line 2: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
while (*s_offset == ' ') |
|
s_offset++; |
|
if (sscanf(s_offset, "%ju %ju", offset, len) != 2) { |
|
fprintf(stderr, "%s: Malformed line 3: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
} |
|
|
|
void |
|
get_openat_flags_mode(char *buf, char *flags, mode_t *mode) |
|
{ |
|
char *s, *s2, lookfor; |
|
|
|
s = strchr(buf, ','); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 2; |
|
s = strchr(s, ','); |
|
if (s == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
s += 2; |
|
if (strstr(s, "O_CREAT") == NULL) |
|
lookfor = ')'; |
|
else |
|
lookfor = ','; |
|
s2 = strchr(s, lookfor); |
|
if (s2 == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
*s2 = '\0'; |
|
strcpy(flags, s); |
|
*s2 = lookfor; |
|
if (strstr(s, "O_CREAT") != NULL) { |
|
s = s2 + 2; |
|
s2 = strchr(s, ')'); |
|
if (s2 == NULL) { |
|
fprintf(stderr, "%s: Malformed line: %s\n", |
|
__func__, buf); |
|
exit(EXIT_FAILURE); |
|
} |
|
*s2 = '\0'; |
|
sscanf(s, "%o", mode); |
|
*s2 = ')'; |
|
} |
|
} |
|
|
|
int |
|
lookup_map(char *s, struct flags_map_s *flags_map, int maplen) |
|
{ |
|
int found = 0, flag = 0; |
|
int i; |
|
|
|
while (isspace(*s)) |
|
s++; |
|
for (i = 0 ; i < maplen ; i++) { |
|
if (strcmp(flags_map[i].flag_str, s) == 0) { |
|
flag = flags_map[i].flag; |
|
found = 1; |
|
break; |
|
} |
|
} |
|
if (found == 0) { |
|
fprintf(stderr, "%s: Unknown syscall %s\n", |
|
__func__, s); |
|
exit(1); |
|
} |
|
return flag; |
|
} |
|
|
|
int |
|
map_open_flags(char *s) |
|
{ |
|
int flags = 0; |
|
char *s1; |
|
|
|
while ((s1 = strchr(s, '|'))) { |
|
*s1 = '\0'; |
|
flags |= lookup_map(s, open_flags_map, |
|
ARRAY_SIZE(open_flags_map)); |
|
*s1 = '|'; |
|
s = s1 + 1; |
|
} |
|
/* Last option */ |
|
flags |= lookup_map(s, open_flags_map, |
|
ARRAY_SIZE(open_flags_map)); |
|
return flags; |
|
} |
|
|
|
int |
|
map_lseek_action(char *s) |
|
{ |
|
int flags = 0; |
|
char *s1; |
|
|
|
while ((s1 = strchr(s, '|'))) { |
|
*s1 = '\0'; |
|
flags |= lookup_map(s, lseek_action_map, |
|
ARRAY_SIZE(lseek_action_map)); |
|
*s1 = '|'; |
|
s = s1 + 1; |
|
} |
|
/* Last option */ |
|
flags |= lookup_map(s, lseek_action_map, |
|
ARRAY_SIZE(lseek_action_map)); |
|
return flags; |
|
} |
|
|
|
enum file_op |
|
map_syscall(char *syscall) |
|
{ |
|
return lookup_map(syscall, fileop_map, |
|
ARRAY_SIZE(fileop_map)); |
|
} |
|
|
|
/* |
|
* For each tracefile, we first create in-memory structures, then once |
|
* we've processed each tracefile completely, we write out the in-memory |
|
* structures and free them. |
|
*/ |
|
int main(int argc, char **argv) |
|
{ |
|
FILE *fp; |
|
char path[512]; |
|
char syscall[512]; |
|
char lseek_action_str[512]; |
|
char *s; |
|
char open_flags_str[64]; |
|
void *db_node; |
|
int num_io_operations = 0; |
|
struct ioshark_header header; |
|
struct ioshark_file_operation *disk_file_op; |
|
struct in_mem_file_op *in_mem_fop; |
|
struct stat st; |
|
char *infile, *outfile; |
|
struct timeval prev_time; |
|
char trace_type[64]; |
|
|
|
progname = argv[0]; |
|
if (argc != 3) { |
|
usage(); |
|
exit(EXIT_FAILURE); |
|
} |
|
infile = argv[1]; |
|
outfile = argv[2]; |
|
if (stat(infile, &st) < 0) { |
|
fprintf(stderr, "%s Can't stat %s\n", |
|
progname, infile); |
|
exit(EXIT_FAILURE); |
|
} |
|
if (st.st_size == 0) { |
|
fprintf(stderr, "%s Empty file %s\n", |
|
progname, infile); |
|
exit(EXIT_FAILURE); |
|
} |
|
init_prev_time(&prev_time); |
|
init_filename_cache(); |
|
fp = fopen(infile, "r"); |
|
if (fp == NULL) { |
|
fprintf(stderr, "%s Can't open %s\n", |
|
progname, infile); |
|
exit(EXIT_FAILURE); |
|
} |
|
while (fgets(in_buf, 2048, fp)) { |
|
s = in_buf; |
|
while (isspace(*s)) |
|
s++; |
|
in_mem_fop = malloc(sizeof(struct in_mem_file_op)); |
|
disk_file_op = &in_mem_fop->disk_file_op; |
|
disk_file_op->delta_us = get_delta_ts(s, &prev_time); |
|
get_tracetype(s, trace_type); |
|
if (strcmp(trace_type, "strace") == 0) { |
|
get_syscall(s, syscall); |
|
disk_file_op->file_op = map_syscall(syscall); |
|
} else |
|
disk_file_op->file_op = map_syscall("ftrace"); |
|
get_pathname(s, path, disk_file_op->file_op); |
|
db_node = files_db_add(path); |
|
disk_file_op->fileno = files_db_get_fileno(db_node); |
|
switch (disk_file_op->file_op) { |
|
case IOSHARK_LLSEEK: |
|
case IOSHARK_LSEEK: |
|
get_lseek_offset_action(s, |
|
disk_file_op->file_op, |
|
&disk_file_op->lseek_offset, |
|
lseek_action_str); |
|
disk_file_op->lseek_action = |
|
map_lseek_action(lseek_action_str); |
|
if (disk_file_op->lseek_action == SEEK_SET) |
|
files_db_update_size(db_node, |
|
disk_file_op->lseek_offset); |
|
break; |
|
case IOSHARK_PREAD64: |
|
case IOSHARK_PWRITE64: |
|
get_prw64_offset_len(s, |
|
&disk_file_op->prw_offset, |
|
(u_int64_t *)&disk_file_op->prw_len); |
|
files_db_update_size(db_node, |
|
disk_file_op->prw_offset + |
|
disk_file_op->prw_len); |
|
break; |
|
case IOSHARK_READ: |
|
case IOSHARK_WRITE: |
|
get_rw_len(s, (u_int64_t *)&disk_file_op->rw_len); |
|
files_db_add_to_size(db_node, |
|
disk_file_op->rw_len); |
|
break; |
|
case IOSHARK_MMAP: |
|
case IOSHARK_MMAP2: |
|
get_mmap_offset_len_prot(s, |
|
&disk_file_op->mmap_prot, |
|
&disk_file_op->mmap_offset, |
|
(u_int64_t *)&disk_file_op->mmap_len); |
|
files_db_update_size(db_node, |
|
disk_file_op->mmap_offset + |
|
disk_file_op->mmap_len); |
|
break; |
|
case IOSHARK_OPEN: |
|
disk_file_op->open_mode = 0; |
|
get_openat_flags_mode(s, open_flags_str, |
|
&disk_file_op->open_mode); |
|
disk_file_op->open_flags = |
|
map_open_flags(open_flags_str); |
|
break; |
|
case IOSHARK_FSYNC: |
|
case IOSHARK_FDATASYNC: |
|
break; |
|
case IOSHARK_CLOSE: |
|
break; |
|
case IOSHARK_MAPPED_PREAD: |
|
/* Convert a mmap'ed read into a PREAD64 */ |
|
disk_file_op->file_op = IOSHARK_PREAD64; |
|
get_ftrace_offset_len(s, |
|
&disk_file_op->prw_offset, |
|
(u_int64_t *)&disk_file_op->prw_len); |
|
files_db_update_size(db_node, |
|
disk_file_op->prw_offset + |
|
disk_file_op->prw_len); |
|
break; |
|
default: |
|
break; |
|
} |
|
/* Chain at the end */ |
|
num_io_operations++; |
|
in_mem_fop->next = NULL; |
|
if (in_mem_file_op_head == NULL) { |
|
in_mem_file_op_tail = in_mem_fop; |
|
in_mem_file_op_head = in_mem_fop; |
|
} else { |
|
in_mem_file_op_tail->next = in_mem_fop; |
|
in_mem_file_op_tail = in_mem_fop; |
|
} |
|
} |
|
fclose(fp); |
|
/* |
|
* Now we can write everything out to the output tracefile. |
|
*/ |
|
fp = fopen(outfile, "w+"); |
|
if (fp == NULL) { |
|
fprintf(stderr, "%s Can't open trace.outfile\n", |
|
progname); |
|
exit(EXIT_FAILURE); |
|
} |
|
header.num_io_operations = num_io_operations; |
|
header.num_files = files_db_get_total_obj(); |
|
if (fwrite(&header, sizeof(struct ioshark_header), 1, fp) != 1) { |
|
fprintf(stderr, "%s Write error trace.outfile\n", |
|
progname); |
|
exit(EXIT_FAILURE); |
|
} |
|
files_db_write_objects(fp); |
|
while (in_mem_file_op_head != NULL) { |
|
struct in_mem_file_op *temp; |
|
|
|
disk_file_op = &in_mem_file_op_head->disk_file_op; |
|
if (fwrite(disk_file_op, |
|
sizeof(struct ioshark_file_operation), 1, fp) != 1) { |
|
fprintf(stderr, "%s Write error trace.outfile\n", |
|
progname); |
|
exit(EXIT_FAILURE); |
|
} |
|
temp = in_mem_file_op_head; |
|
in_mem_file_op_head = in_mem_file_op_head->next; |
|
free(temp); |
|
} |
|
store_filename_cache(); |
|
}
|
|
|