Setting file permissions when opening a file with ofstream - c++

Is there a way in C++'s standard libraries (or linux sys/stat.h, sys/types.h, sys/.... libraries) to set the file permissions of a file when creating it with ofstream (or using something else from those libraries)?
When I create a file it just gets created with some default set of file permissions (I assume whatever the current umask is), but I want to explicitly set the permissions to something other than the default (ex. 600), and I can't just set the umask before starting the program (b/c others will run it).
// Example of creating a file by writing to it
ofstream fp(filename.c_str())
/* write something to it */
Is there a way to do this in C++ or if not, a way to set the umask within the C++ program?
For example, in C's standard library you can just do:
open(filename, O_RDWR|O_CREAT, 0666)
but I don't want to resort to using the C function, as it'd be nice to be able to use the functions associated with fstream objects.
(Side Note: there was a question whose title was exactly what I was looking for, but it turned out to be unrelated.)

You cannot. The standard library must be platform agnostic, and permissions like 0600 are meaningless on Windows for example. If you want to use platform-specific features, you'll need to use platform-specific APIs. Of course, you can always call umask() before you open the file, but that's not part of the C++ standard library, it's part of the platform API.
Note: open() isn't part of the C standard library either. It's a platform API. The C standard library function to open a file is fopen().

In C++17, std::filesystem::permissions was introduced. It will enable you to get and set permissions in a platform-agnostic manner.
Get permissions:
using fs = std::filesystem;
bool owner_can_read =
fs::status("my_file.txt").permissions() & fs::perms::owner_read != fs::perms::none;
Set permissions (add all permissions for owner and group, that is, add modes 0x770 on unix):
using fs = std::filesystem;
fs::permissions("my_file.txt",
fs::perms::owner_all | fs::perms::group_all,
fs::perm_options::add);
Example based on an example from cppreference.

Related

How to read a file name containing 'œ' as character in C/C++ on windows

This post is not a duplicate of this one: dirent not working with unicode
Because here I'm using it on a different OS and I also don't want to do the same thing. The other thread is trying to simply count the files, and I want to access the file name which is more complex.
I'm trying to retrieve data information through files names on a windows 10 OS.
For this purpose I use dirent.h(external c library, but still very usefull also in c++).
DIR* directory = opendir(path);
struct dirent* direntStruct;
if (directory != NULL)
{
while (direntStruct = readdir(directory))
{
cout << direntStruct->d_name << endl;
}
}
This code is able to retrieve all files names located in a specific folder (one by one). And it works pretty well!
But when it encounter a file containing the character 'œ' then things are going crazy:
Example:
grosse blessure au cœur.txt
is read in my program as:
GUODU0~6.TXT
I'm not able to find the original data in the string name because as you can see my string variable has nothing to do with the current file name!
I can rename the file and it works, but I don't want to do this, I just need to read the data from that file name and it seems impossible. How can I do this?
On Windows you can use FindFirstFile() or FindFirstFileEx() followed by FindNextFile() to read the contents of a directory with Unicode in the returned file names.
Short File Name
The name you receive is the 8.3 short file name NTFS generates for non-ascii file names, so they can be accessed by programs that don't support unicode.
clinging to dirent
If dirent doesn't support UTF-16, your best bet may be to change your library.
However, depending on the implementation of the library you may have luck with:
adding / changing the manifest of your application to support UTF-8 in char-based Windows API's. This requires a very recent version of Windows 10.
see MSDN:
Use the UTF-8 code page under Windows - Apps - UWP - Design and UI - Usability - Globalization and localization.
setting the C++ Runtime's code page to UTF-8 using setlocale
I do not recommend this, and I don't know if this will work.
life is change
Use std::filesystem to enumerate directory content.
A simple example can be found here (see the "Update 2017").
Windows only
You can use FindFirstFileW and FindNextFileW as platform API's that support UTF16 strings. However, with std::filesystem there's little reason to do so (at least for your use case).
If you're in C, use the OS functions directly, specifically FindFirstFileW and FindNextFileW. Note the W at the end, you want to use the wide versions of these functions to get back the full non-ASCII name.
In C++ you have more options, specifically with Boost. You have classes like recursive_directory_iterator which allow cross-platform file searching, and they provide UTF-8/UTF-16 file names.
Edit: Just to be absolutely clear, the file name you get back from your original code is correct. Due to backwards compatibility in Windows filesystems (FAT32 and NTFS), every file has two names: the "full", Unicode aware name, and the "old" 8.3 name from DOS days.
You can absolutely use the 8.3 name if you want, just don't show it to your users or they'll be (correctly) confused. Or just use the proper, modern API to get the real name.

Setting permissions to a directory using C++

I try to create new directory and set its permissions (using at most c++11 and without boost) so user, group and others can list files inside read them and write new files (linux environment).
#include <sys/stat.h>
#include <sys/types.h>
int main(void) {
const char* path = "/tmp/newDir";
mode_t process_mask = umask(0);
int syscall_status = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
umask(process_mask);
return syscall_status;
}
This code is based on the man (2) page of mkdir (and umask).
However the created directory has rwxr-xr-x permissions (no write for group and others).
I also tried using chmod syscall on the directory but it didn't solve the problem. Other sources in stackoverflow treated files (rather than folders), and trying to apply the file-methods on my directory didn't work as well.
Also, I want to avoid calling system() from stdlib, this is the last option I'll use if I don't find a solution (security considerations).
char* path = "/tmp/newDir";
Besides the syntax error, this is ill-formed since C++11. Prior to that, this would be using a deprecated conversion. String literals are const in C++ -> Use pointer to const.
Other than that, the program is correct assuming a POSIX system. If it fails, then you can check errno to see why. If you don't get all permissions: Check if the parent directory has a default ACL; that would override umask.
A portable way of creating a directory in C++ is std::filesystem::create_directory and a way of setting permissions is std::filesystem::permissions.

C++/boost: checking process permission

I'm writting a C++ program in order to make some static analyse and modifications over a website. I don't change the project files, but the files are copied, analysed and modified in a new folder.
Is there a way of checking, for example, using boost::filesystem, if I (the program/the user executing it) have permissions enough to read (files), execute (for processing the directory hierarchy) and write (files or create new folders) on a current folder and its files (or at least under unix systems)?
No, you can not check file permissions with boost program options library. File permissions can be queried with boost filesystem library:
#include <boost/filesystem.hpp>
#include <stdio.h>
namespace bfs=boost::filesystem;
int main(int argc,char * argv[])
{
if (argc < 2)
return;
bfs::path p(argv[1]);
bfs::file_status s = status(p);
printf("%X\n",s.permissions());
}
The values of permissions flags are as enum perms in
boost/filesystem/v3/operations.hpp
IF you're using linux you can use stat function for get info about the file, including the mode permissions, the owner's ID and the owner's group ID of such file.
Then you can use getuid to compare the permissions of the user running the program with those obtained with stat.
Both links (that are actually man pages) has examples on how to use these functions.
Also you can try to do some operation over the selected file, lets say write, and then handle the exception properly. You don't have to access the file, just try to open it for writting, if you get and exception and errno is equal to EACCESS(permission denied) well, you don't have shuch permissions.

Write a file in a specific path in C++

I have this code that writes successfully a file:
ofstream outfile (path);
outfile.write(buffer,size);
outfile.flush();
outfile.close();
buffer and size are ok in the rest of code.
How is possible put the file in a specific path?
Specify the full path in the constructor of the stream, this can be an absolute path or a relative path. (relative to where the program is run from)
The streams destructor closes the file for you at the end of the function where the object was created(since ofstream is a class).
Explicit closes are a good practice when you want to reuse the same file descriptor for another file. If this is not needed, you can let the destructor do it's job.
#include <fstream>
#include <string>
int main()
{
const char *path="/home/user/file.txt";
std::ofstream file(path); //open in constructor
std::string data("data to write to file");
file << data;
}//file destructor
Note you can use std::string in the file constructor in C++11 and is preferred to a const char* in most cases.
Rationale for posting another answer
I'm posting because none of the other answers cover the problem space.
The answer to your question depends on how you get the path. If you are building the path entirely within your application then see the answer from #James Kanze. However, if you are reading the path or components of the path from the environment in which your program is running (e.g. environment variable, command-line, config files etc..) then the solution is different. In order to understand why, we need to define what a path is.
Quick overview of paths
On the operating systems (that I am aware of), a path is a string which conforms to a mini-language specified by the operating-system and file-system (system for short). Paths can be supplied to IO functions on a given system in order to access some resource. For example here are some paths that you might encounter on Windows:
\file.txt
\\bob\admin$\file.txt
C:..\file.txt
\\?\C:\file.txt
.././file.txt
\\.\PhysicalDisk1\bob.txt
\\;WebDavRedirector\bob.com\xyz
C:\PROGRA~1\bob.txt
.\A:B
Solving the problem via path manipulation
Imagine the following scenario: your program supports a command line argument, --output-path=<path>, which allows users to supply a path into which your program should create output files. A solution for creating files in the specified directory would be:
Parse the user specified path based on the mini-language for the system you are operating in.
Build a new path in the mini-language which specifies the correct location to write the file using the filename and the information you parsed in step 1.
Open the file using the path generated in step 2.
An example of doing this:
On Linux, say the user has specified --output-path=/dir1/dir2
Parse this mini-language:
/dir1/dir2
--> "/" root
--> "dir1" directory under root
--> "/" path seperator
--> "dir2" directory under dir1
Then when we want to output a file in the specified directory we build a new path. For example, if we want to output a file called bob.txt, we can build the following path:
/dir1/dir2/bob.txt
--> "/" root
--> "dir1" directory under root
--> "/" path separator
--> "dir2" directory under dir1
--> "/" path seperator
--> "bob.txt" file in directory dir2
We can then use this new path to create the file.
In general it is impossible to implement this solution fully. Even if you could write code that could successfully decode all path mini-languages in existence and correctly represent the information about each system so that a new path could be built correctly - in the future your program may be built or run on new systems which have new path mini-languages that your program cannot handle. Therefore, we need to use a careful strategy for managing paths.
Path handling strategies
1. Avoid path manipulation entirely
Do not attempt to manipulate paths that are input to your program. You should pass these strings directly to api functions that can handle them correctly. This means that you need to use OS specific api's directly avoiding the C++ file IO abstractions (or you need to be absolutely sure how these abstractions are implemented on each OS). Make sure to design the interface to your program carefully to avoid a situation where you might be forced into manipulating paths. Try to implement the algorithms for your program to similarly avoid the need to manipulate paths. Document the api functions that your program uses on each OS to the user - this is because OS api functions themselves become deprecated over time so in future your program might not be compatible with all possible paths even if you are careful to avoid path manipulation.
2. Document the functions your program uses to manipulate paths
Document to the user exactly how paths will be manipulated. Then make it clear that it is the users responsibility to specify paths that will work correctly with the documented program behavior.
3. Only support a restricted set of paths
Restrict the path mini-languages your program will accept until you are confident that you can correctly manipulate the subset of paths that meet this set of restrictions. Document this to the user. Error if paths are input that do not conform.
4. Ignore the issues
Do some basic path manipulation without worrying too much. Accept that your program will exhibit undefined behavior for some paths that are input. You could document to the user that the program may or may not work when they input paths to it, and that it is the users responsibly to ensure that the program has handled the input paths correctly. However, you could also not document anything. Users will commonly expect that your program will not handle some paths correctly (many don't) and therefore will cope well even without documentation.
Closing thoughts
It is important to decide on an effective strategy for working with paths early on in the life-cycle of your program. If you have to change how paths are handled later it may be difficult to avoid a change in behaviour that might break the your program for existing users.
Try this:
ofstream outfile;
string createFile = "";
string path="/FULL_PATH";
createFile = path.as<string>() + "/" + "SAMPLE_FILENAME" + ".txt";
outfile.open(createFile.c_str());
outfile.close();
//It works like a charm.
That needs to be done when you open the file, see std::ofstream constructor or open() member.
It's not too clear what you're asking; if I understand correctly, you're
given a filename, and you want to create the file in a specific
directory. If that's the case, all that's necessary is to specify the
complet path to the constructor of ofstream. You can use string
concatenation to build up this path, but I'd strongly recommend
boost::filesystem::path. It has all of the functions to do this
portably, and a lot more; otherwise, you'll not be portable (without a
lot of effort), and even simple operations on the filename will require
considerable thought.
I was stuck on this for a while and have since figured it out. The path is based off where your executable is and varies a little. For this example assume you do a ls while in your executable directory and see:
myprogram.out Saves
Where Saves is a folder and myprogram.out is the program you are running.
In your code, if you are converting chars to a c_str() in a manner like this:
string file;
getline(cin, file, '\n');
ifstream thefile;
thefile.open( ("Saves/" + file + ".txt").c_str() );
and the user types in savefile, it would be
"Saves/savefile.txt"
which will work to get to to get to savefile.txt in your Saves folder. Notice there is no pre-slashes and you just start with the folder name.
However if you are using a string literal like
ifstream thefile;
thefile.open("./Saves/savefile.txt");
it would be like this to get to the same folder:
"./Saves/savefile.txt"
Notice you start with a ./ in front of the foldername.
If you are using linux, try execl(), with the command mv.

How to set file permissions (cross platform) in C++?

I am using C++ ofstream to write out a file. I want to set the permissions to be only accessible by the user: 700. In unix; I suppose I can just issue a system("chmod 700 file.txt"); but I need this code to work on Windows as well. I can use some Windows api; but what is the best c++ cross platform way to do this?
Ironically, I have just run into this very same need earlier today.
In my case, the answer came down to what level of permission granularity I need on Windows, versus Linux. In my case, I only care about User, Group, and Other permission on Linux. On Windows, the basic Read/Write All permission leftover from DOS is good enough for me, i.e. I don't need to deal with ACL on Windows.
Generally speaking, Windows has two privilege models: the basic DOS model and the newer access control model. Under the DOS model there is one type of privilege: write privilege. All files can be read, so there is no way to turn off read permission (because it doesn't exist). There is also no concept of execute permission. If a file can be read (answer is yes) and it is binary, then it can be executed; otherwise it can't.
The basic DOS model is sufficient for most Windows environments, i.e. environments where the system is used by a single user in a physical location that can be considered relatively secure. The access control model is more complex by several orders of magnitude.
The access control model uses access control lists (ACL) to grant privileges. Privileges can only be granted by a process with the necessary privileges. This model not only allows the control of User, Group, and Other with Read, Write, and Execute permission, but it also allows control of files over the network and between Windows domains. (You can also get this level of insanity on Unix systems with PAM.)
Note: The Access Control model is only available on NTFS partitions, if you are using FAT partitions you are SOL.
Using ACL is a big pain in the ass. It is not a trivial undertaking and it will require you to learn not just ACL but also all about Security Descriptors, Access Tokens, and a whole lot of other advanced Windows security concepts.
Fortunately for me, for my current needs, I don't need the true security that the access control model provides. I can get by with basically pretending to set permissions on Windows, as long as I really set permissions on Linux.
Windows supports what they call an "ISO C++ conformant" version of chmod(2). This API is called _chmod, and it is similar to chmod(2), but more limited and not type or name compatible (of course). Windows also has a deprecated chmod, so you can't simply add chmod to Windows and use the straight chmod(2) on Linux.
I wrote the following:
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _WIN32
# include <io.h>
typedef int mode_t;
/// #Note If STRICT_UGO_PERMISSIONS is not defined, then setting Read for any
/// of User, Group, or Other will set Read for User and setting Write
/// will set Write for User. Otherwise, Read and Write for Group and
/// Other are ignored.
///
/// #Note For the POSIX modes that do not have a Windows equivalent, the modes
/// defined here use the POSIX values left shifted 16 bits.
static const mode_t S_ISUID = 0x08000000; ///< does nothing
static const mode_t S_ISGID = 0x04000000; ///< does nothing
static const mode_t S_ISVTX = 0x02000000; ///< does nothing
static const mode_t S_IRUSR = mode_t(_S_IREAD); ///< read by user
static const mode_t S_IWUSR = mode_t(_S_IWRITE); ///< write by user
static const mode_t S_IXUSR = 0x00400000; ///< does nothing
# ifndef STRICT_UGO_PERMISSIONS
static const mode_t S_IRGRP = mode_t(_S_IREAD); ///< read by *USER*
static const mode_t S_IWGRP = mode_t(_S_IWRITE); ///< write by *USER*
static const mode_t S_IXGRP = 0x00080000; ///< does nothing
static const mode_t S_IROTH = mode_t(_S_IREAD); ///< read by *USER*
static const mode_t S_IWOTH = mode_t(_S_IWRITE); ///< write by *USER*
static const mode_t S_IXOTH = 0x00010000; ///< does nothing
# else
static const mode_t S_IRGRP = 0x00200000; ///< does nothing
static const mode_t S_IWGRP = 0x00100000; ///< does nothing
static const mode_t S_IXGRP = 0x00080000; ///< does nothing
static const mode_t S_IROTH = 0x00040000; ///< does nothing
static const mode_t S_IWOTH = 0x00020000; ///< does nothing
static const mode_t S_IXOTH = 0x00010000; ///< does nothing
# endif
static const mode_t MS_MODE_MASK = 0x0000ffff; ///< low word
static inline int my_chmod(const char * path, mode_t mode)
{
int result = _chmod(path, (mode & MS_MODE_MASK));
if (result != 0)
{
result = errno;
}
return (result);
}
#else
static inline int my_chmod(const char * path, mode_t mode)
{
int result = chmod(path, mode);
if (result != 0)
{
result = errno;
}
return (result);
}
#endif
It's important to remember that my solution only provides DOS type security. This is also known as no security, but it is the amount of security that most apps give you on Windows.
Also, under my solution, if you don't define STRICT_UGO_PERMISSIONS, when you give a permission to group or other (or remove it for that matter), you are really changing the owner. If you didn't want to do that, but you still didn't need full Windows ACL permissions, just define STRICT_UGO_PERMISSIONS.
There is no cross-platform way to do this. Windows does not support Unix-style file permissions. In order to do what you want, you'll have to look into creating an access control list for that file, which will let you explicitly define access permissions for users and groups.
An alternative might be to create the file in a directory whose security settings have already been set to exclude everyone but the user.
Cross-platform example to set 0700 for a file with C++17 and its std::filesystem.
#include <exception>
//#include <filesystem>
#include <experimental/filesystem> // Use this for most compilers as of yet.
//namespace fs = std::filesystem;
namespace fs = std::experimental::filesystem; // Use this for most compilers as of yet.
int main()
{
fs::path myFile = "path/to/file.ext";
try {
fs::permissions(myFile, fs::perms::owner_all); // Uses fs::perm_options::replace.
}
catch (std::exception& e) {
// Handle exception or use another overload of fs::permissions()
// with std::error_code.
}
}
See std::filesystem::permissions, std::filesystem::perms and std::filesystem::perm_options.
The system() call is a strange beast. I have been bitten by a NOP system() implementation on a Mac many moons ago. It's implementation defined meaning the standard doesn't define what an implementation (platform/compiler) is supposed to do. Unfortunately, this is also about the only standard way of doing something outside the scope of your function (in your case -- changing the permissions).
Update: A proposed hack:
Create a non-empty file with appropriate permissions on your system.
Use Boost Filesystem's copy_file to copy this file out to your desired output.
void copy_file(const path& frompath, const path& topath): The contents and attributes of the file referred to by frompath is copied to the file referred to by topath. This routine expects a destination file to be absent; if the destination file is present, it throws an exception. This, therefore, is not equivalent to the system specified cp command in UNIX. It is also expected that the frompath variable would refer to a proper regular file. Consider this example: frompath refers to a symbolic link /tmp/file1, which in turn refers to a file /tmp/file2; topath is, say, /tmp/file3. In this situation, copy_file will fail. This is yet another difference that this API sports compared to the cp command.
Now, overwrite the output with actual contents.
But, this is only a hack I thought of long after midnight. Take it with a pinch of salt and try this out :)
No idea if it would work, but you could look into using the chmod.exe executable that comes with Cygwin.
There's no standard way to do this in C++, but for this special requirement you should probably just write a custom wrapper, with #ifdef _WIN32. Qt has a permission wrapper in it's QFile class, but this would of course mean depending on Qt ...
I just found a couple of ways to do chmod 700 easily from the Windows command line. I'm going to post another question asking for help coming up with an equivalent win32 security descriptor structure to use (if I can't figure it out in the next few hours).
Windows 2000 & XP (messy- it always seems to prompt):
echo Y|cacls *onlyme.txt* /g %username%:F
Windows 2003+:
icacls *onlyme.txt* /inheritance:r /grant %username%:r
EDIT:
If you had the ability to use the ATL, this article covers it (I don't have Visual Studio available):
http://www.codeproject.com/KB/winsdk/accessctrl2.aspx
Actually, it says the sample code includes non-ATL sample code as well- it should have something that works for you (and me!)
The important thing to remember is to get r/w/x for owner only on win32, you just have to wipe all of the security descriptors from the file and add one for yourself with full control.
You can't do it in a cross-platform manner. In Linux, you should use the function chmod(2) instead of using system(2) to spawn a new shell. On Windows, you'll have to use the various authorization functions to make an ACL (access-control list) with the proper permissions.