Read DVDs with bogus permissions in Ubuntu
What if your DVD recorder sets bogus permissions to your DVDs?
My dad has a DVD recorder (a Panasonic DMR-EX769) which he uses to record TV programs to watch them later. That device includes an internal hard disk so the recorded shows can later be burnt in a DVD. Unfortunately such DVDs cannot be played in Ubuntu. Why? Because the recorder sets bogus permissions to the directories: all directories only have the read permission set. When the user (my dad) inserts the DVD into the reader, Ubuntu correctly mounts the DVD and grants ownership of its files and directories to the user. But since the permissions of the directories inside the DVD are wrong, the user itself cannot enter any directory, effectively preventing the user to be able to use that DVD. The net effect is that the DVD cannot be played.
DVDs use the UDF. This format seems to include support for permissions. According to some comments I read when looking information about this, Windows seems to ignore them (although I have not verified this fact by myself). But the UDF driver in the Linux kernel seems to honour these permissions, even if they happen to be meaningless.
This issue affects Ubuntu (and probably other Linux distributions) according to these two bug reports: #10550 and #635499.
Technically speaking this is not a blocker since it can be worked around by being superuser (using sudo
and friends) or mounting the DVD passing a mode
and dmode
option at mount-time. This may work if you just want to retrieve your files from the DVD but it is ludicrous if your goal was as mundane as trying to play a DVD.
An expert Linux user is now thinking just
. Ok, I tried this first, but, you know, Linux (or whoever is responsible for this bit) happily ignores you. So the only way to change the permissions is first unmounting and then mounting again: remount
the DVD with the appropiate mode
and dmode
options, no big deal-o remount
does not work for that.
For Ubuntu 14.04 LTS, the problem lies in package udisks2
. This package provides a DBus service for disk management, including automounting facilities when a new disk is inserted (i.e. a DVD or a USB pendrive). One of the applications provided by this package, udisksctl
, can mount and unmount drives for you (which is very nice and handy). As a security measure it filters out unsafe mount-options (those that would let the user of this tool elevate its privileges). Sadly, both mode
and dmode
are filtered, so it is not possible to use that tool. So we have to resort with old-school mount
tool (which is what udisks
is internally calling, probably).
Abovementioned bug report #635499 includes a comment with a (more or less) crude patch in the source code of udisks
(version 1) which tries to dynamically add mode
and dmode
flags when a readonly DVD is about to be mounted. The patch cannot be applied directly to udisks2
but it gives an idea of the defaults used by udisks
and how it filters out options in udisksctl
.
So our goal is make a minimal patch to udisk2
so it always passes mode=0400
and dmode=0500
when it mounts an UDF filesystem. This will force the files to have r--------
permissions (only the owner can read the file) and directories r-x------
(only the owner can read and enter the directory). Note that this patch is obviously not realistic for general consumption since some systems can use UDF filesystems in a read/write fashion (according to Wikipedia for DVD-RW with packet writing). This patch would effectively prevent that. But, this is not the scenario of my dad, who only wants to be able the DVDs with films he recorded from the TV.
Setup your environment
Before anything, let's create some working directory and do everything therein.
$ mkdir Builds
$ cd Builds
Now we need to install a tool that we will need later when building the package.
$ sudo apt-get install fakeroot
Now we have to be sure that the package will be buildable. Since Ubuntu is a Debian derivative it uses apt
for package managing. This nice tool includes an option to install all the build dependencies of another package. This is, all the (development) packages you are going to need to successfully build the package. This may install a ton or just a few packages into your system, depending on its current installation state.
$ sudo apt-get build-dep udisks2
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
autoconf automake autopoint autotools-dev build-essential debhelper
dh-apparmor dh-autoreconf docbook docbook-dsssl docbook-to-man docbook-xml
docbook-xsl dpkg-dev g++ g++-4.8 gir1.2-polkit-1.0 gnome-common
gobject-introspection gtk-doc-tools intltool jade libacl1-dev
libatasmart-dev libattr1-dev libencode-locale-perl libffi-dev
libfile-listing-perl libgirepository1.0-dev libglib2.0-dev libgudev-1.0-dev
libhtml-parser-perl libhtml-tagset-perl libhtml-tree-perl
libhttp-cookies-perl libhttp-date-perl libhttp-message-perl
libhttp-negotiate-perl libio-html-perl liblwp-mediatypes-perl
liblwp-protocol-https-perl libnet-http-perl libpcre3-dev libpcrecpp0
libpolkit-agent-1-dev libpolkit-gobject-1-dev libsigsegv2 libsp1c2
libstdc++-4.8-dev libtool libwww-perl libwww-robotrules-perl
libxml-parser-perl m4 po-debconf python-mako python-markupsafe sgml-data sp
xsltproc zlib1g-dev
0 upgraded, 61 newly installed, 0 to remove and 6 not upgraded.
Need to get 20.6 MB of archives.
After this operation, 97.7 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
...
This may take a few minutes. The next step is getting the source. Again we will use apt
for this.
$ apt-get source udisks2
Reading package lists... Done
Building dependency tree
Reading state information... Done
NOTICE: 'udisks2' packaging is maintained in the 'Git' version control system at:
git://git.debian.org/git/pkg-utopia/udisks2.git
Need to get 924 kB of source archives.
Get:1 http://es.archive.ubuntu.com/ubuntu/ trusty/main udisks2 2.1.3-1 (dsc) [2496 B]
Get:2 http://es.archive.ubuntu.com/ubuntu/ trusty/main udisks2 2.1.3-1 (tar) [910 kB]
Get:3 http://es.archive.ubuntu.com/ubuntu/ trusty/main udisks2 2.1.3-1 (diff) [11.7 kB]
Fetched 924 kB in 1s (512 kB/s)
gpgv: Signature made Mon Mar 10 10:44:35 2014 CET using RSA key ID AFE11347
gpgv: Can't check signature: public key not found
dpkg-source: warning: failed to verify signature on ./udisks2_2.1.3-1.dsc
dpkg-source: info: extracting udisks2 in udisks2-2.1.3
dpkg-source: info: unpacking udisks2_2.1.3.orig.tar.bz2
dpkg-source: info: unpacking udisks2_2.1.3-1.debian.tar.xz
dpkg-source: info: applying mount_in_media.patch
dpkg-source: info: applying unsupported_acls.patch
apt-get
source downloads the code, unpacks it and applies any patches of the distribution (in this case there are two patches).
$ ls
udisks2-2.1.3 udisks2_2.1.3-1.debian.tar.xz udisks2_2.1.3-1.dsc udisks2_2.1.3.orig.tar.bz2
Build the current package
The next step is to make sure we can build the existing version of the package. This may take a few minutes.
$ cd udisks2-2.1.3
$ dpkg-buildpackage -us -uc
... gibberish ...
dpkg-deb: building package `udisks2' in `../udisks2_2.1.3-1_amd64.deb'.
dpkg-deb: building package `udisks2-doc' in `../udisks2-doc_2.1.3-1_all.deb'.
dpkg-deb: building package `libudisks2-0' in `../libudisks2-0_2.1.3-1_amd64.deb'.
dpkg-deb: building package `libudisks2-dev' in `../libudisks2-dev_2.1.3-1_amd64.deb'.
dpkg-deb: building package `gir1.2-udisks-2.0' in `../gir1.2-udisks-2.0_2.1.3-1_amd64.deb'.
dpkg-genchanges >../udisks2_2.1.3-1_amd64.changes
dpkg-genchanges: including full source code in upload
dpkg-source --after-build udisks2-2.1.3
dpkg-buildpackage: full upload (original source is included)
Now the packages should have been built in the upper directory.
$ ls ../*.deb
../gir1.2-udisks-2.0_2.1.3-1_amd64.deb ../udisks2-doc_2.1.3-1_all.deb
../libudisks2-0_2.1.3-1_amd64.deb ../udisks2_2.1.3-1_amd64.deb
../libudisks2-dev_2.1.3-1_amd64.deb
These are the packages files that Ubuntu uses to install software. But now they are not interesting to us since they still do not do what we want. Let's remove the packages first, to make sure that later we install the new ones.
$ rm -vf ../*.deb
removed '../gir1.2-udisks-2.0_2.1.3-1_amd64.deb'
removed '../libudisks2-0_2.1.3-1_amd64.deb'
removed '../libudisks2-dev_2.1.3-1_amd64.deb'
removed '../udisks2-doc_2.1.3-1_all.deb'
removed '../udisks2_2.1.3-1_amd64.deb'
Use the source, Luke
Time to hack! We will modify the file src/udiskslinuxfilesystem.c
using our favorite editor. Around line 300 we will see the following.
298
299
300
301
/* ---------------------- udf -------------------- */
static const gchar *udf_defaults[] = { "uid=", "gid=", "iocharset=utf8", "umask=0077", NULL };
static const gchar *udf_allow[] = { "iocharset=", "umask=", NULL };
The first array is the default parameters that udisks
uses when mounting a filesystem. The second array are the parameters allowed by udisksctl
in its mount
subcommand. Let's change these two arrays to
298
299
300
301
/* ---------------------- udf -------------------- */
static const gchar *udf_defaults[] = { "uid=", "gid=", "iocharset=utf8", "umask=0077", "mode=0400", "dmode=0500", NULL };
static const gchar *udf_allow[] = { "iocharset=", "umask=", "mode=", "dmode=", NULL };
This will set mode=0400
and dmode=0500
by default. We will be able to override them in udisksctl
tool also. Save the file and now let's rebuild the package.
Rebuild package
$ dpkg-buildpackage -us -uc
... gibberish ...
dpkg-source: info: local changes detected, the modified files are:
udisks2-2.1.3/src/udiskslinuxfilesystem.c
dpkg-source: info: you can integrate the local changes with dpkg-source --commit
dpkg-source: error: aborting due to unexpected upstream changes, see /tmp/udisks2_2.1.3-1.diff.YgBM9N
dpkg-buildpackage: error: dpkg-source -b udisks2-2.1.3 gave error exit status 2
dpkg
has detected that we changed a file and tells us to make a patch for it. It even tells us what we have to do.
$ dpkg-source --commit
... gibberish ...
dpkg-source: info: local changes detected, the modified files are:
udisks2-2.1.3/src/udiskslinuxfilesystem.c
Enter the desired patch name:
It asks us the name of the patch, I used force-udf-permissions
(press $EDITOR
. Just save it.
dpkg-source: info: local changes have been recorded in a new patch: udisks2-2.1.3/debian/patches/force-udf-permissions
Now we can build the package. Again this will take a few minutes.
$ dpkg-buildpackage -us -uc
... gibberish ...
dpkg-deb: building package `udisks2' in `../udisks2_2.1.3-1_amd64.deb'.
dpkg-deb: building package `udisks2-doc' in `../udisks2-doc_2.1.3-1_all.deb'.
dpkg-deb: building package `libudisks2-0' in `../libudisks2-0_2.1.3-1_amd64.deb'.
dpkg-deb: building package `libudisks2-dev' in `../libudisks2-dev_2.1.3-1_amd64.deb'.
dpkg-deb: building package `gir1.2-udisks-2.0' in `../gir1.2-udisks-2.0_2.1.3-1_amd64.deb'.
dpkg-genchanges >../udisks2_2.1.3-1_amd64.changes
dpkg-genchanges: including full source code in upload
dpkg-source --after-build udisks2-2.1.3
dpkg-buildpackage: full upload (original source is included)
Now there should be several deb
files again, in the upper directory.
$ ls ../*.deb
../gir1.2-udisks-2.0_2.1.3-1_amd64.deb ../udisks2-doc_2.1.3-1_all.deb
../libudisks2-0_2.1.3-1_amd64.deb ../udisks2_2.1.3-1_amd64.deb
../libudisks2-dev_2.1.3-1_amd64.deb
Cool!
Installation of the new packages
Let's install the only two that are actually needed (the other ones are development files and development documentation).
$ sudo dpkg -i ../libudisks2-0_2.1.3-1_amd64.deb ../udisks2_2.1.3-1_amd64.deb
(Reading database ... 236576 files and directories currently installed.)
Preparing to unpack .../libudisks2-0_2.1.3-1_amd64.deb ...
Unpacking libudisks2-0:amd64 (2.1.3-1) over (2.1.3-1) ...
Preparing to unpack ../udisks2_2.1.3-1_amd64.deb ...
Unpacking udisks2 (2.1.3-1) over (2.1.3-1) ...
Setting up libudisks2-0:amd64 (2.1.3-1) ...
Setting up udisks2 (2.1.3-1) ...
Processing triggers for man-db (2.6.7.1-1ubuntu1) ...
Processing triggers for libc-bin (2.19-0ubuntu6.4) ...
Now restart Ubuntu (maybe this is not needed but I'm now too lazy to figure out what would be the minimum required to reload the udisks2
thing). Once logged on, insert a DVD, wait it to be mounted. Now open a terminal and check that the mode
and dmode
flags were passed:
$ mount
...
/dev/sr0 on /media/rferrer/A DVD HERE type iso9660 (ro,nosuid,nodev,uid=1000,gid=1000,iocharset=utf8,mode=0400,dmode=0500,uhelper=udisks2)
Yeah.
Hold the package
Next time we update Ubuntu, it will overwrite our package, so make sure it is held.
$ sudo apt-mark hold udisks2
Future work
The next step is to figure out how to more or less automate this process when Ubuntu updates a new version of udisks2
.
Have a good day.