How to get mounted drive's volume name in linux using C? - c

I'm currently working on program, which must display information about mounted flash drive. I want to display full space, free space, file system type and volume name. But problem is that, i can't find any API through which i can get volume name(volume label). Is there any api to do this?
p.s. full space, free space and file system type i'm getting via statfs function

Assuming that you work on a recent desktop-like distribution (Fedora, Ubuntu, etc.), you have HAL daemon running and a D-Bus session.
Within org.freedesktop.UDisks namespace you can find the object that represents this drive (say org/freedekstop/UDisks/devices/sdb/. It implements org.freedesktop.UDisks.interface. This interface has all the properties that you can dream of, including UUID (IdUuid), FAT label (IdLabel), all the details about filesystem, SMART status (if the drive supports that) etc. etc.
How to use D-Bus API in C is a topic for another question. I assume that's been already discussed in detail -- just search [dbus] and [c] tags.

Flash drives are generally FAT32, which means the "name" that you're looking for is probably the FAT drive label. The most common linux command to retrieve that information is mlabel from the mtools package.
The command looks like this:
[root#localhost]$ mlabel -i /dev/sde1 -s ::
Volume label is USB-DISK
This program works by reading the raw FAT header of the filesystem and retrieving the label from that data. You can look at the source code for the applciation to see how you can replicate the parsing of FAT data in your own application... or you can simply execute run the mlabel binary and read the result into your program. The latter sounds simpler to me.

To call the methods:
kernResult = self->FindEjectableCDMedia(&mediaIterator);
if (KERN_SUCCESS != kernResult) {
printf("FindEjectableCDMedia returned 0x%08x\n", kernResult);
}
kernResult = self->GetPath(mediaIterator, bsdPath, sizeof(bsdPath));
if (KERN_SUCCESS != kernResult) {
printf("GetPath returned 0x%08x\n", kernResult);
}
and the methods:
// Returns an iterator across all DVD media (class IODVDMedia). Caller is responsible for releasing
// the iterator when iteration is complete.
kern_return_t ScanPstEs::FindEjectableCDMedia(io_iterator_t *mediaIterator)
{
kern_return_t kernResult;
CFMutableDictionaryRef classesToMatch;
// CD media are instances of class kIODVDMediaTypeROM
classesToMatch = IOServiceMatching(kIODVDMediaClass);
if (classesToMatch == NULL) {
printf("IOServiceMatching returned a NULL dictionary.\n");
} else {
CFDictionarySetValue(classesToMatch, CFSTR(kIODVDMediaClass), kCFBooleanTrue);
}
kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, mediaIterator);
return kernResult;
}
// Given an iterator across a set of CD media, return the BSD path to the
// next one. If no CD media was found the path name is set to an empty string.
kern_return_t GetPath(io_iterator_t mediaIterator, char *Path, CFIndex maxPathSize)
{
io_object_t nextMedia;
kern_return_t kernResult = KERN_FAILURE;
DADiskRef disk = NULL;
DASessionRef session = NULL;
CFDictionaryRef props = NULL;
char * bsdPath = '\0';
*Path = '\0';
nextMedia = IOIteratorNext(mediaIterator);
if (nextMedia) {
CFTypeRef bsdPathAsCFString;
bsdPathAsCFString = IORegistryEntryCreateCFProperty(nextMedia,CFSTR(kIOBSDNameKey),kCFAllocatorDefault,0);
if (bsdPathAsCFString) {
//strlcpy(bsdPath, _PATH_DEV, maxPathSize);
// Add "r" before the BSD node name from the I/O Registry to specify the raw disk
// node. The raw disk nodes receive I/O requests directly and do not go through
// the buffer cache.
//strlcat(bsdPath, "r", maxPathSize);
size_t devPathLength = strlen(bsdPath);
if (CFStringGetCString( (CFStringRef)bsdPathAsCFString , bsdPath + devPathLength,maxPathSize - devPathLength, kCFStringEncodingUTF8)) {
qDebug("BSD path: %s\n", bsdPath);
kernResult = KERN_SUCCESS;
}
session = DASessionCreate(kCFAllocatorDefault);
if(session == NULL) {
qDebug("Can't connect to DiskArb\n");
return -1;
}
disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, bsdPath);
if(disk == NULL) {
CFRelease(session);
qDebug( "Can't create DADisk for %s\n", bsdPath);
return -1;
}
props = DADiskCopyDescription(disk);
if(props == NULL) {
CFRelease(session);
CFRelease(disk);
qDebug("Can't get properties for %s\n",bsdPath);
return -1;
}
CFStringRef daName = (CFStringRef )CFDictionaryGetValue(props, kDADiskDescriptionVolumeNameKey);
CFStringGetCString(daName,Path,sizeof(Path),kCFStringEncodingUTF8);
if(daName) {
qDebug("%s",Path);
CFRetain(daName);
}
CFRelease(daName);
CFRelease(props);
CFRelease(disk);
CFRelease(session);
CFRelease(bsdPathAsCFString);
}
IOObjectRelease(nextMedia);
}
return kernResult;
}

Related

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;
}
}

Linux : setting locale at runtime and interprocess dependencies

I am stuck in a strange problem.
I have two scripts (C program executables) running on ARM linux machine that are mounting the same USB device (containing chinese character filenames) on two different paths, as soon as the device is inserted.
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);
In the last parameter,
Script A passes "utf8" and Script B passes 0.
So, as soon as I insert the USB device, the scripts race to mount the device.
If Script A mounts first (which passes utf8 parameter), I get proper filenames. This is the mount command output [Notice that even second mount has utf8 as parameter, even if its not passed. Why?]
/dev/sdb1 on /home/root/script1 type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-r
o)
/dev/sdb1 on /home/root/script2 type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed
,utf8,errors=remount-ro)
But if script B mounts first(which passes 0 as last parameter to mount), I get broken filenames ?????.mp3 from readdir(). This is the mount command output.
/dev/sdb1 on /home/root/script2 type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
/dev/sdb1 on /home/root/script1 type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed
,errors=remount-ro)
EDIT
This is the basic mount code of both the scripts developed for testing(only difference in last mount argument). Both scripts are executed immediately on reboot using a service.
//mount the device
ret = mount("/dev/sda1", "/home/root/script1/", "vfat", 1, "utf8");
if (ret == 0) {
fprintf(stdout,"mount() succeeded.\n");
sleep(2000);
} else {
ret = mount("/dev/sdb1", "/home/root/script1/", "vfat", 1, "utf8");
if(ret == 0)
{
fprintf(stdout,"mount() succeeded\n");
sleep(2000);
}
else
{
fprintf(stdout,"/dev/sdb1 mount() failed: %d, %s\n", errno, strerror(errno));
ret = mount("/dev/sdc1", "/home/root/script1/", "vfat", 1, "utf8");
if(ret == 0)
{
fprintf(stdout,"mount() succeeded\n");
sleep(2000);
}
else
fprintf(stdout,"mount() failed: %d, %s\n", errno, strerror(errno));
}
}
Generally speaking, you should never mount same filesystem twice -- if OS drivers will decide to write twice to the same block, you'll get filesystem corruption. Use bind-mounts in such cases.
Linux, however, is smart enough to help you with that -- it will reuse older filesystem mount super_block (with all mountpoint flags) for a to a location.
I couldn't find it in documentation, but it is traceable through kernel source in sget() which is called by mount_bdev():
hlist_for_each_entry(old, &type->fs_supers, s_instances) {
if (!test(old, data))
continue;
if (!grab_super(old))
goto retry;
if (s) {
up_write(&s->s_umount);
destroy_super(s);
s = NULL;
}
return old;
}
In this snippet it'll seek for previous instance of super_block corresponding to a block device, and if it already exists -- simply returns it.
Some practical proof using SystemTap:
# stap -e 'probe kernel.function("sget").return {
sb = $return;
active = #cast(sb, "super_block")->s_active->counter;
fsi = #cast(sb, "super_block")->s_fs_info;
uid = fsi == 0 ? -1
: #cast(fsi, "msdos_sb_info", "vfat")->options->fs_uid;
printf("%p active=%d uid=%d\n", sb, active, uid);
}'
Setting uid in second mount doesn't alter option, but increases number of active mounts (obvious):
# mount /dev/sdd1 /tmp/mnt1
0xffff8803ce87e800 active=1 uid=-1
# mount -o uid=1000 /dev/sdd1 /tmp/mnt2
0xffff8803ce87e800 active=2 uid=0
Mounting in reverse order also inherits mount options:
# mount -o uid=1000 /dev/sdd1 /tmp/mnt2
0xffff8803cc609c00 active=1 uid=-1
# mount /dev/sdd1 /tmp/mnt1
0xffff8803cc609c00 active=2 uid=1000
If you wish to know who was responsible for such behavior, ask Linus, similiar code exists since 0.11:
struct super_block * get_super(int dev)
{
struct super_block * s;
if (!dev)
return NULL;
s = 0+super_block;
while (s < NR_SUPER+super_block)
if (s->s_dev == dev) {
wait_on_super(s);
if (s->s_dev == dev)
return s;
s = 0+super_block;
} else
s++;
return NULL;
}
(but when this code was in charge, sys_mount() explicitly checked that no other mountpoints exist for that superblock).
You can possibly try to ask a question at LKML.

File IO in the apache portable runtime library

While working through Zed Shaw's learn C the Hard Way, I encountered the function apr_dir_make_recursive() which according to the documentation here has the type signature
apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
Which makes the directory, identical to the Unix command mkdir -p.
Why would the IO function need a memory pool in order to operate?
My first thought was that it was perhaps an optional argument to populate the newly made directory, however the code below uses an initialized but presumptively empty memory pool. Does this mean that the IO function itself needs a memory pool, that we are passing in for it to use? But that doesn't seem likely either; couldn't the function simply create a local memory pool for it to use which is then destroyed upon return or error?
So, what use is the memory pool? The documentation linked is unhelpful on this point.
Code shortened and shown below, for the curious.
int DB_init()
{
apr_pool_t *p = NULL;
apr_pool_initialize();
apr_pool_create(&p, NULL);
if(access(DB_DIR, W_OK | X_OK) == -1) {
apr_status_t rc = apr_dir_make_recursive(DB_DIR,
APR_UREAD | APR_UWRITE | APR_UEXECUTE |
APR_GREAD | APR_GWRITE | APR_GEXECUTE, p);
}
if(access(DB_FILE, W_OK) == -1) {
FILE *db = DB_open(DB_FILE, "w");
check(db, "Cannot open database: %s", DB_FILE);
DB_close(db);
}
apr_pool_destroy(p);
return 0;
}
If you pull up the source, you'll see: apr_dir_make_recursive() calls path_remove_last_component():
static char *path_remove_last_component (const char *path, apr_pool_t *pool)
{
const char *newpath = path_canonicalize (path, pool);
int i;
for (i = (strlen(newpath) - 1); i >= 0; i--) {
if (path[i] == PATH_SEPARATOR)
break;
}
return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
}
This function is creating copies of the path in apr_pstrndup(), each representing a smaller component of it.
To answer your question - because of how it was implemented. Would it be possible to do the same without allocating memory, yes. I think in this case everything came out cleaner and more readable by copying the necessary path components.
The implementation of the function (found here) shows that the pool is used to allocate strings representing the individual components of the path.
The reason the function does not create its own local pool is because the pool may be reused across multiple calls to the apr_*() functions. It just so happens that DB_init() does not have a need to reuse an apr_pool_t.

How to remove an ACL from a Mac OS symlink - in C

It's a known problem on MacOS that the supplied command line tools (chmod) cannot remove an ACL from a symbolic link: https://discussions.apple.com/thread/1440377?start=0&tstart=0
So I thought I'd break out the C compiler and get it done .. before I realised that ACLs hadn't been invented the last time I used C on Unix.
Can anyone say what system calls (MacOS for preference, if this isn't yet a Unix-wide standard) I can draw on to make this happen?
M.
The function acl_get_link_np(path, ACL_TYPE_EXTENDED)should let you get the ACLs for a path without following symlinks.
Also, I'd think that acl_get_link_np(fd, ACL_TYPE_EXTENDED) should work if you first open the symlink with open(path, O_NOFOLLOW).
To remove all ACLs from a file, you'd use this code:
acl_t acl = acl_get_link_np (path, ACL_TYPE_EXTENDED);
if (acl) {
acl_entry_t entry;
int res = acl_get_entry (acl, ACL_FIRST_ENTRY, &entry);
bool didModify = false;
while (res == 0) {
res = acl_delete_entry (acl, entry);
if (res) break;
didModify = true;
res = acl_get_entry (acl, ACL_NEXT_ENTRY, &entry);
}
if (didModify) {
int res = acl_set_link_np (path, ACL_TYPE_EXTENDED, acl);
if (res) {
NSLog(#"%s – Write ACL: %s", path, strerror(errno));
}
}
acl_free (acl);
}

Using VirtualQueryEx to enumerate modules at remote process doesn't return all modules

I am trying to get a list of DLLs that a given process is using, I am trying to achieve that through VirtualQueryEx. My problem is that it return to me just a partial list of DLLs and not all of them (i can see the list using Process Explorer or using VirtualQuery on the given process).
Here's the code:
char szBuf[MAX_PATH * 100] = { 0 };
PBYTE pb = NULL;
MEMORY_BASIC_INFORMATION mbi;
HANDLE h_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, iPID);
while (VirtualQueryEx(h_process, pb, &mbi, sizeof(mbi)) == sizeof(mbi)) {
int nLen;
char szModName[MAX_PATH];
if (mbi.State == MEM_FREE)
mbi.AllocationBase = mbi.BaseAddress;
if ((mbi.AllocationBase == hInstDll) ||
(mbi.AllocationBase != mbi.BaseAddress) ||
(mbi.AllocationBase == NULL)) {
// Do not add the module name to the list
// if any of the following is true:
// 1. If this region contains this DLL
// 2. If this block is NOT the beginning of a region
// 3. If the address is NULL
nLen = 0;
} else {
nLen = GetModuleFileNameA((HINSTANCE) mbi.AllocationBase,
szModName, _countof(szModName));
}
if (nLen > 0) {
wsprintfA(strchr(szBuf, 0), "\n%p-%s",
mbi.AllocationBase, szModName);
}
pb += mbi.RegionSize;
}
I am getting the result on szBuf.
This function is part of a DLL file so that it is harder for me to debug.
Right now the DLL is compiled as x64 binary and i am using it against x64 processes.
P.S i know about EnumProcessModules and i am not using it with a reason (too long:).
GetModuleFileName() only gives you the name for modules loaded in your process, not the other process. It will give you a few hits by accident, the Windows operating system DLLs will get loaded at the same address and will thus have the same module handle value.
You will need to use GetModuleFileNameEx() so you can pass the process handle.
Do note the fundamental flaw with your code as posted, you are not doing anything to ensure that you can safely use VirtualQueryEx() on another process. Which requires that you suspend all its threads so it cannot allocate memory while you are iterating it, the kind of thing a debugger does. Also required for EnumProcessModules. The failure mode is nasty, it is random and it can easily get your loop stuck, iterating the same addresses over and over again. Which is why the CreateToolHelp32Snapshot() function exists, emphasis on "snapshot".

Resources