How to get file's owner name in Linux using C++? - c++

How can I get get the owner name and group name of a file on a Linux filesystem using C++? The stat() call only gives me owner ID and group ID but not the actual name.
-rw-r--r--. 1 john devl 3052 Sep 6 18:10 blah.txt
How can I get 'john' and 'devl' programmatically?

Use getpwuid() and getgrgid().
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
struct stat info;
stat(filename, &info); // Error check omitted
struct passwd *pw = getpwuid(info.st_uid);
struct group *gr = getgrgid(info.st_gid);
// If pw != 0, pw->pw_name contains the user name
// If gr != 0, gr->gr_name contains the group name

One way would be to use stat() to get the uid of a file and then getpwuid() to get the username as a string.

Related

Creating folders in C++

I have recently started working in C++ and came across this situation when I have to create a directory while executing my code. The code is working fine when I have to create a single folder but it fails when I have to create another folder withing this newly created folder.
Suppose, I am in C: and want to store my file in C:/A/B/ .The following piece of code using mkdir() works fine if I have to store my file in C:/A/ but fails when I am adding another folder B.
Following is my code snippet:
#include <sys/stat.h>
#include <string>
using namespace std;
int main()
{
string stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
if(status!=0)
{
//.....
}
else
{
//....
}
}
Can someone help me in creating this directory where I can have any number of folders inside the parent directory? (P.S:I have added the header files sys/stat.h,iostream and string)
This is how you do it in C++17:
#include <filesystem>
namespace fs = std::filesystem;
fs::create_directories("./a/b/c")
mkdir() creates only the last component of the specified path. In your example, it will create only B. If any of the parent directories do not exist (ie, if A does not exist), the function fails with ENOENT. You need to split up the path and call mkdir() for every intermediate directory in the path, ignoring EEXIST errors as you go.
status = mkdir("C:/A/", 0777);
if ((status < 0) && (errno != EEXIST)) ...
status = mkdir("C:/A/B/", 0777);
if ((status < 0) && (errno != EEXIST)) ...
If you don't want to handle this manually, use a wrapper that handles it for you, such as Boost's create_directories() function:
bool create_directories(const path& p);
bool create_directories(const path& p, system::error_code& ec);
Effects: Establishes the postcondition by calling create_directory() for any element of p that does not exist.
Postcondition: is_directory(p)
Returns: true if a new directory was created, otherwise false.
Throws: As specified in Error reporting.
Complexity: O(n+1)where n is the number of elements of p that do not exist.
You can call the following:
string stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
If
C:/A/ directory exists. If its not exists, then do the following:
string stringpath = "C:/A/";
int status = mkdir(stringpath.c_str(),0777);
stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
In C++11 you can use the experimental functios:
#include <experimental/filesystem>
...
std::stringstream bufH;
bufH << dirName << fName;
if (!std::experimental::filesystem::exists(bufH.str()))
{
std::experimental::filesystem::create_directories(bufH.str());
}
Try the octal flag 7777 like this to have all the rights necessary to create this folder.
int status = mkdir(stringpath.c_str(), 7777);
Or do a chmod in the A folder like that :
chmod -r 7777 *

Find bus number and device number with device file symlink

I have a device file(SYMLINK) /dev/CDMAModem generated by a udev rule. I want to find the bus number and device number of the actual device. Actually I want to perform USBDEVFS_RESET ioctl on device /dev/bus/usb/BUS_NO/DEVICE_NO in my C++ program.
----udev rule----
SUBSYSTEMS=="usb", ACTION=="add", DRIVERS=="zte_ev", ATTRS{bNumEndpoints}=="03", SYMLINK+="CDMAModem"
SUBSYSTEMS=="usb", ACTION=="remove", DRIVERS=="zte_ev", ATTRS{bNumEndpoints}=="03", SYMLINK-="CDMAModem"
I think libudev will give you that:
#include <libudev.h>
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <unistd.h>
int main(int argc, char **argv)
{
struct udev *udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
struct udev_device *dev;
udev = udev_new();
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "CDMAModem");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
const char *path;
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
fprintf(stderr, "devnum: %s\n",
udev_device_get_sysattr_value(dev, "devnum"));
fprintf(stderr, "busnum: %s\n",
udev_device_get_sysattr_value(dev, 'busnum:));
udev_device_unref(dev);
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
return 0;
}
You can then use this information with ioctl() as in:
[charles#localhost 2-1]$ cd /sys/class/mem/random
[charles#localhost 2-1]$echo $PWD
/sys/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1
You can perform an ioctl on the file represented by the symlink /dev/CDMAModem as you would on the file under the /dev/bus/ structure.
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
int f = open("/dev/CDMAModem", O_RDWR);
ioctl(f, USBDEVFS_RESET);
If you actually want to find where this link is pointing, the file command will tell you.
> file /dev/CDMAModem
/dev/CDMAModem: symbolic link to `bus/usb/BUS/DEV'
I think the stat() library call would be a good place to start... Along with libusb.
If all you want to do is resolve the link, you can use readlink and parse the information later with string functions.
Function: ssize_t readlink (const char *filename, char *buffer, size_t size)
The readlink function gets the value of the symbolic link filename. The file name that the link points to is copied into buffer. This file name string is not null-terminated; readlink normally returns the number of characters copied. The size argument specifies the maximum number of characters to copy, usually the allocation size of buffer.
~$ sudo udevadm info -a -p $(sudo udevadm info -q path -n /dev/CDMAModem)
[sudo] password for gowtham:
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/ttyUSB0/tty/ttyUSB0':
KERNEL=="ttyUSB0"
SUBSYSTEM=="tty"
DRIVER==""
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/ttyUSB0':
KERNELS=="ttyUSB0"
SUBSYSTEMS=="usb-serial"
DRIVERS=="zte_ev"
ATTRS{port_number}=="0"
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0':
KERNELS=="2-1.2:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="zte_ev"
ATTRS{bInterfaceClass}=="ff"
ATTRS{bInterfaceSubClass}=="ff"
ATTRS{bInterfaceProtocol}=="ff"
ATTRS{bNumEndpoints}=="03"
ATTRS{supports_autosuspend}=="1"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bInterfaceNumber}=="00"
ATTRS{interface}=="Data Interface"
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2':
KERNELS=="2-1.2"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{devpath}=="1.2"
ATTRS{idVendor}=="19d2"
ATTRS{speed}=="12"
ATTRS{bNumInterfaces}==" 6"
ATTRS{bConfigurationValue}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{busnum}=="2"
ATTRS{devnum}=="8"
ATTRS{busnum}=="2"
ATTRS{devnum}=="8"
Though this is ugly but works. From C++ program call udevadm and filter for busnum and devnum attributes from the output. I hope some developer who involved in libudev development shall help and may be udev mailing-list will help.

C++ : semget : Returns ENOENT

#include <sys/sem.h>
#include <sys/stat.h>
#include <cerrno>
#include <iostream>
using namespace std;
main () {
string key = "/m/lb1/activity.log";
int sem_key = ftok (key.c_str (), 1);
if (access (key.c_str (), R_OK) == 0)
printf ("keypath is accessible \n");
printf("sem_getid : %d", sem_key);
errno = 0;
int sem_id = semget (sem_key, 0, 0655);
printf( " sem get val :%d :%s\n", sem_id, strerror(errno));
}
$ ./a.out
keypath is accessible
sem_getid : 16850172 sem get val :-1 :No such file or directory
ls -l /m/lb1/activity.log
-rw-r--r-- 1 apple g6 0 Feb 9 19:08 /m/lb1/activity.log
The "access" call returns 0; so the keypath gets printed out. However, the semget is returning an ENOENT (No such file or directory)
sem_getid : 16850172
sem get val :-1 :No such file or directory
What could be wrong here?
semget gets an existing semaphore set unless you specify IPC_CREAT or IPC_PRIVATE in the third argument. From man semget:
ENOENT
No semaphore set exists for key and semflg did not specify IPC_CREAT.
In this case, ENOENT means that the semaphore set didn't exist, rather than meaning the path didn't exist.
The sem_key you pass to semget is not a filename or path, its just an arbitrary 32-bit integer. The namespace of possible semaphore keys is not related to the file name space in any way.
So you need to pick some (integer) key value to use, and you need to actually create the key before using it, by calling semget with IPC_CREAT first.

How to neglect the umask so as to create the file with given permission

I am creating a file using open function and using O_CREAT | O_EXCEL . I have passed the mode as "0666" . But by masking finally the permission allotted to it is -rw-r--r-- and not the
-rw-rw-rw- . Someone told me i can use umask (011) and then reset the original mask again .
But i dont know how to pass this in c++ program. This is the small snippet of What i am doing .
# include <iostream>
# include <stdio.h>
# include <conio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
int main()
{
int fd = open("C:\\Users\\Ritesh\\Music\\music.txt", O_CREAT | O_EXCL, 0666);
getch();
return 0;
}
creates file C:\Users\Ritesh\Music\music.txt with permission -rw-r--r-- . I want it to be -rw-rw-rw-
mode_t old_mask;
old_mask = umask(011);
open( ... );
umask(old_mask);
umask means permissions that you don't want to give to files by default. So if you want to control the permissions completely while creating a file, set umask to 0, which tells the os don't reserve any permissions and let you call the shot. like this:
int main()
{
mode_t oldmask = umask(0);
int fd = open("C:\\Users\\Ritesh\\Music\\music.txt", O_CREAT | O_EXCL, 0666);
close(fd);
umask(oldmask);
getch();
return 0;
}
The only thread-safe way to set file permissions to be what you want is to set them explicitly with chmod() or fchmod() after creating the file (example without error checking):
int fd = open("C:\\Users\\Ritesh\\Music\\music.txt", O_CREAT | O_EXCL, 0666);
fchmod(fd, 0666 );
If you use umask(), you will change the umask value for the entire process. If any other threads are running you risk files getting created with unexpected permissions, which could lead to a security issue or other problems. And any child process created while your changed umask value is in effect will be created with an unexpected umask value.

Cannot access file in Network Attached Storage (NAS) by using C++ access() function?

I have an Isilon NAS in 10.20.30.11 for example, and I mounted it like following:
mount 10.20.30.11:/folder /content
I could use ls command to find the file in folder or /content. Its mod is 777.
bash-3.00# ls -l /content/a/b/1.txt
total 344131
rwxrwxrwx 1 1005 65533 140750 Feb 28 00:58 1.txt
But I cannot access it by access() function.
#include <iostream>
#include <string>
#include <unistd.h>
#include <cerrno>
using namespace std;
#include <stdio.h>
int main( int argc, const char* argv[] )
{
int returnVal = 0;
returnVal = access(argv[1], R_OK);
cout << returnVal << endl;
cout << errno << endl;
return 0;
}
It will return -1 and 2 as a result, which means 'No such file or directory'.
./a.out /content/a/b/1.txt
-1
2
#define ENOENT 2 /* No such file or directory */
It is not a permission problem I think, because the mod is 777, and the result is 'No such file or directory'.
From the Linux man pages.
access() may not work correctly on NFS
file systems with UID mapping enabled,
because UID mapping is done on the
server and hidden from the client,
which checks permissions.
Finally, it is found that it need to use following command to mount the Isilon storage.
mount -o vers=2,proto=tcp
1.2.3.4:/remote /mnt
The version and protocol need specified.
Thanks!