I try to get the target of symlink dentries in a kernel module, i.e. the dentry on which my dentry points.
I use this approach:
int print_dentry(struct dentry *d) {
struct path p;
char *buffer, *path_name;
int ret;
buffer = (char *)__get_free_page(GFP_KERNEL);
if (!buffer)
return -ENOMEM;
path_name = dentry_path_raw(d, buffer, PAGE_SIZE);
if (IS_ERR(path_name))
printk(KERN_ERR "ERR");
if ((ret=kern_path(path_name, LOOKUP_FOLLOW, &p))) {
printk("kern_path returned %d for path_name \"%s\", inode %ld\n", ret, path_name, d->d_inode->i_ino);
return 0;
}
printk_once(KERN_INFO "Path %s -> dentry_uid %ld\n", path_name, p.dentry->d_inode->i_ino);
free_page((unsigned long)buffer);
return 0;
}
However, dentry_path_raw doesn't return the absolute path, but a path relative to the vfsmount.
Hence I get errors like this when there is a vfsmount e.g.
kern_path returned -2 for path_name "/self", inode 4026531841
Which corresponds to /proc/self
ls -ial /proc/self
4026531841 lrwxrwxrwx 1 root root 0 5 déc. 04:28 /proc/self -> 1341
Is there a way to get the absolute path so that I can give it to kern_path()? Or maybe another approach to follow the symlink and get the associated dentry?
I don't think I can use directly d_absolute_path or prepend_path since they take as input a struct path* or a char* and I only have a access to a struct dentry* or a char*
Looking at the source code here, it seems that when you call dentry_path_raw() it actually calls another function that does the following:
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Write full pathname from the root of the filesystem into the buffer.
*/
static char *__dentry_path(const struct dentry *d, struct prepend_buffer *p)
{
const struct dentry *dentry;
struct prepend_buffer b;
int seq = 0;
rcu_read_lock();
restart:
dentry = d;
b = *p;
read_seqbegin_or_lock(&rename_lock, &seq);
while (!IS_ROOT(dentry)) {
const struct dentry *parent = dentry->d_parent;
prefetch(parent);
if (!prepend_name(&b, &dentry->d_name))
break;
dentry = parent;
}
if (!(seq & 1))
rcu_read_unlock();
if (need_seqretry(&rename_lock, seq)) {
seq = 1;
goto restart;
}
done_seqretry(&rename_lock, seq);
if (b.len == p->len)
prepend_char(&b, '/');
return extract_string(&b);
}
And everything seems to be in the description of this function. It writes the full path into the buffer, not in its return value, which share it's return with dentry_path_raw().
So, you should use the value pushed into your buffer instead of file_name, which is just the name returned by the function dentry_path_raw().
Note: dentry_path_raw() doesn't resolve symlinks, if you really want to resolve them you should probably use dentry_path() instead.
All these functions are available since linux v2.6.26/v2.6.38
Related
I'm implementing parts of the Linux ls command in C. I want to sort the contents of directories lexicographically, which I've been doing using scandir(). This is easy enough for listing single directories, but I'm having trouble doing it for listing subdirectories recursively. My current code: (results in a segmentation faults once a directory type is reached)
void recursive(char* arg){
int i;
struct dirent **file_list;
int num;
char* next_dir;
num = scandir(arg, &file_list, NULL, alphasort);
for(i = 0; i < num; i++) {
if(file_list[i]->d_type == DT_DIR) {
if(strcmp(".", file_list[i]->d_name) != 0 && strcmp("..", file_list[i]->d_name) != 0) {
// Directories are printed with a colon to distinguish them from files
printf("%s: \n", file_list[i]->d_name);
strcpy(next_dir, arg);
strcat(next_dir, "/");
strcat(next_dir, file_list[i]->d_name);
printf("\n");
recursive(next_dir);
}
} else {
if(strcmp(".", file_list[i]->d_name) != 0 && strcmp("..", file_list[i]->d_name) != 0) {
printf("%s \n", file_list[i]->d_name);
}
}
}
}
int main(void) {
recursive(".");
return 0;
}
There are two recommended methods for traversing entire filesystem trees in Linux and other POSIXy systems:
nftw(): man 3 nftw
Given an initial path, a callback function, the maximum number of descriptors to use, and a set of flags, nftw() will call the callback function once for every filesystem object in the subtree. The order in which entries in the same directory is called is not specified, however.
This is the POSIX.1 (IEEE 1003) function.
fts_open()/fts_read()/fts_children()/fts_close(): man 3 fts
The fts interface provides a way to traverse filesystem hierarchies. The fts_children() provides a linked list of filesystem entries sorted by the comparison function specified in the fts_open() call. It is rather similar to how scandir() returns an array of filesystem entries, except that the two use very different structures to describe each filesystem entry.
Prior to glibc 2.23 (released in 2016), the Linux (glibc) fts implementation had bugs when using 64-bit file sizes (so on x86-64, or when compiling with -D_FILE_OFFSET_BITS=64).
These are BSD functions (FreeBSD/OpenBSD/macOS), but are available in Linux also.
Finally, there is also the atfile version of scandir(), scandirat(), that returns the filtered and sorted filesystem entries from a specific directory, but in addition to the pathname, it takes a file descriptor to the relative root directory to be used as a parameter. (If AT_FDCWD is used instead of a file descriptor, then scandirat() behaves like scandir().)
The simplest option here is to use nftw(), store all walked paths, and finally sort the paths. For example, walk.c:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <locale.h>
#include <ftw.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
struct entry {
/* Insert additional properties like 'off_t size' here. */
char *name; /* Always points to name part of pathname */
char pathname[]; /* Full path and name */
};
struct listing {
size_t max; /* Number of entries allocated for */
size_t num; /* Number of entries in the array */
struct entry **ent; /* Array of pointers, one per entry */
};
#define STRUCT_LISTING_INITIALIZER { 0, 0, NULL }
/* Locale-aware sort for arrays of struct entry pointers.
*/
static int entrysort(const void *ptr1, const void *ptr2)
{
const struct entry *ent1 = *(const struct entry **)ptr1;
const struct entry *ent2 = *(const struct entry **)ptr2;
return strcoll(ent1->pathname, ent2->pathname);
}
/* Global variable used by nftw_add() to add to the listing */
static struct listing *nftw_listing = NULL;
static int nftw_add(const char *pathname, const struct stat *info, int typeflag, struct FTW *ftwbuf)
{
const char *name = pathname + ftwbuf->base;
/* These generate no code, just silences the warnings about unused parameters. */
(void)info;
(void)typeflag;
/* Ignore "." and "..". */
if (name[0] == '.' && !name[1])
return 0;
if (name[0] == '.' && name[1] == '.' && !name[2])
return 0;
/* Make sure there is room for at least one more entry in the listing. */
if (nftw_listing->num >= nftw_listing->max) {
const size_t new_max = nftw_listing->num + 1000;
struct entry **new_ent;
new_ent = realloc(nftw_listing->ent, new_max * sizeof (struct entry *));
if (!new_ent)
return -ENOMEM;
nftw_listing->max = new_max;
nftw_listing->ent = new_ent;
}
const size_t pathnamelen = strlen(pathname);
struct entry *ent;
/* Allocate memory for this entry.
Remember to account for the name, and the end-of-string terminator, '\0', at end of name. */
ent = malloc(sizeof (struct entry) + pathnamelen + 1);
if (!ent)
return -ENOMEM;
/* Copy other filesystem entry properties to ent here; say 'ent->size = info->st_size;'. */
/* Copy pathname, including the end-of-string terminator, '\0'. */
memcpy(ent->pathname, pathname, pathnamelen + 1);
/* The name pointer is always to within the pathname. */
ent->name = ent->pathname + ftwbuf->base;
/* Append. */
nftw_listing->ent[nftw_listing->num++] = ent;
return 0;
}
/* Scan directory tree starting at path, adding the entries to struct listing.
Note: the listing must already have been properly initialized!
Returns 0 if success, nonzero if error; -1 if errno is set to indicate error.
*/
int scan_tree_sorted(struct listing *list, const char *path)
{
if (!list) {
errno = EINVAL;
return -1;
}
if (!path || !*path) {
errno = ENOENT;
return -1;
}
nftw_listing = list;
int result = nftw(path, nftw_add, 64, FTW_DEPTH);
nftw_listing = NULL;
if (result < 0) {
errno = -result;
return -1;
} else
if (result > 0) {
errno = 0;
return result;
}
if (list->num > 2)
qsort(list->ent, list->num, sizeof list->ent[0], entrysort);
return 0;
}
int main(int argc, char *argv[])
{
struct listing list = STRUCT_LISTING_INITIALIZER;
setlocale(LC_ALL, "");
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
fprintf(stderr, " %s .\n", arg0);
fprintf(stderr, " %s TREE [ TREE ... ]\n", arg0);
fprintf(stderr, "\n");
fprintf(stderr, "This program lists all files and directories starting at TREE,\n");
fprintf(stderr, "in sorted order.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
for (int arg = 1; arg < argc; arg++) {
if (scan_tree_sorted(&list, argv[arg])) {
fprintf(stderr, "%s: Error scanning directory tree: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
}
}
printf("Found %zu entries:\n", list.num);
for (size_t i = 0; i < list.num; i++)
printf("\t%s\t(%s)\n", list.ent[i]->pathname, list.ent[i]->name);
return EXIT_SUCCESS;
}
Compile using gcc -Wall -Wextra -O2 walk.c -o walk, and run using e.g. ./walk ...
The scan_tree_sorted() function calls nftw() for the directory specified, updating the global variable nftw_listing so that the nftw_add() callback function can add each new directory entry to it. If the listing contains more that one entry afterwards, it is sorted using qsort() and a locale-aware comparison function (based on strcoll()).
nftw_add() skips . and .., and adds every other pathname to the listing structure nftw_listing. It automatically grows the array as needed in linear fashion; the new_max = nftw_listing->num + 1000; means we allocate in units of a thousand (pointers).
The scan_tree_sorted() can be called multiple times with the same listing as the target, if one wants to list disjoint subtrees in one listing. Note, however, that it does not check for duplicates, although those could easily be filtered out after the qsort.
In the book Operating System Concepts: 10th edition, there's a programming project to log some information about a process by providing its pid echo'd to the /proc entry made while initializing the module, namely here, /proc/pid.
/**
* This function is called each time the /proc/pid is read.
*
* This function is called repeatedly until it returns 0, so
* there must be logic that ensures it ultimately returns 0
* once it has collected the data that is to go into the
* corresponding /proc file.
*/
static ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos)
{
int rv = 0;
char buffer[BUFFER_SIZE];
static int completed = 0;
struct task_struct *tsk = NULL;
if (completed) {
completed = 0;
return 0;
}
/* l_pid is a global that gets set properly when the proc entry is read. */
tsk = pid_task(find_vpid(l_pid), PIDTYPE_PID);
if (tsk == NULL) {
rv = -1;
return rv;
}
completed = 1;
rv = sprintf(buffer, "pid = [%d], command = [%s], state = [%ld]\n",
tsk->pid, tsk->comm, tsk->state);
if (copy_to_user(usr_buf, buffer, rv)) {
rv = -1;
}
return rv;
}
My problem is with the proc_read function. The way I understand it (as explained in the book) is that rv has to be 0 eventually because the function specified as the .read field in the file_operations struct keeps getting called till returning 0.
The problem is that I do not understand the necessity of having all this "complete" logic. If I change the code to be like this:
static ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos)
{
int rv = 0;
char buffer[BUFFER_SIZE];
struct task_struct *tsk = NULL;
/* l_pid is a global that gets set properly when the proc entry is read. */
tsk = pid_task(find_vpid(l_pid), PIDTYPE_PID);
if (tsk == NULL) {
rv = -1;
return rv;
}
int num_bytes = sprintf(buffer, "pid = [%d], command = [%s], state = [%ld]\n",
tsk->pid, tsk->comm, tsk->state);
if (copy_to_user(usr_buf, buffer, num_bytes)) {
rv = -1;
}
return rv;
}
To my understanding, when the proc entry is read, this function will get called, either returning -1 (if tsk == NULL or could not copy from buffer to usr_buf) and thus get called again. Or it will return 0 on success.
The problem with this change (of course) is that it does not work. It does not print anything to the console when issuing cat /proc/pid. However, by printking the string inside buffer, it appears to be correct (at least in the kernel log buffer).
I have a very simple modified driver to write to specific registers. I pass the value and the register I intend to write to. IE: 0x00000008 2, read as write 8 to register (base_addr + 2*4).
This works and was relatively straight forward to implement because the driver automatically receives an input buffer.
Now since in order to read I have to use the seq_file I'm not sure how to pass the register that I would like to read from... The augmented device driver I'm using is posted below.
Is there a way to pass the register value and then using ioread32(base_addr + register)? How can I use copy_from_user in this function where my only inputs are the seq_file?
Usage Example (altered ioread32 to be base_addr+2):
$ sudo echo "0x00001233 2" > /proc/accelerator
$ cat /proc/accelerator
0x1233
$
Dev Driver Code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h> /* Needed for copy_from_user */
#include <asm/io.h> /* Needed for IO Read/Write Functions */
#include <linux/proc_fs.h> /* Needed for Proc File System Functions */
#include <linux/seq_file.h> /* Needed for Sequence File Operations */
#include <linux/platform_device.h> /* Needed for Platform Driver Functions */
#include <linux/slab.h> /*for kmalloc and kfree */
#include <linux/vmalloc.h>
/* Define Driver Name */
#define DRIVER_NAME "accelerator"
unsigned long *base_addr; /* Vitual Base Address */
struct resource *res; /* Device Resource Structure */
unsigned long remap_size; /* Device Memory Size */
/* Write operation for /proc/accelerator
* -----------------------------------
* When user cat a string to /proc/accelerator file, the string will be stored in
* const char __user *buf. This function will copy the string from user
* space into kernel space, and change it to an unsigned long value.
* It will then write the value to the register of accelerator controller,
* and turn on the corresponding LEDs eventually.
*/
static ssize_t proc_accelerator_write(struct file *file, const char __user * buf,
size_t count, loff_t * ppos)
{
//Allocate
char * myaddr_phrase;
char * pEnd;
char *buffer = vzalloc(count);
myaddr_phrase = buffer;
u32 myaddr_value;
u32 myreg_value;
//Copy Data
if (count < 22) {
if (copy_from_user(myaddr_phrase, buf, count))
return -EFAULT;
//myaddr_phrase[count] = '\0';
printk("count = %d\n", count);
printk("%s\n",myaddr_phrase);
}
// Use strtol to parse input
/* http://www.cplusplus.com/reference/cstdlib/strtol/ */
myaddr_value = simple_strtoul(myaddr_phrase, &pEnd, 0);
printk("myaddr_value = %08x\n", myaddr_value);
pEnd = strsep(&myaddr_phrase," ");
myreg_value = simple_strtoul(myaddr_phrase,&pEnd ,0);
printk("myreg_value = %08x\n", myreg_value);
printk("final_value = %08x\n", (base_addr + (myreg_value)));
printk("mult_val = %08x\n", (myreg_value));
wmb();
iowrite32(myaddr_value, (base_addr + (myreg_value)));
return count;
}
/* Callback function when opening file /proc/accelerator
* ------------------------------------------------------
* Read the register value of accelerator controller, print the value to
* the sequence file struct seq_file *p. In file open operation for /proc/accelerator
* this callback function will be called first to fill up the seq_file,
* and seq_read function will print whatever in seq_file to the terminal.
*/
static int proc_accelerator_show(struct seq_file *p, void *v)
{
u32 accelerator_value;
accelerator_value = ioread32(base_addr+2);
seq_printf(p, "0x%x\n", accelerator_value);
return 0;
}
/* Open function for /proc/accelerator
* ------------------------------------
* When user want to read /proc/accelerator (i.e. cat /proc/accelerator), the open function
* will be called first. In the open function, a seq_file will be prepared and the
* status of accelerator will be filled into the seq_file by proc_accelerator_show function.
*
*p 69
int (*open) (struct inode *, struct file *);
Though this is always the first operation performed on the device file, the driver
is not required to declare a corresponding method. If this entry is NULL, opening
the device always succeeds, but your driver isn’t notified.
Open described on p76
*/
static int proc_accelerator_open(struct inode *inode, struct file *file)
{
unsigned int size = 16;
char *buf;
struct seq_file *m;
int res;
buf = (char *)kmalloc(size * sizeof(char), GFP_KERNEL);
if (!buf)
return -ENOMEM;
res = single_open(file, proc_accelerator_show, NULL);
if (!res) {
m = file->private_data;
m->buf = buf;
m->size = size;
} else {
kfree(buf);
}
return res;
}
/* File Operations for /proc/accelerator */
static const struct file_operations proc_accelerator_operations = {
.open = proc_accelerator_open,
.read = seq_read,
.write = proc_accelerator_write,
.llseek = seq_lseek,
.release = single_release
};
/*
int (*open) (struct inode *, struct file *);
Though this is always the first operation performed on the device file, the driver
is not required to declare a corresponding method. If this entry is NULL, opening
the device always succeeds, but your driver isn’t notified.
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
Used to retrieve data from the device. A null pointer in this position causes the
readsystem call to fail with-EINVAL(“Invalid argument”). A nonnegative return
value represents the number of bytes successfully read (the return value is a
“signed size” type, usually the native integer type for the target platform)
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
Sends data to the device. IfNULL, -EINVALis returned to the program calling the
writesystem call. The return value, if nonnegative, represents the number of
bytes successfully written.
loff_t (*llseek) (struct file *, loff_t, int);
Thellseek method is used to change the current read/write position in a file, and
the new position is returned as a (positive) return value. Theloff_tparameter is
a “long offset” and is at least 64 bits wide even on 32-bit platforms. Errors are
signaled by a negative return value. If this function pointer isNULL, seek calls will
modify the position counter in thefilestructure (described in the section “The
file Structure”) in potentially unpredictable ways.
int (*release) (struct inode *, struct file *);
This operation is invoked when thefilestructure is being released. Likeopen,
releasecan beNULL.
*
*/
/* Shutdown function for accelerator
* -----------------------------------
* Before accelerator shutdown, turn-off all the leds
*/
static void accelerator_shutdown(struct platform_device *pdev)
{
iowrite32(0, base_addr);
}
/* Remove function for accelerator
* ----------------------------------
* When accelerator module is removed, turn off all the leds first,
* release virtual address and the memory region requested.
*/
static int accelerator_remove(struct platform_device *pdev)
{
accelerator_shutdown(pdev);
/* Remove /proc/accelerator entry */
remove_proc_entry(DRIVER_NAME, NULL);
/* Release mapped virtual address */
iounmap(base_addr);
/* Release the region */
release_mem_region(res->start, remap_size);
return 0;
}
/* Device Probe function for accelerator
* ------------------------------------
* Get the resource structure from the information in device tree.
* request the memory region needed for the controller, and map it into
* kernel virtual memory space. Create an entry under /proc file system
* and register file operations for that entry.
*/
static int accelerator_probe(struct platform_device *pdev)
{
struct proc_dir_entry *accelerator_proc_entry;
int ret = 0;
printk(KERN_ALERT "Probing\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "No memory resource\n");
return -ENODEV;
}
remap_size = res->end - res->start + 1;
if (!request_mem_region(res->start, remap_size, pdev->name)) {
dev_err(&pdev->dev, "Cannot request IO\n");
return -ENXIO;
}
base_addr = ioremap(res->start, remap_size);
if (base_addr == NULL) {
dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n",
(unsigned long)res->start);
ret = -ENOMEM;
goto err_release_region;
}
accelerator_proc_entry = proc_create(DRIVER_NAME, 0, NULL,
&proc_accelerator_operations);
if (accelerator_proc_entry == NULL) {
dev_err(&pdev->dev, "Couldn't create proc entry\n");
ret = -ENOMEM;
goto err_create_proc_entry;
}
printk(KERN_INFO DRIVER_NAME " probed at VA 0x%08lx\n",
(unsigned long) base_addr);
printk(KERN_ALERT "Goodbye, probe\n");
return 0;
err_create_proc_entry:
iounmap(base_addr);
err_release_region:
release_mem_region(res->start, remap_size);
return ret;
}
/* device match table to match with device node in device tree */
/*
https://lwn.net/Articles/448502/
*/
static const struct of_device_id accelerator_of_match[] = {
{.compatible = "PCA,bitSplitter"},
{},
};
MODULE_DEVICE_TABLE(of, accelerator_of_match);
/* platform driver structure for accelerator driver */
/*
Platform devices are represented by the struct, and is found in <linux/platform_device.h>
at minimum probe() and remove() must be supplied, the others have to do with power management
https://lwn.net/Articles/448499/
*/
static struct platform_driver accelerator_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = accelerator_of_match},
.probe = accelerator_probe,
.remove = accelerator_remove,
.shutdown = accelerator_shutdown
};
/* Register accelerator platform driver */
/*
Helper macro for drivers that don't do
* anything special in module init/exit. This eliminates a lot of
* boilerplate. Each module may only use this macro once, and
* calling it replaces module_init() and module_exit()
*
* Platform drivers are for HW that will not dynamically come and go into a Linux system,
* such as the video and audio controllers in a tablet. In makes sense to statically pull
* in those code necessary through the __initcall magic discussed above.
*
* http://henryomd.blogspot.com/2014/11/linux-kernel-startup.html
*/
module_platform_driver(accelerator_driver);
/* Module Informations */
/*
Discussed in 2.6 Preliminaries
*/
MODULE_AUTHOR("Digilent, Inc.");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRIVER_NAME ": accelerator driver (Simple Version)");
MODULE_ALIAS(DRIVER_NAME);
This is probably something simple, but I can't figure out why I can't get the value from this char*.
Here is my issue:
static char* DIR_ENTRY_PATH = NULL;
...
while (1) // infinite loop
{
// accept a client connection
clientFd = accept (serverFd, clientSockAddrPtr, &clientLen);
if (fork() == 0) // Create child proc to get dir entry info
{
//read dir entry info
readInfo(clientFd);
printf("dpath: %s\n", DIR_ENTRY_PATH); //prints out the the correct value (DIR_ENTRY_PATH set in readInfo)
int test = 1;
//get dir entry info and write back
DIR *dir;
struct dirent *entry; //pointer to dir entry
struct stat stbuf; //contains file info
if((dir = opendir(DIR_ENTRY_PATH)) == NULL){ //make sure entry is valid
printf("error with dirent\n");
exit(1);
}
else{
printf("gathering directory entry info...\n");
while((entry = readdir(dir)) != NULL){
char *entryname = entry->d_name;
printf("path: %s\n", DIR_ENTRY_PATH); /*prints nothing out.. */ - PROBLEM IS HERE
printf("int: %d\n", test); //*prints 1 out.. */
...
readInfo():
//reads info from the client
void readInfo(int fdesc){
int fd = fdesc;
char str[200];
readLine(fd, str); //read line in from socket
DIR_ENTRY_PATH = str;
printf("received path: %s\n", DIR_ENTRY_PATH); //displays correct value
}
//reads a single line
int readLine(int fdesc, char *strng){
int fd = fdesc;
char *str = strng;
int n;
do{
n = read(fd,str, 1); //read a single character
}while(n > 0 && *str++ != 0);
return (n>0); //return false if eoi
}//end readLine
Why am I able to get the value of the test int but not the dir_entry_path? Thanks for any help.
You are assigning a pointer to a local variable to your global variable, but once the function returns, the local variable is gone!
void readInfo(int fdesc){
int fd = fdesc;
char str[200]; // Local variable
readLine(fd, str);
DIR_ENTRY_PATH = str; // Pointer assigned to global
printf("received path: %s\n", DIR_ENTRY_PATH); //displays correct value
}
After the function returns, the local variable is undefined, and its storage can be reused by the next function, etc.
How to fix the problem? There are many possible ways, but the simplest might be:
static char DIR_ENTRY_PATH[1024];
void readInfo(int fdesc){
int fd = fdesc;
char str[200]; // Local variable
readLine(fd, str);
strcpy(DIR_ENTRY_PATH, str); // Copy local string into global string
printf("received path: %s\n", DIR_ENTRY_PATH); //displays correct value
}
As a stylistic point, ALL_CAPS normally denotes a macro, or an enum value (because enum is a bit like #define which uses ALL_CAPS).
I hope you have proper bounds checking on readLine(). I got lazy with my modification and simply ensured the global was (five times) longer than the local variable. Tune to suit yourself. I'd also use an enum for the buffer size (and a lower-case name instead of DIR_ENTRY_PATH).
You can allocate memory to your global pointer DIR_ENTRY_PATH before calling readInfo() or inside the function readInfo() depending on your wish.
#define DIR_ENTRY_LEN 200
void readInfo(int fdesc){
....
// Add below code <br>
strncpy(DIR_ENTRY_PATH, str, DIR_ENTRY_LEN);
I have a code to mimic ls -la in ansi C, but when I change the directory from . (current directory) to any other it keep saying No such file or directory, any ideas why?
code:
DIR * mydir;
struct dirent * mydirent;
struct stat st;
char outstr[100];
struct tm *tmp;
mydir = opendir("..");
while ((mydirent=readdir(mydir))!=NULL)
if ( stat(mydirent->d_name,&st) != -1 ) {
tmp = localtime(&st.st_mtime);
if (tmp == NULL)
perror("localtime ERROR: ");
else {
strftime(outstr, sizeof(outstr), "%d/%m/%Y", tmp);
printf("%o\t%d\t%d\t%d\t%d\t%s\t%s\n",
st.st_mode, st.st_nlink, st.st_uid, st.st_gid,
st.st_size, outstr, mydirent->d_name);
}
}
else
perror("stat ERROR: ");
closedir(mydir);
You need to concatenate the directory path and the file name.
stat(mydirent->d_name,&st) /* d_name is just the name, not the full path. */
Use s(n)printf or something like that:
sprintf(fullpath, "%s/%s", dirpath, mydirent->d_name);
stat(fullpath, &st);
The problem is, as already stated by #cnicutar, that stat wants the filename in the form dir/file. The problems are:
The / may not work on every operating system.
You need to allocate memory for the composed string, do overflow checks ...
If you don't insist on ANSI and can live with POSIX instead, then you can try fstatat and dirfd:
int dirfd(DIR *dirp); // convert DIR * from opendir() to dirhandle
int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags);
With fstatat the pathname is relative to the directory handle and you can point pathname directly to struct dirent.d_name.