Remove file from directory - ext-like file system implementation - c

I have an issue. I'm currently trying to implement an ext-ish file system. I've done the inode operations such as read and write. I've created a structure that represents both a regular file and a directory. I have a problem when trying to remove a certain file from the directory.
char
dirremove(struct dirent *dir, struct dirent *file)
{
dirent_t n = {.mode = NODDIR, .inumber = remdirnod,
.r = 0, .w = 0};
strcpy(n.nm, dir->nm);
dirent_t t;
dir->r = 0;
char r = 1;
while (!dirread(dir, &t))
{
int tt = dir->r;
dir->r = 0;
dirent_t ff[3];
filread(ff, dir, 3 * entrysiz);
dir->r = tt;
if (!strcmp(t.nm, ""))
return 1;
if (!(!strcmp(t.nm, file->nm) && !(r = 0)))
assert(!dirappend(&n, &t));
}
assert(n.w == dir->w - entrysiz);
dir->w = n.w;
dir->r = n.r;
copyinode(dir->inumber, remdirnod);
return r;
}
This is the function called from the rm command. It takes the directory object (where the file is stored) and the file object to be deleted. I know this solution is not the best in terms of speed and memory usage but I'm still a beginner in this area, so don't hate me a lot, please :).
The function is designed to do the following. It has to read all files and check if the current is the one to be deleted. If not, the file is added to a new directory (empty in the beginning) which will replace the old one at the end of the function. The "new" directory is an entry saved entirely for this purpose, so there isn't a chance that all inodes are already used.
The test that I've done is to create a file (works fine), then remove it, then create it again and remove it. Everything works perfectly except for the second execution of the dirremove function. The directory has its dot and dot-dot directories by default so it goes through them first. The result is that the first deletion is successful. Everything works perfectly. But the second time things go wrong somewhere.
int tt = dir->r;
dir->r = 0;
dirent_t ff[3];
filread(ff, dir, 3 * entrysize;
dir->r = tt;
I added the ff array that should read the whole content of the directory and this would help me figure out if the correct files are there. On the first and second iteration, all files (".", ".." and "some-other-file") are there but at the iteration which should hold the object of the file that's to be removed the third file suddenly goes all zeroes.
I've debugged this for several hours but it continues to fail the same way.
Probably I didn't explain the failure the best way, but there are a lot of things that I forgot to say, so if I missed something please don't ignore the question and just ask about it.

Related

Recursive function : abort-condition

We need to create a binary tree which contains content of textfiles. The pointer selection_a and selection_b pointing to another textfile in the directory.
The structure of the textfiles is following:
line: Title
line: OptionA
line: OptionB
line: Text.
The first file is given as parameter while starting the program. All files should be saved at the beginning of the program. Then the text of the first file shows, and the user can input A or B to continue. Based on the selection, the text of File Option A/B is shown and the user can decide again.
The last file of a tree contains no Options: lines 2 and 3 are "-\n".
The problem is, this code only reads all the option A files of the first tree. It doesn't read in any B-Options. In the end, the program shows a memory access error.
I think the problem is that the readingRows function has no abort condition.
current->selection_a = readingRows(input_selection_a);
current->selection_b = readingRows(input_selection_b);
I know the code may be kind of chaotic, but we are beginners in programming. Hope anybody can help us to write an abort-condition.
The function should be aborted if the content of option A (line 3) is "-\n".
Here is the whole function:
struct story_file* readingRows(FILE *current_file)
{
char *buffer = fileSize(current_file);
char *delimiter = "\n";
char *lines = strtok(buffer, delimiter);
int line_counter = 0;
struct story_file *current = malloc(sizeof(struct story_file));
while(lines != NULL)
{
if(line_counter == 0)
{
current->title = lines;
}
else if(line_counter == 1)
{
char *filename_chapter_a = lines;
FILE *input_selection_a = fopen(filename_chapter_a, "r");
if(input_selection_a)
{
current->selection_a = readingRows(input_selection_a);
}
fclose(input_selection_a);
}
else if(line_counter == 2)
{
char *filename_chapter_b = lines;
FILE *input_selection_b = fopen(filename_chapter_b, "r");
if(input_selection_b)
{
current->selection_b = readingRows(input_selection_b);
}
fclose(input_selection_b);
}
else if (line_counter >= 3)
{
current->text = lines;
}
lines = strtok(NULL, delimiter);
line_counter++;
}
return current;
}
There are two items that define a terminating recursive function:
One or more base cases
Recursive calls that move toward a base case
Your code has one base case: while (lines!=NULL) {} return current;, it breaks the while loop when lines is NULL and returns current. In other words, within any particular call to your function, it only terminates when it reaches the end of a file.
Your code moves toward that base case as long as your files do not refer to each other in a loop. We know this because you always read a line, take an action according to your if-else block, and the read the next line. So you always move toward the end of each file you read.
But as you note, the issue is that you don't have a case to handle "no Options", being when lines 2 or 3 are "-\n". So right now, even though you move through files, you are always opening files in line 2. Unless a file is malformed and does not contain a line 2, your recursive call tree never ends. So you just need to add another base case that looks at whether the beginning of lines matches "-\n", and if it does, return before the recursive call. This will end that branch of your recursive tree.
Inside of your while loop, you will need code along the lines of:
if `line_counter` is `2` or `3`
if `lines` starts with your terminating sequence "-\n"
return current
else
`fopen` and make the recursive call
In the parent function that made the recursive call, it will move to the next line and continue as expected.
P.S. Make sure you use free for each malloc you do.

Restore or remove the Linux Kernel Module from sysfs

I recently coded a LKM which has the ability to hide itself. All works just fine when I hide the module but when I restore it and look at it in the lsmod the value of the Used By column suddenly is -2
Module Size Used by
my_module 13324 -2
vboxsf 43798 1
dm_crypt 23177 0
nfsd 284396 2
auth_rpcgss 59309 1 nfsd
nfs_acl 12837 1 nfsd
nfs 240815 0
and when I remove it i get the error saying rmmod: ERROR: Module my_module is builtin. I know that it is a refcount for the kobject associated with the module and the module can only be removed when it is 0. I am almost certain that it happens because when I hide the module I delete all of its files in the /sys/modules.(holders, parameters, sections, srcversion etc.). Can someone help me with the remove operation or restore the files back?(I don't get any errors in the dmesg)
Here is the code:
`
void module_hide(void) {
if(module_hidden) //is hidden
return;
module_prev = THIS_MODULE->list.prev;
kobject_prev = &THIS_MODULE->mkobj.kobj;
kobject_parent_prev = THIS_MODULE->mkobj.kobj.parent;
sect_attrs_bkp = THIS_MODULE->sect_attrs;
notes_attrs_bkp = THIS_MODULE->notes_attrs;
list_del(&THIS_MODULE->list); //remove from procfs
//kobject_del(THIS_MODULE->holders_dir);
kobject_del(&THIS_MODULE->mkobj.kobj); //remove from sysfs
THIS_MODULE->sect_attrs = NULL;
THIS_MODULE->notes_attrs = NULL;
module_hidden = (unsigned int)0x1;
}
void module_show(void) {
int result, result2;
if(!module_hidden) //is not hidden
return;
list_add(&THIS_MODULE->list, module_prev); //add to procfs
result = kobject_add(&THIS_MODULE->mkobj.kobj, kobject_parent_prev, "my_module"); //add the module to sysfs
if(result<0) {
printk(KERN_ALERT "Error to restore the old kobject\n");
}
result2 = kobject_add(THIS_MODULE->holders_dir, &THIS_MODULE->mkobj.kobj, "holders"); //add the holders dir to the module folder
if(!THIS_MODULE->holders_dir) {
printk(KERN_ALERT "Error to restore the old holders_dir\n");
}
THIS_MODULE->sect_attrs = sect_attrs_bkp;
THIS_MODULE->notes_attrs = notes_attrs_bkp;
//kobject_get(&THIS_MODULE->mkobj.kobj);
//tried using THIS_MODULE->refcnt = 0; and kobject_get(&THIS_MODULE->mkobj.kob) with no luck
module_hidden = (unsigned int)0x0;
}
Thanks
Using kobject_add will only add the directory as you already know, while using kobject_dell will remove the direcotry and all subdirectories.
Hence as you mention you need to add all of the subdires needed.
To understand what is the way of adding the subdirs, you read the source code of sys_init_module carefully at module.c or read kobject_del->sys_remove_dir
which remove all attributes(files) and subdirs when clear recursively kobj->kernfs_nodes.
Thus, you need create the struct recursivly with all his attrs using the functions
sysfs_add_file_mode_ns
sysfs_create_dir_ns
or:
__kernfs_create_file
kernfs_create_empty_dir
for example to add the sections file use the follwoing line:
sysfs_create_group(&THIS_MODULE->mkobj.kobj, &sect_attrs_bkp->grp))
You need you change more values in order to fix the problem but to restore the directories this will be enough.
But other solution and perherp easier one would be just to make you module directory unvisible by hijacking getdents_t and getdents64_t as done at Diamorphine.
I solved it
static void populate_sysfs(void)
{
int i;
THIS_MODULE->holders_dir=kobject_create_and_add("holders",&THIS_MODULE->mkobj.kobj);
for (i=0;(THIS_MODULE->modinfo_attrs[i].attr.name) != NULL;i++){
if (sysfs_create_file(&THIS_MODULE->mkobj.kobj,&THIS_MODULE->modinfo_attrs[i].attr)!=0)
break;
}
}

Unique String generator

I want to make a program (network server-client).
One of the specification for this program is next:
The server will receive the sent packages and save it into a file, with a unique name (generated by the server at the moment the transfer starts.
Ex __tf_"unique_random_string".txt
I made a function that returns a pointer to a "unique" string created.
The problem is: If i stop the server and then start it again it will generate the same names.
Ex:this file names were generated and then i stopped the server.
__ft_apqfwk.txt
__ft_arzowk.txt
__ft_cdyggx.txt
I start it again and i try to generate 3 file names. Them will be the same.
Sorry for my english. I'm still learning it.
My function to generate this "unique string" is:
char *create_random_name(void)
{
const char charset[] = "abcdefghijklmnopqrstuvwxyz";
char *file_name;
int i=0;
int key;
if((file_name = malloc(16 * sizeof ( char )) ) == NULL)
{
printf("Failed to alloc memory space\n");
return NULL;
}
strcpy(file_name,"__ft_");
for(i=5 ; i<11 ; i++)
{
key = rand() % (int)(sizeof(charset)-1);
file_name[i]=charset[key];
}
strcat(file_name,".txt");
file_name[15] = '\0';
return file_name;
}
One option is saving to a file the names that have been used, and using them as a checklist. You also want to seed rand with something like srand(time(NULL)).
another is ignoring the randomisation, and just going in order, e.g. aaa, aab aac...aba ,abb etc. Again, save where your cycle is up to on a file.
Your question seems a little bit unclear but if you want to generate a unique string there are a couple of things you can consider:
Get System timestamp ( yyyy-MM-dd-HH-mm-ss-fff-tt)
Use Random function to generate a random number
Combine this with your function and I am sure you will get a unique string.
Hope it helps !
If it's available, you could avoid manually generating random names that might collide and let the system do it for you (and handle collision resolution by creating a new name) by using mkstemps. This is also safer because it opens the file for you, removing the risk of a random name being generated, verified to be unique, then trying to open it and discovering another thread/process raced in and created it.
char name[] = "/path/to/put/files/in/__ft_XXXXXX.txt";
int fd = mkstemps(name, strlen(".txt"));
if (fd == -1) { ... handle error ... }
After mkstemps succeeds, name will hold the path to the file (it's mutated in place, replacing the XXXXXX string), and fd will be an open file descriptor to that newly created file; if you need a FILE*, use fdopen to convert to a stdio type.
Before calling rand(),--- once and only once---, call srand(time()) to initialize the random number generator.
Before settling on any specific file name, call stat() to assure that file name does not already exist.

Reading and selecting files using dirent.h in C

I'm new into programming microcontrollers, and I have a problem.
I'm trying to make a device that plays music from USB. I can read from USB, but I have no idea how to choose a certain file. I'm using dirent.
My code so far:
while (true) {
USBHostMSD msd("usb");
//setup PWM hardware for a Class D style audio output
PWMout.period(1.0/400000.0);
// wait until connected to a USB device
while(!msd.connect()) {
Thread::wait(500);
}
FILE *wave_file;
lcd.cls();
lcd.locate(0,3);
DIR *dir;
struct dirent *ent;
int i=0;
int stevilo_datotek =0;
dir = opendir ("/usb/");
if (dir != NULL)
{
while ((ent = readdir (dir)) != NULL)
{
lcd.printf ("%s\n", ent->d_name[0]);
}
}
Now this code displays what's on USB. How can I navigate through files on USB using the buttons on the device? I'd like to know if there's a way to assign certain song a certain number so I can navigate. I've studied dirent.h file for so long, and I can't find where dirent saves the file order (if it does).
You may be confusing the purpose of dirent.h In short it may be difficult to see the forest for the trees.
You can read the information (ent->d_name, note that ent->d_name is a pointer to an array of chars, often called a 'string') into a data structure (like an array or a list), then use that struct with code that detects button presses to move an index into the array information either up or down (to a greater or lesser index, make sure to check that your index doesn't go outside the range or your structure). Or you can create code where your while loop waits on a button press and only reads the file name then (using seekdir to go backwards).
UPDATE (to response to comment):
Keep in mind that filesystems are tree structures like so:
/ (root) +--- dir1 ----------------------------+- dir1.1
---- dir2 (empty) -- file1.1
---- dir3 ---- dir3.1 +--- file3.1
---- file1 ---- file3.2
---- file2
You have to decide how to handle that, are you going to only support one directory (make them put all their music files in one place), allow them to navigate directories, or look through all directories selecting only files you know how to play?
There is no inherit order to the files (or sub-directories) and in some systems files can be added or deleted at any time.
Here is a very simple-minded example of one way to keep the list of directory entries:
char *names[400]; // make space for 400 names
int ix;
ix = 0;
if (dir != NULL)
{
while ((ent = readdir (dir)) != NULL)
{
lcd.printf ("%s\n", ent->d_name[0]);
// allocate memory to store the name
names[ix] = (char*) malloc(strlen(ent->d_name)); // strlen from string.h
// malloc from stdlib.h
// copy the name from the directory entry
strcpy(names[ix], ent->d_name); // strcpy from string.h
ix++;
if (ix >= 400)
{
// do something because your array isn't big enough
}
}
}
Now you have your names in the array 'names' and can address them by index. The value 'ix-1' is your last name, 0 is your first name. Your button presses can increment/decrement the index into your name array which identifies the name you want. Keep in mind that some of those names might be directory names rather than file names.
Admittedly this is simply-minded, you might want to allocation the array rather than use a fixed value (in fact you have to if you want to pass the 'names' array to a calling function), there are "secure" versions of strcpy (meant to help prevent memory overflow corruption), etc. but it should give you an idea about how you can keep the names in memory.

Create temporary file in C, MS Windows system

Basically, i have a program that is given a 4 meg compressed file, it has to decode this file into uncompressed ~ 100 meg, then compress it back into ~4 meg file. I need to store this intermediate 100 meg file somewhere on the drive (dont want to keep it in memory).
Program is written in C and will be executed on MS Windows 7. At the moment of uncompressing, no guaranteed folder (with write access) is given to the program (folder with source file might be read only and folder with target file might be not yet specified).
This has proven to be not an easy task:
1) I have read about a C function that creates a temp file that will disappear when closed or program is terminated. However, from what i understand it tries to make the file on disk C, in root directory, so this will obviously fail if user has no rights for that (which normal user doesnt)
2) I had an idea to use environmental/system variable TEMP and create a file there, BUT looking on a random Win7 PC which wasnt tweaked, i see that this variable points to c:/windows/temp, and that folder has specific rights for "users" - that is, they have rights to read, execute, create and write files, but not to delete them, check their attributes, etc. This means, i assume, that if program is ran with user privilleges, it will be able to make a file but not able to delete it, so the only way to "delete" it would be to open the file for writing and then close it, making it a 0 length file. This is also not desired, and i dont know how to query for system variables from C
3) So, basically, only idea i have right now is to make a function to open file that:
tries to create a temp file in the output dir, if possible
if failed, tries to create a temp file in input dir
if failed, tries to create a temp file in TEMP dir from system variable
if failed, tries to create a temp file in TMP dir from system variable
and a delete function that:
tries to remove() the file (by its name that is stored somewhere)
if failed, it tries to open the file for write, and close it, so it becomes a 0 byte file
Are there better ideas?
Any help is appreciated, thanks!
PS: Program must not use any external libraries like MFC or something, only built-in standart C functions
GetTempPath
Retrieves the path of the directory designated for temporary files.
GetTempFileName
Creates a name for a temporary file. If a unique file name is
generated, an empty file is created and the handle to it is released;
otherwise, only a file name is generated.
These two provide you easy way to obtain a location and name for a temporary file.
UPD: Code sample on MSDN: Creating and Using a Temporary File.
#include <windows.h>
#include <iostream>
#include <chrono>
#include <string>
#include <cstdio>
#include <chrono>
using namespace std;
int FileExists(string& filepath)
{
DWORD dwAttrib = GetFileAttributes(filepath.c_str());
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
int GetTemporaryFilePath(
string filePrefix,
string fileExt,
string& TmpFilePath /*return*/)
{
if (fileExt[0] == '.')
fileExt.erase(0,1);
char TempPath[MAX_PATH] = { 0 };
if (!GetTempPath(MAX_PATH, TempPath))
return -1;
uint16_t tickint = 0;
while(1) {
const int nowlen = 17; char nowstr[nowlen];
const int ticklen = 5; char tickstr[ticklen];
// Milliseconds since 1970
auto ms = chrono::duration_cast<chrono::milliseconds>(
chrono::system_clock::now().time_since_epoch()
);
__int64 nowint = ms.count();
snprintf(nowstr, nowlen, "%016" "I64" "x", nowint);
snprintf(tickstr, ticklen, "%04x", tickint);
TmpFilePath = string(TempPath)
+ filePrefix
+ "." + string(nowstr)
+ "." + string(tickstr)
+ "." + fileExt;
if (!FileExists(TmpFilePath)) {
//Touch File
FILE* w = fopen(TmpFilePath.c_str(), "w");
fclose(w);
break;
}
tickint++;
}
return 0;
}
int main()
{
string TmpFilePath;
GetTemporaryFilePath("MyFile", ".txt", TmpFilePath);
cout << "TmpFilePath: " << TmpFilePath << endl;
return 0;
}

Resources