How to tell if a file is on tmpfs given its path on Linux? - linux

This might be a dumb question, but suppose I'm given a file path and I'd like to know if it points to a file on tmpfs (that is, it's an in-memory file). How can I do that using only Linux system calls? (That is, I can't go to the shell.)

Use the statfs syscall and see if the returned f_type field is TMPFS_MAGIC.
Here's a small utility demonstrating this:
#include <sys/vfs.h>
#include <linux/magic.h>
#include <stdio.h>
int main(int argc, char** argv) {
struct statfs info;
statfs(argv[1], &info);
if (info.f_type == TMPFS_MAGIC) {
printf("It's tmpfs\n");
return 0;
} else {
printf("It's not tmpfs\n");
return 1;
}
}
Example:
$ ./isittmpfs /etc/passwd
It's not tmpfs
$ ./isittmpfs /dev/shm/pulse-shm-1358569836
It's tmpfs
(NB: This is just an example of how to determine if a file is on tmpfs through syscalls. This answer does not suggest dropping to a shell even though the example code is invoked from a shell)

Related

Why am I denied permission trying to shm_open?

Consider the following C program:
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/types.h>
#include <string.h>
int main() {
const char* name = "/memmap_ipc_shm";
int shmFd = shm_open(name, O_RDWR | O_CREAT, 0777);
if (shmFd < 0) {
fprintf(stderr,"bad shmfd opening %s: %s\n", name, strerror(errno));
return -1;
}
return 0;
}
When I run it on my GNU/Linux system (Devuan Beowulf, Linux 5.10.0-9, amd64 CPU), I get:
bad shmfd opening /memmap_ipc_shm: Permission denied
Why am I denied permission? I'm pretty sure I followed all the guidelines in the man shm_open page, my requested permissions seem ok - so what's wrong?
This may be happening when you have already created this shared memory object, but perhaps without the appropriate . Try calling shm_unlink(name) to ensure that's the case (or even do so as root if you encounter a permission failure again).
Note that this behavior seems to clash with the man page:
O_EXCL If O_CREAT was also specified, and a shared memory object with the given
name already exists, return an error. The check for the existence of the
object, and its creation if it does not exist, are performed atomically.

Why I couldn't use mount --bind /proc/<pid>/ns/mnt to another file in ubuntu?

Recently, I am learning about linux namespaces.
I hope even if the process ends, I could still attach the namespace that process is in.
Someone told I could use mount --bind /proc/<pid>/ns/pid ./pid to keep this file open. So I tried it.
When I mount uts, user, ipc, pid, net... They are all okay..
root#ubuntu:/home/jiashenh/workspace# touch uts
root#ubuntu:/home/jiashenh/workspace# mount --bind /proc/171/ns/uts ./uts
But when it comes to mnt.... I used it in the same way ...
root#ubuntu:/home/jiashenh/workspace# touch mnt
root#ubuntu:/home/jiashenh/workspace# mount --bind /proc/171/ns/mnt ./mnt
mount: wrong fs type, bad option, bad superblock on /proc/171/ns/mnt,
missing codepage or helper program, or other error
In some cases useful info is found in syslog - try
dmesg | tail or so
It failed.....So could anyone told me how to fix it?
.
Bind mount for /proc/<pid>/ns/mnt really doesn't work if called within the same mount namespace.
However, bind mount for ns/mnt works if:
mount() is called from a process with another mount namespace, and
mount() is called from a process where the ns/mnt being mounted is visible.
Both requirements are met if mount() is called from a process forked before unshare() call that created ns/mnt entry being mounted.
(Note that after unshare() call, previously visible ns/mnt entries are no more visible and become broken symlinks).
Here is a test:
#define _GNU_SOURCE
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/mount.h>
int main(int argc, char** argv) {
int fd = open("/tmp/mnt.ns", O_CREAT | O_RDWR);
close(fd);
if (argc > 1) {
if (fork() == 0) {
sleep(1);
} else {
unshare(CLONE_NEWNS);
wait(NULL);
return 0;
}
}
char src[PATH_MAX] = {};
snprintf(src, sizeof(src), "/proc/%u/ns/mnt", (unsigned)getppid());
if (mount(src, "/tmp/mnt.ns", NULL, MS_BIND, NULL) != 0) {
printf("mount failed: %s\n", strerror(errno));
return 1;
}
else {
printf("mount ok\n");
}
return 0;
}
This test fails without fork() and unshare():
$ sudo ./a.out
mount failed: Invalid argument
But works if it calls fork() and unshare() before mount() (note the x arg):
$ sudo ./a.out x
mount ok
See this commit for unshare tool and also this patch.
Unfortunately, this behaviour specific to ns/mnt is not documented in namespaces(7).

daemon process mount failed

I am programming a Linux distribution C program. When daemon process read a MSG from the client, it will fork a child process, then mount a "NFS" dir to local host and do the compute task on this NFS dir.
However this is NFS is IBM ClearCase dynamic view which is called "MVFS" rather than "NFS".
So I have to exec "cleartool setview xxyyzz" to mount this view.
But I tried several methods such as fork() + execvp, system(shell), but never succeed.
Below is the code and shell:
void my_system();
int main(int argc, char** argv)
{
pid_t pid=fork();
if(pid!=0) exit(0);
if(setsid()==-1)
{
printf("setsid failed.");
exit(-1);
}
umask(0);
chdir("/tmp");
int i;
for(i=0;i<3;i++)
close(i);
FILE* logfd=fopen("/tmp/ccdcc.log","a+");
dup2(fileno(logfd),STDOUT_FILENO);
dup2(fileno(logfd),STDERR_FILENO);
fclose(logfd);
my_system(); //method1
system("/tmp/ccdccshell"); //method2
sleep(SEVERALTIME);
}
void my_system()
{
pid_t pid=fork();
if(pid!=0) return;
char *arg1[]={"cleartool","setview","zzzzzz"};
char *arg2[]={"cd","/proj/layer/mak"};
char *arg3[]={"mycmd"};
execvp("cleartool",arg1);
execvp("cd",arg2);
execvp("mycmd",NULL);
}
xxx#yyy> cat /tmp/ccdccshell
#!/bin/sh
#this command will mount a dynamic view and source some profile.
cleartool setview zzzzzz
#this path is under the mounted path
cd /proj/layer/mak
#to test where we are
pwd
#call my prog.
mycmd
I failed "cleartool...." everytime. There's no view been mounted.
"pwd" always return the path "/".
Could you tell me how to finish my program?
Don't use cleartool setview: it spawns a subshell which would not be visible from your sameon.
Simply use the full dynamic view path:
/view/myDynView
# under which you would see:
/view/myDynView/vobs/MyVob
All you need is for the view to be started.

Linux alternative to _NSGetExecutablePath?

Is it possible to side-step _NSGetExecutablePath on Ubuntu Linux in place of a non-Apple specific approach?
I am trying to compile the following code on Ubuntu: https://github.com/Bohdan-Khomtchouk/HeatmapGenerator/blob/master/HeatmapGenerator2_Macintosh_OSX.cxx
As per this prior question that I asked: fatal error: mach-o/dyld.h: No such file or directory, I decided to comment out line 52 and am wondering if there is a general cross-platform (non-Apple specific) way that I can rewrite the code block of line 567 (the _NSGetExecutablePath block) in a manner that is non-Apple specific.
Alen Stojanov's answer to Programmatically retrieving the absolute path of an OS X command-line app and also How do you determine the full path of the currently running executable in go? gave me some ideas on where to start but I want to make certain that I am on the right track here before I go about doing this.
Is there a way to modify _NSGetExecutablePath to be compatible with Ubuntu Linux?
Currently, I am experiencing the following compiler error:
HeatmapGenerator_Macintosh_OSX.cxx:568:13: error: use of undeclared identifier
'_NSGetExecutablePath'
if (_NSGetExecutablePath(path, &size) == 0)
Basic idea how to do it in a way that should be portable across POSIX systems:
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
static char *path;
const char *appPath(void)
{
return path;
}
static void cleanup()
{
free(path);
}
int main(int argc, char **argv)
{
path = realpath(argv[0], 0);
if (!path)
{
perror("realpath");
return 1;
}
atexit(&cleanup);
printf("App path: %s\n", appPath());
return 0;
}
You can define an own module for it, just pass it argv[0] and export the appPath() function from a header.
edit: replaced exported variable by accessor method

Howto Write to the GPIO Pin of the CM108 Chip in Linux?

The CM108 from C-Media has 4 GPIO pin that you can access via a hid interface.
Using the generic write function in Windows I was able to write to the gpio pins.
However I'm trying to do the same thing in Linux without success.
The linux kernel detect the device as a hidraw device.
Note: I was able to read from the device, just not write. (I've run the app as root just to make sure it wasn't a permission issue).
I got this working, here's how.
I needed to create a new linux hid kernel mod. (it wasn't that hard)/*
/*
* Driver for the C-Media 108 chips
*
* Copyright (C) 2009 Steve Beaulac <steve#sagacity.ca>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*/
/*
* This driver is based on the cm109.c driver
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#define DRIVER_VERSION "20090526"
#define DRIVER_AUTHOR "Steve Beaulac"
#define DRIVER_DESC "C-Media 108 chip"
#define CM108_VENDOR_ID 0x0d8c
#define CM108_PRODUCT_ID 0x000c
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define CM108_MINOR_BASE 0
#else
#define CM108_MINOR_BASE 96
#endif
/*
* Linux interface and usb initialisation
*/
static int cm108_hid_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
ret = hid_parse(hdev);
if (ret) {
dev_err(&hdev->dev, "parse failed\n");
goto error;
}
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (ret) {
dev_err(&hdev->dev, "hw start failed\n");
goto error;
}
return 0;
error:
return ret;
}
static struct hid_device_id cm108_device_table[] = {
{ HID_USB_DEVICE (CM108_VENDOR_ID, CM108_PRODUCT_ID) },
/* you can add more devices here with product ID 0x0008 - 0x000f */
{ }
};
MODULE_DEVICE_TABLE (hid, cm108_device_table);
static struct hid_driver hid_cm108_driver = {
.name = "cm108",
.id_table = cm108_device_table,
.probe = cm108_hid_probe,
};
static int hid_cm108_init(void)
{
return hid_register_driver(&hid_cm108_driver);
}
static void hid_cm108_exit(void)
{
hid_unregister_driver(&hid_cm108_driver);
}
module_init(hid_cm108_init);
module_exit(hid_cm108_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
used This makefile
obj-m += cm108.o
and compile the module
make -C /lib/modules/`uname -r`/build/ M=`pwd` EXTRAVERSION="-generic" modules
sudo make -C /lib/modules/`uname -r`/build/ M=`pwd` EXTRAVERSION="-generic" modules_install
depmod -a
I had to modify the modules.order file so that my module would get queried before the generic hid linux module.
This modules make sure that the hidraw uses Interface 2.
Then I can use fopen to read and write to the GPIO pin of the CM108 chip.
BTW: when writing you need to write 5byte the 1st byte is used for the HID_OUTPUT_REPORT
Most hardware in Linux is accessible as a file. If the driver created a hardware node for it on the file-system, you're in luck. You will be able to write to it using regular file routines. Otherwise, you may need to do some assembly magic, which may require you to write a kernel module to do it.
Here is a complete example of how to write to the CM108/CM119 GPIO pins on Linux.
https://github.com/wb2osz/direwolf/blob/dev/cm108.c
You don't need to run as root or write your own device driver.
I have the opposite problem. I'm trying to figure out how to do the same thing on Windows.

Resources