Executing binary/elf-file directly from (shared)-memory in C - c

we are trying to copy a binary/elf file into a shared-memory region of our system and then execute it thereafter. We don't want to call our "client"-program directly, since we need to execute it from the memory itself for our purpose.
While we know that our approach (described below) won't really work, we are (obviously) trying to get it to work. How would it be possible to copy a binary/elf/etc. file directly into the (shared)-memory and execute it thereafter? Maybe we just compiled it in the wrong way? Or something else was done wrong?
We also don't want to convert it into hex/shell-code, we already did that. We are looking for an easier and more practical solution.
Is anyone able to help? Would be much appreciated!
Two programs:
"Host"-Program (copy & execute client-program in shared memory)
"Client"-Program (basically a hello-world echo)
"Client"-Program:
#include <stdio.h>
int main()
{
printf("Hello, World!\n");
return 0;
}
Compiled with gcc -o binfile clientprogram.c -static.
"Host"-Program:
#include <string.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
FILE *fp; //filepointer
size_t size; //filesize
unsigned char *buffer; //buffer
fp = fopen("binfile","rb");
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
buffer = (unsigned char *) malloc(size);
if (fp == NULL){ //file empty?
printf("Error: There was an Error reading the file %s \n", "binfile");
exit(1);
}
else if (fread(buffer, sizeof *buffer, size, fp) != size){
printf("Error: There was an Error reading the file %s\n", "binfile");
exit(1);
}else{
int i;
// for(i=0; i<size;i++){
// printf("%02x", buffer[i]);
// }
}
void *mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memcpy(mem, buffer, size);
mprotect(mem, size, PROT_READ|PROT_WRITE|PROT_EXEC);
void (*func)();
func = (void (*)()) buffer;
func();
munmap(mem, size);
fclose(fp);
free(buffer);
return 0;
}
Compiled with gcc hostprogram.c.

Build the client as a PIE, with -rdynamic. Then you'll be able to dlopen() it and dlsym() its main symbol (dlopen() will do the mmaping and mprotecting for you, as you'll be able to see if you strace the program), after which you'll be able to run its main from within the address space of the host.
Example:
#!/bin/sh
cat > client.c <<EOF
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("Hello, World!: from %ld\n", (long)getpid());
return 0;
}
EOF
gcc -fpic -c client.c
gcc -pie -rdynamic -o client client.o
cat > host.c <<EOF
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("Hello, I'm your host: %ld\n", (long)getpid()); ;
void *client_hndl;
typedef int main_t(int, char**);
main_t *client_main;
client_hndl = dlopen("./client", RTLD_LAZY);
if (!client_hndl){
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
client_main = (main_t*)dlsym(client_hndl, "main");
if (!client_main){
fprintf(stderr, "%s\n", dlerror());
exit(2);
}
return client_main(1, (char*[]){"client", 0});
}
EOF
gcc host.c -ldl
./client
echo =============
./a.out
Example output:
Hello, World!: from 14520
=============
Hello, I'm your host: 14521
Hello, World!: from 14521

You are looking for a solution to this GLIBC feature request.
This feature request is 7 years old, and it's somewhat unlikely that anything will happen with it any time soon.
Your best bet is probably to do roughly what you are already doing (building a fully-static binary).
Your approach doesn't work because the executable you built requires to be loaded at the address it was linked at (visible in readelf -l binfile as the address of the first PT_LOAD segment. You would need to mmap your binfile there with MAP_FIXED, no other address will do.
You also need to read and decode the Elf{32,64}_Ehdr that is found at the beginning of the file to find entry point to jump to. You currently are jumping to the ELF header itself, but that header is not where the execution should start.

Related

Doesn't GCC recognize the function "read"?

I was solving the pwnable.kr's fd problem and wondered how does the fd.c code works.
So I copied the c code and I put it on GCC to see how it works. And it has an error says: "implicit declaration of function ‘read’; did you mean ‘fread’?" Does GCC not recognize the Read function on C?
The code looks like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
Thank you
From the man page (man 2 read) :
NAME
read - read from a file descriptor
SYNOPSIS
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
You must include unistd.h and the warning will go away.

C Shared Memory Reader-Writer Segmentation Fault

Here is a Synchronized Reader and Writer. The target is passing data between these two Processes via a Shared Memory.
The Writer opens a Shared Memory through a Structure and writes Some Data. I am getting Segmentation Fault(Core Dumped) error message.
The code is compiled through the following command in Ubuntu.
g++ Writer.c -o Writer -lrt
g++ Reader.c -o Reader -lrt
And these two Processes are run by-
./Writer
./Reader
The Writer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main(void){
struct MemData{
char* FileName;
int LastByteLength;
int ReadPointer;
int WritePointer;
char Data[512000];//MEMORY BLOCK SIZE: 500 KB
};
int SD;
struct MemData *M;
int NumberOfBuffers=10;
int BufferSize=51200;//FILE BUFFER SIZE 50 KB
SD= shm_open("/program.shared", O_RDWR|O_CREAT, S_IREAD|S_IWRITE);
if(SD< 0){
printf("\nshm_open() error \n");
return EXIT_FAILURE;
}
fchmod(SD, S_IRWXU|S_IRWXG|S_IRWXO);
if(ftruncate(SD, sizeof(MemData))< 0){
printf ("ftruncate() error \n");
return EXIT_FAILURE;
}
//THE FOLLOWING TYPECASTING AVOIDS THE NEED TO ATTACH THROUGH shmat() in shm.h HEADER I GUESS.
M=(struct MemData*)mmap(NULL, sizeof(MemData), PROT_READ|PROT_WRITE, MAP_SHARED, SD, 0);
if(M== MAP_FAILED){
printf("mmap() error");
return EXIT_FAILURE;
}else{
M->FileName=(char*)"xaa";
M->LastByteLength=0;
M->ReadPointer=-1;
M->WritePointer=-1;
memset(M->Data, '\0', strlen(M->Data));
}
/*
FILE *FP= fopen(FileName, "rb");
if(FP!= NULL){
unsigned long int FilePosition;
fseek(FP, 0, SEEK_SET);
FilePosition=ftell(FP);
fclose(FP);
}
*/
close(SD);
return 0;
}
The Reader.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main(void){
struct MemData{
char* FileName;
int LastByteLength;
int ReadPointer;
int WritePointer;
char Data[512000];//MEMORY BLOCK SIZE: 500 KB
};
int SD;
struct MemData *M;
int NumberOfBuffers=10;
int BufferSize=51200;//FILE BUFFER SIZE 50 KB
SD= shm_open("/program.shared", O_RDWR|O_CREAT, S_IREAD|S_IWRITE);
if(SD< 0){
printf("\nshm_open() error \n");
return EXIT_FAILURE;
}
fchmod(SD, S_IRWXU|S_IRWXG|S_IRWXO);
if(ftruncate(SD, sizeof(MemData))< 0){
printf ("ftruncate() error \n");
return EXIT_FAILURE;
}
//THE FOLLOWING TYPECASTING AVOIDS THE NEED TO ATTACH THROUGH shmat() in shm.h HEADER I GUESS.
M=(struct MemData*)mmap(NULL, sizeof(MemData), PROT_READ|PROT_WRITE, MAP_SHARED, SD, 0);
if(M== MAP_FAILED){
printf("mmap() error");
return EXIT_FAILURE;
}else{
printf("\n%s", M->FileName);
printf("\n%d", M->LastByteLength);
printf("\n%d", M->ReadPointer);
printf("\n%d", M->WritePointer);
}
/*
FILE *FP= fopen(FileName, "rb");
if(FP!= NULL){
unsigned long int FilePosition;
fseek(FP, 0, SEEK_SET);
FilePosition=ftell(FP);
fclose(FP);
}
*/
munmap(M,sizeof(MemData));
close(SD);
return 0;
}
Based on your comments, the issue is because of the way you're assigning and passing the FileName value.
M->FileName=(char*)"xaa";
This results in M->FileName holding a pointer to a string in the writer process' memory. Dereferencing this pointer in the reader process results in a segmentation fault due to the filename being stored in the writer process memory, which is not shared with the reader. You need to store the characters themselves in the shared memory, not a pointer to writer process memory.
If you can safely assume the maximum length of the filename string, you can change your struct to store the entire string rather than a pointer: change char* FileName; to char FileName[256]; or some other fixed length value. You will need to use strcpy rather than direct assignment after making this change: change M->FileName=(char*)"xaa"; to strcpy(M->FileName, "xaa");.
If you want a dynamic length string, you can call mmap again to allocate shared memory for just the string, and then store the pointer to this shared memory string in FileName.

How to get device name on which a file is located from its path in c?

Let's say I have a file in Linux with this path:
/path/to/file/test.mp3
I want to know the path to its device. For example I want to get something like:
/dev/sdb1
How do I do this with the C programming language?
I know the terminal command to do it, but I need C functions that will do the job.
EDIT:
I have read this question before asking mine. It doesn't concretly mention code in C, it's more related to bash than to the C language.
Thanks.
You need to use stat on the file path, and get the device ID st_dev and match that to a device in /proc/partitions
Read this for how to interpret st_dev: https://web.archive.org/web/20171013194110/http://www.makelinux.net:80/ldd3/chp-3-sect-2
I just needed that inside a program I am writing...
So instead of running "df" and parsing the output, I wrote it from scratch.
Feel free to contribute!
To answer the question:
You first find the device inode using stat() then iterate and parse /proc/self/mountinfo to find the inode and get the device name.
/*
Get physical device from file or directory name.
By Zibri <zibri AT zibri DOT org>
https://github.com/Zibri/get_device
*/
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <libgen.h>
int get_device(char *name)
{
struct stat fs;
if (stat(name, &fs) < 0) {
fprintf(stderr, "%s: No such file or directory\n", name);
return -1;
}
FILE *f;
char sline[256];
char minmaj[128];
sprintf(minmaj, "%d:%d ", (int) fs.st_dev >> 8, (int) fs.st_dev & 0xff);
f = fopen("/proc/self/mountinfo", "r");
if (f == NULL) {
fprintf(stderr, "Failed to open /proc/self/mountinfo\n");
exit(-1);
}
while (fgets(sline, 256, f)) {
char *token;
char *where;
token = strtok(sline, "-");
where = strstr(token, minmaj);
if (where) {
token = strtok(NULL, " -:");
token = strtok(NULL, " -:");
printf("%s\n", token);
break;
}
}
fclose(f);
return -1;
}
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage:\n%s FILE OR DIRECTORY...\n", basename(argv[0]));
return -1;
}
get_device(argv[1]);
return 0;
}
output is just the device name.
Example:
$ gcc -O3 getdevice.c -o gd -Wall
$ ./gd .
/dev/sda4
$ ./gd /mnt/C
/dev/sda3
$ ./gd /mnt/D
/dev/sdb1
$
Use this command to print the partition path:
df -P <pathname> | awk 'END{print $1}'

Segmentation Fault when moving binary outside directory

Hello guys this is my first post here.
My problem is stupid I think but I can't find any solution, hope you can help me!
So, me and a friend are coding a small system monitor (learn better/fun), the code has 2 sections: the daemon and the command line interface (for now), when I compile the CLI section all went great, the daemon is particular one, because when I compile and I execute it in the compile directory it works without error! Magically, when I move out of the compile directory it gives me a segmentation fault!
Compiler: GCC
Here is the repository: https://github.com/StefanoBelli/JASM
Makefile:
#!/usr/bin/make -f
SHELL=/bin/sh
#### CONFIGURATION ####
CC=gcc
DEBUG=-g
CFLAGS=-O2 -pipe -Wall -std=c11 $(DEBUG)
LIBS=
BINOUT=jasm
#### SOURCES & RULES ####
OBJS:=$(patsubst %.c,%.o,$(wildcard *.c))
install:$(OBJS)
$(CC) $(CFLAGS) $(LIBS) -o $(BINOUT) $(OBJS)
clean:
rm -fv *.o
.PHONY: install,clean
GDB Output:
(gdb) run
Starting program: /home/stefanozzz123/Devel/C.Cpp/JASM/bin/jasm
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a7db04 in vfprintf () from /usr/lib/libc.so.6
(gdb)
Thank all of you guys! :)
EDIT: As you requested here is code:
jasm.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "queue.h"
#include "miscellaneous.h"
#include "ipc.h"
int main(int argc, char *argv[])
{
start_daemon();
start_server();
}
ipc.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include "ipc.h"
#include "miscellaneous.h"
#include "getter.h"
static void excecute_command(int fd, char *command)
{
/*
* if get* -> modulo get
* if start* -> modulo dei moduli
*/
// ************************** getter ***************************************
if(strncmp("get", command, 3)==0) { //ricevuto comando getter
int i;
//char buf[BUFSIZ];
strcpy(command, &command[3]);
for(i=0; i<NGETTER; i++) {
if(strcmp(getterName[i], command)==0) { //se esiste getter
log_string("getter found :)");
getterFunction[i](fd);
return;
}
}
log_error("getter NOT found :(");
write(fd, "null\0", 4);
return;
}
// ************************** starter **************************************
if(strncmp("start", command, 5)==0) { //ricevuto start modulo
log_error("starter NOT found :(");
write(fd, "null\0", 4);
return;
}
// ************************** miscellaneous ********************************
if(strcmp("halt", command)==0) { //spegne jasm
log_string("# halt and catch fire, done");
write(fd, "halt\0", 4);
exit(0);
}
/*if(strcmp("getVersion", command)==0) {
write(fd, (void *)VERSION, sizeof(VERSION));
log_string("server reply <version> with success");
return;
}*/
log_error("request not found");
write(fd, "null\0", 4);
}
void start_server()
{
int server_sockfd, client_sockfd;
int server_len;
socklen_t client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
int result;
fd_set readfds, testfds;
server_sockfd=socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family=AF_INET;
server_address.sin_addr.s_addr=htonl(INADDR_ANY);
server_address.sin_port=htons(SERVER_PORT);
server_len=sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
listen(server_sockfd, 5);
FD_ZERO(&readfds);
FD_SET(server_sockfd, &readfds);
log_string("server started");
while(1) {
char buf[BUFSIZ];
char received[BUFSIZ];
int fd;
int nread;
testfds=readfds;
result=select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
if(result<1) {
log_error("server fail");
exit(1);
}
for(fd=0; fd<FD_SETSIZE; fd++) {
if(FD_ISSET(fd, &testfds)) {
if(fd==server_sockfd) {
client_len=sizeof(client_address);
client_sockfd=accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
FD_SET(client_sockfd, &readfds);
sprintf(buf, "adding client on fd %d", client_sockfd);
log_string(buf);
} else {
ioctl(fd, FIONREAD, &nread);
if(nread==0) {
close(fd);
FD_CLR(fd, &readfds);
sprintf(buf, "removing client on fd %d", fd);
log_string(buf);
} else {
read(fd, &received, BUFSIZ);
sprintf(buf, "received from fd %d command <%s>", fd, received);
log_string(buf);
excecute_command(fd, received);
}
}
}
}
}
}
miscellanous.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "miscellaneous.h"
char * getTime()
{
time_t curtime;
struct tm *loctime;
static char *ret;
curtime=time(NULL);
loctime=localtime(&curtime);
ret=asctime(loctime);
ret[24]='\0';
return ret;
}
void log_string(const char *message)
{
FILE *fp;
fp=fopen(LOGPATH, "a+");
fprintf(fp, "[%s] %s\n", getTime(), message);
fclose(fp);
}
void log_error(const char *message)
{
FILE *fp;
fp=fopen(LOGPATH, "a+");
fprintf(fp, "[%s] ERROR: %s!\n", getTime(), message);
fclose(fp);
}
void start_daemon()
{
pid_t pid;
char buf[BUFSIZ];
log_string("boot");
pid=fork();
switch(pid) {
case -1:
log_error("fork fail");
exit(1);
break;
case 0:
log_string("fork success");
break;
default:
exit(0);
break;
}
if(setsid()<0) {
log_error("setsid fail");
exit(1);
} else {
log_string("setsid success");
}
//chiude i file descriptor di stdin, stdout, stderr
close(0);
close(1);
close(2);
sprintf(buf, "jasm started with pid %d and ppid %d", getpid(), getppid());
log_string(buf);
}
Essentially these are main srcs...
GDB Backtrace says nothing as the program run stops immediately
Since the question was tagged gdb, let's see how gdb can help. In my case, I've installed the debuginfo files for libc, so that I can examine the arguments to C library functions, but you don't really need that in this case because we can find the bug by looking at the user's source code.
(gdb) run
Starting program: ./jasm
Program received signal SIGSEGV, Segmentation fault.
_IO_vfprintf_internal (s=0x0, format=0x4019b1 "[%s] %s\n",
ap=ap#entry=0x7fffffffbd38) at vfprintf.c:1295
1295 vfprintf.c: No such file or directory.
(gdb) bt
#0 _IO_vfprintf_internal (s=0x0, format=0x4019b1 "[%s] %s\n",
ap=ap#entry=0x7fffffffbd38) at vfprintf.c:1295
#1 0x00007ffff7a693f7 in __fprintf (stream=<optimized out>,
format=<optimized out>) at fprintf.c:32
#2 0x000000000040149d in log_string (message=0x4019cb "boot")
at miscellaneous.c:46
#3 0x000000000040151f in start_daemon () at miscellaneous.c:64
#4 0x0000000000401400 in main (argc=1, argv=0x7fffffffdf78) at jasm.c:31
The declaration for vfprintf is:
int vfprintf(FILE *restrict stream, const char *restrict format, va_list ap);
Even though we don't have the source code for vfprintf installed, we can see that the first argument passed to it, s, is a NULL stream pointer, and that is likely the cause of the seg fault.
Let's look at something we have source code for: frame 2, log_string.
(gdb) frame 2
#2 0x000000000040149d in log_string (message=0x4019cb "boot")
at miscellaneous.c:46
46 fprintf(fp, "[%s] %s\n", getTime(), message);
(gdb) print fp
$2 = (FILE *) 0x0
There it is.
#define LOGPATH "../../../../data/log/jasm.log"
void log_string(const char *message)
{
FILE *fp;
fp=fopen(LOGPATH, "a+");
fprintf(fp, "[%s] %s\n", getTime(), message);
fclose(fp);
}
void log_error(const char *message)
{
FILE *fp;
fp=fopen(LOGPATH, "a+");
fprintf(fp, "[%s] ERROR: %s!\n", getTime(), message);
fclose(fp);
}
Check the return value from fopen. It may be NULL depending on what directory the program is run from. It may be better to use an absolute pathname, possibly settable in the Makefile for portability.
write(fd, "halt\0", 4);
All of these should have a count of 5, to include the trailing NUL. (And it isn't absolutely necessary to explicitly include \0 in the string literal, because C string literals implicitly have a \0 at the end.)

How to get the path to the current file (pwd) in Linux from C?

I'd like to know if it is somehow possible to run system("pwd") on the current DIR. So for example let's have this folder structure:
example
>test
>>file
>test2
>>file3
>>file4
And with opendir() and readdir() I'll get to file3, and I want to use system("pwd") to get the path ..../example/test2/file3. Is this somehow possible, or will pwd return the path to main.c all the time?
Simply opening and reading directories does not change the current working directory. However, changing directory in your program will.
for reference,
#include <unistd.h>
#include <stdio.h>
int main() {
char cwd[1024];
chdir("/path/to/change/directory/to");
getcwd(cwd, sizeof(cwd));
printf("Current working dir: %s\n", cwd);
}
For POSIX systems I found three solutions:
Get value from an environment variables "PWD"
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
#define IS_POSIX 1
#else
#define IS_POSIX 0
#endif
int main (int argv, char **argc)
{
if (IS_POSIX == 1) {
puts("Path info by use environment variable PWD:");
printf("\tWorkdir: %s\n", getenv("PWD"));
printf("\tFilepath: %s/%s\n", getenv("PWD"), __FILE__);
}
return 0;
}
Result:
Path info by use environment variable PWD:
Workdir: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils
Filepath: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils/main.c
Use getcwd()
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
#define IS_POSIX 1
#include <unistd.h>
#else
#define IS_POSIX 0
#endif
int main (int argv, char **argc)
{
if (IS_POSIX == 1) {
char cwd[1024];
getcwd(cwd, sizeof(cwd));
puts("Path info by use getcwd():");
printf("\tWorkdir: %s\n", cwd);
printf("\tFilepath: %s/%s\n", cwd, __FILE__);
}
return 0;
}
Result
Path info by use getcwd():
Workdir: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils
Filepath: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils/main.c
Execute system command "pwd" and read output
#ifdef __unix__
#define IS_POSIX 1
#define _BSD_SOURCE
#else
#define IS_POSIX 0
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argv, char **argc)
{
if (IS_POSIX == 1) {
char buffer[500];
FILE *output;
// read output of a command
output = popen("/bin/pwd", "r");
char *pwd = fgets(buffer, sizeof(buffer), output);
// strip '\n' on ending of a line
pwd = strtok(pwd, "\n");
puts("Path info by execute shell command 'pwd':");
printf("\tWorkdir: %s\n", pwd);
printf("\tFilepath: %s/%s\n", pwd, __FILE__);
}
return 0;
}
Result:
Path info by execute shell command 'pwd':
Workdir: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils
Filepath: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils/main.c
You can use chdir(2) to change dir from C, then system("pwd"); will give you what ever directory you chdir'ed to.
The C-equvivalent of the pwd-command is getcwd(3).
When you use system(...) call with Windows and Linux it just executes one command. It is possible to do the same using file with commands (you can create it with C code), but my oppinion is, that you should use nftw() to get dirrectories and after that use opendir()/readdir().
How to not hardcode the path length with pathconf
I believe this is the correct way to do it:
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
long n;
char *buf;
n = pathconf(".", _PC_PATH_MAX);
assert(n != -1);
buf = malloc(n * sizeof(*buf));
assert(buf);
if (getcwd(buf, n) == NULL) {
perror("getcwd");
exit(EXIT_FAILURE);
} else {
printf("%s\n", buf);
}
free(buf);
return EXIT_SUCCESS;
}
GitHub upstream.
Compile and run:
gcc -Wall -Wextra -std=c11 -pedantic-errors -o getcwd.out getcwd.c
./getcwd.out
POSIX describes _PC_PATH_MAX it as:
The value returned for the variable {PATH_MAX} indicates the longest relative pathname that could be given if the specified directory is the process' current working directory. A process may not always be able to generate a name that long and use it if a subdirectory in the pathname crosses into a more restrictive file system.
Tested on Ubuntu 18.10, n == 4096 in this implementation.

Resources