Read only filesystem error

Hey everyone! snapcraft newbie here :wink:

I’m working to package a python application into a snap: https://github.com/snovvcrash/usbrip

So far, everything cool but the application itself has an “update” module which downloads a file from a website an store it locally in the application folder /snap/<snap_name>/x1/lib/python3.6/site-packages/<snap_name>/usb_ids but since the folder / filesystem is read-only this task cannot be performed.

I’ve tried also changing the confinement from devmode to classic but even so …

OSError: [Errno 30] Read-only file system: '/snap/<snap_name>/x1/lib/python3.6/site-packages/<snap_name>/usb_ids/usb.ids'

So I guess my question is how can a snap can write data into its application dir.??

If you can point me in the right direction pls.

Thanks in advance! -Xavier.

The file structure in your $SNAP directory is designed to be static, and is indeed mounted as R/O. Changing confinement doesn’t change that. You should put the file in $SNAP_DATA instead, which is writable.

Check out https://snapcraft.io/docs/environment-variables for more info on the different options you have here.

Finally, given what you’ve said above, I’m guessing you’ll also want to tweak your $PYTHON_PATH to include the new location of the additional module(s) you’re downloading

1 Like

mount / -o “remount,rw”

hilarious, but wrong. The filesystem is readonly. You can’t write to it.

2 Likes

Thank you for your reply!

Sorry my late response but I’ve tried a lot of combinations lately that I’m really confused by now haha! …

According to the setuptools doc. there are a few methods you can use to define a custom installation directory if, for example, the python site-packages directory is not user writable :point_down:

https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations

For example by setting up the PYTHONUSERBASE env. variable and using the command:

~$ python setup.py --user

So in a mix of reading docs and following your advice, I ended up using the $SNAP_DATA env. variable as for this PYTHONUSERBASE env. variable / destination for the package build / installation, but I’m pretty sure I’m doing it wrong or something, by the output of snapcraft


Installing usbrip script to /var/snap/snapcraft/3308/bin

Installed /var/snap/snapcraft/3308/lib/python3.6/site-packages/usbrip-2.1.4.post1-py3.6.egg
Processing dependencies for usbrip==2.1.4.post1
Searching for terminaltables==3.1.0
Best match: terminaltables 3.1.0
Processing terminaltables-3.1.0-py3.6.egg
terminaltables 3.1.0 is already the active version in easy-install.pth

Using /var/snap/snapcraft/3308/lib/python3.6/site-packages/terminaltables-3.1.0-py3.6.egg
Searching for termcolor==1.1.0
Best match: termcolor 1.1.0
Processing termcolor-1.1.0-py3.6.egg
termcolor 1.1.0 is already the active version in easy-install.pth

Using /var/snap/snapcraft/3308/lib/python3.6/site-packages/termcolor-1.1.0-py3.6.egg
Finished processing dependencies for usbrip==2.1.4.post1
Cleaning later steps and re-staging usbrip ('build' step changed)
Priming usbrip 
Failed to generate snap metadata: The specified command 'bin/usbrip' defined in the app {'plugs': ['mount-observe', 'log-observe', 'network-control'], 'command': 'bin/usbrip'} does not exist.
Ensure that 'bin/usbrip' is installed with the correct path.
Run the same command again with --debug to shell into the environment if you wish to introspect this failure.

If you guys are wondering what kind of monstrous thing I did , here’s my snapcraft.yaml file :nauseated_face:


name: usbrip
base: core18
version: "2.1.4-1"
summary: Simple CLI forensics tool for tracking USB device artifacts on GNU/Linux
description: |
  usbrip (derived from "USB Ripper", not "USB R.I.P." astonished) is an open source forensics tool with CLI interface that lets you keep
  track of USB device artifacts (aka USB event history, "Connected" and "Disconnected" events) on Linux machines.
grade: devel
confinement: devmode

parts:
  usbrip:
    plugin: python
    source: .
    build-environment:
      - PYTHONUSERBASE: $SNAP_DATA/
    override-build: |
      /usr/bin/python3 setup.py install --user
    stage-packages:
      - p7zip-full
    organize:
      "usbrip/cron": usbrip/cron/
      "usbrip/usb_ids": usbrip/usb_ids/
    prime:
      - -./requirements.txt
      - -./README.md

apps:
  usbrip:
    command: bin/usbrip
    plugs:
      - mount-observe
      - log-observe
      - network-control

Can you guys show me an example snapcraft.yaml for a python app which uses the $SNAP_DATA for this matter ?

Ps. really sorry for the long post.

I think you may have got a bit tangled up here. I believe your original question was about installing python modules at runtime. I.e. having some mechanism whereby your snap (after it’s been installed and running) installs additional modules that are then available to it. Is that correct, or was it a different question?

On the other hand, if there are python modules that you want to have incorporated into your snap at build time, then the mechanism for this is completely different (and quite straightforward). See e.g. https://snapcraft.io/docs/python-apps or Snap including a pip package. Indeed, for 99% of use cases, all required modules would be installed at build time rather than run time.

If you can clarify the objective maybe we can help further.

I believe I didn’t explain very well the situation early, sorry for that. It was a different question.

I think what I’m trying to do is rather trivial (I hope!), let me explain…

The python app itself has a module called usbids.py (local to the app source code, nothing to install at runtime).

This module handles the download and updates of a database file (a raw plain text file) of USB devices and vendors from: http://www.linux-usb.org/usb.ids, and stores it locally where the app is installed commonly: /snap/<snap_name>/x1/lib/python3.6/site-packages/ which is the deafult location where the libraries are installed by the snapcraft python plugin.

But as I mention earlier in my first post, since this directory is read-only, when it tries to download the database file from the interwebs it complains about it.

OSError: [Errno 30] Read-only file system: '/snap/<snap_name>/x1/lib/python3.6/site-packages/<snap_name>/usb_ids/usb.ids`

So? What I’m trying to do is override (in someway) the build of the app by using override-build part in the snapcraft.yaml file, so the app gets installed in a user-writable directory, like $SNAP_DATA as you suggested.

Btw is worth to mention that the real author of the app is :point_right: Sam Freeside (@snovvcrash), I’m just trying to help him distribute it as a snap :stuck_out_tongue_winking_eye:

if you patch:

to actually prefix the path with the value you get from the SNAP_DATA anvironment variable (or if the app should be run without root permissions from SNAP_USER_DATA), you have the file in a writable location.

to get it there originally you can use an install hook to copy it to SNAP_DATA during install, or a wrapper to install it to SNAP_USER_DATA on first run.

1 Like

I wrote the following earlier but didn’t get around to posting. I feel like it probably complements @ogra’s input (which is essentially the next step to what I’m suggesting) in the below:

Ok, got you. And I see that in that context I sent you on a bit of a wild goose chase with the whole “install Python package under $SNAP_DATA”.

Anyway, it’s quite unconventional - at least from what I’ve seen - for Python packages to expect their installation directory under (site-packages) to be writable to them, and to modify files in there at runtime. Indeed, in many situations (aside from snaps) the modules won’t have write access to their installation directory.

I’d say the right solution in this case is to patch the existing code so that it uses another directory for files it expects to be writeable. This could be its execution directory, or a directory set by an environment variable.

2 Likes