How to test code path for read-only file access with PHPunit - phpunit

I have a PHP application with a code path which is only triggered if a specified file is read-only. If the file is readable and writeable another code path is taken. In particular, I am struggling with unlink. The simplified code to be tested looks like this
try {
// do something
unlink($pathToFile);
// do more
} catch (\Throwable $e) {
// recovery and alternative code if unlink failed
}
The catch-branch is taken, if unlink fails because the file is not writable. I have confirmed that manually by creating a read-only file.
My problem is to write a automated test with PHPunit which runs locally on my development machine in the checked out working directory but also as part of Github workflows.
I already tried to create a sample file with PHPunit and then chmod the file to 0666. Unfortunately, this does not trigger the exception, because the file permission seem to be by-passed if the calling user is the owner of the file (which is somehow plausible). I also tried chmod inside PHPunit and make 0 (aka root) the owner of the file, but this fails, because a non-root user is not allowed to call chmod.
Anyway, what is the right approach to "loose" file access rights for the user who is executing the PHPunit process and who is running the PHP application? (Everything I tried so far seemed to be a dead-end as if it was impossible to get rid and throw away the rights on a file which one created oneself.)

To make unlink() return false, unlink a file that does not exist at all.
Example code with single-line error handling (alternative to catch/try in your example which you can do as well):
$result = #unlink('/path/to/non-existing-file');
if (false === $result) {
# ...
}
And if you need to configure much more, an existing tool that allows more fine-grained control is Vfs Stream. It even allows to create dedicated errors.

Related

Error: ENOTEMPTY, directory not empty in Meteorjs

Error: ENOTEMPTY, directory not empty '/path/disk/folder/.meteor/local/build-garbage- qb4wp0/programs/ctl/packages'
I already looked over this website for this problem and known what are maybe the causes of this error and also tried them. I also tried their solutions and I can manage to always reset the project.
The problem is, whenever the project is reset, on the first run of the project,it will run smoothly and no errors will occur but after some moment or changes to my project like error checking, adding packages or changing some stuff... that error will occur.
I have no idea on how to fix this problem and my temporary solution is to always create another meteor project and put all my project files and also install all packages I used.
Badly need help.
I had this error when running Meteor.js on a Vagrant machine. For additional background, I had created a symbolic link for the MongoDB's db folder, since I had faced a locking issue (solution I used for that was described elsewhere).
Following that, my setup was as follows:
/vagrant/.meteor/local/db -> /home/vagrant/my_project_db (symbolic link)
That solved the problem I had with MongoDB's lock, but everytime any source file changed, meteor would crash with the same exception that you faced. Deleting files didn't help, neither did meteor reset.
Fortunately enough it was remedied by changing the folder structure to this:
/vagrant/.meteor/local -> /home/vagrant/my_project_local (symbolic link)
What I did was as simple as moving the Meteor.js's local folder out from the shared folder and only referencing to that with a symbolic link:
cd /vagrant/.meteor
mv local /home/vagrant/my_project_local
ln -s /home/vagrant/my_project_local local
In the end all is good. The error is long gone and feedback cycle is much shorter.
Try deleting the folder it tells you are issues. I think its trying to clean them but there's an unhandled type of situation (it has files in it and its using rm instead of a recursive one)
Remove
/media/Meteor/hash/.meteor/local/build-garbage-**
(Anything with build-garbade in the name). Also you might want to check whether your permissions are right this might have been caused initially with something to do with incorrectly set permissions, maybe you ran as sudo once? If you're on a Mac you could use repair disk permissions.

Unix invoke script when file is moved

I have tons of files dumped into a few different folders. I've tried organizing them several times, unfortunatly, there is no organization structure that consistently makes sense for all of them.
I finally decided to write myself an application that I can add tags to files with, then the organization can be custom to the actual organizational structure.
I want to prevent from getting orphaned data. If I move/rename a file, my tag application should be told about it so it can update the name in the database. I don't want it tagging files that no longer exist, and having to readd tags for files that used to exist.
Is there a way I can write a callback that will hook into the mv command so that if I rename or move my files, they will invoke the script, which will notify my app, which can update its database?
My app is written in Ruby, but I am willing to play with C if necessary.
If you use Linux you can use inotify (manpage) to monitor directories for file events. It seems there is a ruby interface for inotify.
From the Wikipedia:
Some of the events that can be monitored for are:
IN_ACCESS - read of the file
IN_MODIFY - last modification
IN_ATTRIB - attributes of file change
IN_OPEN and IN_CLOSE - open or close of file
IN_MOVED_FROM and IN_MOVED_TO - when the file is moved or renamed
IN_DELETE - a file/directory deleted
IN_CREATE - a file in a watched directory is created
IN_DELETE_SELF - file monitored is deleted
This does not work for Windows (and I think also not for other Unices besides Linux) as inotify does not exist there.
Can you control the path of your users? Place a script or exe and have the path point to it before the standard mv command. Have this script do what you require and then call the standard mv to perform the move.
Alternately an alias in each users profile. Have the alias call your replacement mv command.
Or rename the existing mv command and place a replacement in the same dir, call it mv and have it call your newly renamed mv command after doing what you want.

SQLite error 'attempt to write a readonly database' during insert?

I have a SQLite database that I am using for a website. The problem is that when I try to INSERT INTO it, I get a PDOException
SQLSTATE[HY000]: General error: 8 attempt to write a readonly database
I SSH'd into the server and checked permissions, and the database has the permissions
-rw-rw-r--
I'm not that familiar with *nix permissions, but I'm pretty sure this means
Not a directory
Owner has read/write permissions (that's me, according to ls -l)
Group has read/write permissions
Everyone else only has read permissions
I also looked everywhere I knew to using the sqlite3 program, and found nothing relevant.
Because I didn't know with what permissions PDO is trying to open the database, I did
chmod o+w supplies.db
Now, I get another PDOException:
SQLSTATE[HY000]: General error: 14 unable to open database file
But it ONLY occurs when I try to execute an INSERT query after the database is open.
Any ideas on what is going on?
The problem, as it turns out, is that the PDO SQLite driver requires that if you are going to do a write operation (INSERT,UPDATE,DELETE,DROP, etc), then the folder the database resides in must have write permissions, as well as the actual database file.
I found this information in a comment at the very bottom of the PDO SQLite driver manual page.
This can happen when the owner of the SQLite file itself is not the same as the user running the script. Similar errors can occur if the entire directory path (meaning each directory along the way) can't be written to.
Who owns the SQLite file? You?
Who is the script running as? Apache or Nobody?
For me the issue was SELinux enforcement rather than permissions. The "read only database" error went away once I disabled enforcement, following the suggestion made by Steve V. in a comment on the accepted answer.
echo 0 >/selinux/enforce
Upon running this command, everything worked as intended (CentOS 6.3).
The specific issue I had encountered was during setup of Graphite. I had triple-checked that the apache user owned and could write to both my graphite.db and its parent directory. But until I "fixed" SELinux, all I got was a stack trace to the effect of: DatabaseError: attempt to write a readonly database
This can be caused by SELinux. If you don't want to disable SELinux completely, you need to set the db directory fcontext to httpd_sys_rw_content_t.
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/railsapp/db(/.*)?"
restorecon -v /var/www/railsapp/db
I got this error when I tried to write to a database on an Android system.
Apparently sqlite3 not only needs write permissions to the database file and the containing directory (as #austin-hyde already said in his answer) but also the environment variable TMPDIR has to point to a (possibly writable) directory.
On my Android system I set it to TMPDIR="/data/local/tmp" and now my script runs as expected :)
Edit:
If you can't set environment variables you can use one of the other methods listed here: https://www.sqlite.org/tempfiles.html#temporary_file_storage_locations
like PRAGMA temp_store_directory = 'directory-name';
In summary, I've fixed the problem by putting the database file (* .db) in a subfolder.
The subfolder and the database file within it must be a member of the
www-data group.
In the www-data group, you must have the right to write to the
subfolder and the database file.
####### Additional Notes For Similar Problem #####
I gave write permissions to my sqlite database file to other users and groups but it still didn't work.
File is in my web root directory for my .NET Core WebApi.
It looked like this:
-rw-rw-rw- 1 root root 24576 Jan 28 16:03 librestore.db
Even if I ran the service as root, I kept getting the error :
Error: SQLite Error 8: 'attempt to write a readonly database'.
I also did a chown to www-data on the librestore.db and I still received the same error.
Finally I moved up above my webroot directory and gave others write access to that directory (LibreStore - the root of my WebApi) also and then it worked.
I'm not sure why I had to give the directory write access if the specific file already had write access, but this is the only thing that worked.
But once I made that change www-data user could access the .db file and inserts succeeded.
I got the same error from IIS under windows 7. To fix this error i had to add full control permissions to IUSR account for sqlite database file. You don't need to change permissions if you use sqlite under webmatrix instead of IIS.
I used:
echo exec('whoami');
to find out who is running the script (say username), and then gave the user permissions to the entire application directory, like:
sudo chown -R :username /var/www/html/myapp
(For followers looking for an answer to a similar question)
I'm building a C# .Net Core 6.0 WPF app. I put the Sqlite.db3 on the c:\ drive for convenience while developing. To write to the database I must open Visual Studio 2019 as Administrator.
#Charles in a comment pointed out the solution to this (or at least, a botch solution). This is merely me spelling it out more clearly. Put file_put_contents('./nameofyourdb.sqlite', null); (or .db, whichever you fancy) in a .php file in the root directory of your app (or wherever you want the db to be created), then load that page which renders the php code. Now you have an sqlite db created by whichever user runs your php code, meaning your php code can write to it. Just don't forget to use sudo when interacting with this db in the console.
A good clean solution to this is to allow the file of your main user account to be written to by (in my case) the http user but this worked for me and its simple.
None of these solutions worked for me and I suppose I had a very rare case that can still happen. Had a power shortage so even with 777 permissions on folder and db file, without SELinux, I would get this error.
Turns out there was a jellyfin.pid file (not sure if it's named after the service or user as they have the same name) locking it after the power shortage. Deleted it, restarted the service and everything worked.
I got this in my browser when I changed from using http://localhost to http://145.900.50.20 (where 145.900.50.20 is my local IP address) and then changed back to localhost -- it was necessary to stay with the IP address once I had changed to that once

Change SQLite database mode to read-write

How can I change an SQLite database from read-only to read-write?
When I executed the update statement, I always got:
SQL error: attempt to write a readonly database
The SQLite file is a writeable file on the filesystem.
There can be several reasons for this error message:
Several processes have the database open at the same time (see the FAQ).
There is a plugin to compress and encrypt the database. It doesn't allow to modify the DB.
Lastly, another FAQ says: "Make sure that the directory containing the database file is also writable to the user executing the CGI script." I think this is because the engine needs to create more files in the directory.
The whole filesystem might be read only, for example after a crash.
On Unix systems, another process can replace the whole file.
I solved this by changing owner from "root" to my own user, on all files in Database's folder.
Just do ls -l on said folder, and if any of the files is owned by root, just change it to your user, like:
# For each file do:
sudo chown "$USER":"$USER" /path/to/my-folder/file.txt
# Or "R"ecursive.
sudo chown -R "$USER":"$USER" /path/to/my-folder
(this error message is typically misleading, and is usually a general permissions error)
On Windows
If you're issuing SQL directly against the database, make sure whatever application you're using to run the SQL is running as administrator
If an application is attempting the update, the account that it uses to access the database may need permissions on the folder containing your database file. For example, if IIS is accessing the database, the IUSR and IIS_IUSRS may both need appropriate permissions (you can try this by temporarily giving these accounts full control over the folder, checking if this works, then tying down the permissions as appropriate)
This error usually happens when your database is accessed by one application already, and you're trying to access it with another application.
To share personal experience I encountered with this error that eventually fix both. Might not necessarily be related to your issue but it appears this error is so generic that it can be attributed to gazillion things.
Database instance open in another application. My DB appeared to have been in a "locked" state so it transition to read only mode. I was able to track it down by stopping the a 2nd instance of the application sharing the DB.
Directory tree permission - please be sure to ensure user account has permission not just at the file level but at the entire upper directory level all the way to / level.
Thanks
If using Android.
Make sure you have added the permission to write to your EXTERNAL_STORAGE to your AndroidManifest.xml.
Add this line to your AndroidManifest.xml file above and outside your <application> tag.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
This will allow your application to write to the sdcard. This will help if your EXTERNAL_STORAGE is where you have stored your database on the device.
On win10 after a system crash, try to open db with DB Browser, but read only.
Simply delete the journal file.
In Linux command shell, I did:
chmod 777 <db_folder>
Where contains the database file.
It works. Now I can access my database and make insert queries.
On Windows:
tl;dr: Try opening the file again.
Our system was suffering this problem, and it definitely wasn't a permissions issue, since the program itself would be able to open the database as writable from many threads most of the time, but occasionally (only on Windows, not on OSX), a thread would get these errors even though all the other threads in the program were having no difficulties.
We eventually discovered that the threads that were failing were only those that were trying to open the database immediately after another thread had closed it (within 3 ms). We speculated that the problem was due to the fact that Windows (or the sqlite implementation under windows) doesn't always immediately clean up up file resources upon closing of a file. We got around this by running a test write query against the db upon opening (e.g., creating then dropping a table with a silly name). If the create/drop failed, we waited for 50 ms and tried again, repeating until we succeeded or 5 seconds elapsed.
It worked; apparently there just needed to be enough time for the resources to flush out to disk.
On Ubuntu, change the owner to the Apache group and grant the right permissions (no, it's not 777):
sudo chgrp www-data <path to db.sqlite3>
sudo chmod 664 <path to db.sqlite3>
Update
You can set the permissions for group and user as well.
sudo chown www-data:www-data <path to db.sqlite3>
If <db_name>.sqlite-journal file exists in the same folder with DB file, that means your DB is opened currently and in the middle of some changes (or it had been at the moment when DB folder was copied). If you try to open DB at this moment error attempt to write a readonly database (or similar) could appear.
As a solution, wait till <db_name>.sqlite-journal disappears or remove it (is not recommended on the working system)
I had this problem today, too.
It was caused by ActiveSync on Windows Mobile - the folder I was working in was synced so the AS process grabbed the DB file from time to time causing this error.
On Linux, give read/write permissions to the entire folder containing the database file.
Also, SELinux might be blocking the write. You need to set the correct permissions.
In my SELinux Management GUI (on Fedora 19), I checked the box on the line labelled httpd_unified (Unify HTTPD handling of all content files), and I was good to go.
I'm using SQLite on ESP32 and all answers here are "very strange"....
When I look at the data on the flash of the ESP I notice there is only one file for the whole db (there is also a temp file).
In this db file we have of course the user tables but also the system tables so "sqlite_master" for example which contain the definiton of the tables.
So, it's seems hard to belive this can be a "chmod" problem, because if the file is read only, even creating table would be impossible as SQLite would be unable to write the "sqlite_master" data...
So I think our friend user143482 is trying to acesse a "read only" table. In SQLite source code we can see a function named tabIsReadOnly with this comment:
/* Return true if table pTab is read-only.
**
** A table is read-only if any of the following are true:
**
** 1) It is a virtual table and no implementation of the xUpdate method
** has been provided
**
** 2) It is a system table (i.e. sqlite_master), this call is not
** part of a nested parse and writable_schema pragma has not
** been specified
**
** 3) The table is a shadow table, the database connection is in
** defensive mode, and the current sqlite3_prepare()
** is for a top-level SQL statement.
*/

How can I shell out of an ASP.NET page and execute a git command?

I want my ASP.NET page to shell out and execute git commands. I put the commands in a bat file which works:
REM cd to the git repo folder
cd c:\temp\mygitrepo
"c:\Program Files\Git\Bin\git.exe" show c090dc4b8b1b3512c1b5363c371e21d810d02f54 -- myfile.txt
If I run my .bat file from a cmd prompt, no problem.
If I run it using System.Diagnostics.Process.Start, I get this error:
RUNTIME_PREFIX requested, but prefix computation failed. Using static fallback
The error is coming from this git file: http://github.com/git/git/blob/master/exec_cmd.c
I use exactly the same technique to run svn.exe commands, no problem.
EDIT 1: From the thread here I've learned that msysgit installs some files in a location associated with the current user, me, instead of all users. The IIS web server is running under another user account. I tried copying some of the git files that caught my eye, like .gitconfig, to other users (in Documents and Settings). No luck. So, I have switched my focus to getting IIS to impersonate me when it launches the git command.
Is it actually an error, or is it just notifying you of a fallback? Based on the code linked, it appears that the command should still execute successfully, only using the static PREFIX rather than the RUNTIME_PREFIX.
If the command is indeed failing, you might want to make sure that any required environment variables are available when starting the process from .NET. You might also need to impersonate a different windows identity to run the command with proper permissions (I am assuming that the ASP.NET identity is restricted as much as possible.) The ProcessStartInfo object provides a few ways to configure the process, including the windows identity, the process verb, and environment variables. Make sure you are providing the proper context that your batch file requires before running it.

Resources