Example: Managing hard-links on an ISO
PyCdlib supports an advanced concept called hard-links, which is multiple names for the same piece of data (this is somewhat similar to Unix hard-links). Most users will not need to use this functionality and should stick with the standard add_file and rm_file APIs. However, for those that want to do more advanced things like hiding a file from Joliet while having it remain visible in ISO9660, this functionality can be useful.
On an ISO, a piece of data can be referred to (possibly several times) from four different contexts:
- From the original ISO9660 context, including the Rock Ridge extensions.
- From the Joliet context, since this is a separate context.
- From the El Torito boot record, since this is effectively a separate context.
- From the UDF context, since this is a separate context.
The data can be referred to zero, one, or many times from each of these contexts. The most classic example of hard-links happens when an ISO has the Joliet extensions. In that case, there is implicitly a hard-link from the ISO9660 (and Rock Ridge) context to the file contents, and a hard-link from the Joliet context to the file contents. When a piece of data has zero entries in a context, it is effectively hidden from that context. For example, a file could be visible from ISO9660/Rock Ridge, but hidden from Joliet, or vice-versa. A file could be used for booting, but be hidden from both ISO9660/Rock Ridge and Joliet, etc. Management of these hard-links is done via the PyCdlib APIs add_hard_link and rm_hard_link. Adding or removing a file through the add_file and rm_file APIs implicitly manipulates hard-links behind the scenes. Note that hard-links only make sense for files, since directories have no direct data (only metadata).
An example should help to illustrate the concept. Here’s the complete code for the example:
try:
from cStringIO import StringIO as BytesIO
except ImportError:
from io import BytesIO
import pycdlib
iso = pycdlib.PyCdlib()
iso.new(joliet=3)
foostr = b'foo\n'
iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', joliet_path='/foo')
iso.add_hard_link(iso_old_path='/FOO.;1', iso_new_path='/BAR.;1')
iso.rm_hard_link(joliet_path='/foo')
outiso = BytesIO()
iso.write_fp(outiso)
iso.close()
Let’s take a closer look at the code.
try:
from cStringIO import StringIO as BytesIO
except ImportError:
from io import BytesIO
import pycdlib
As in earlier examples, import the relevant libraries, including pycdlib itself.
iso = pycdlib.PyCdlib()
iso.new(joliet=3)
As in earlier examples, create a PyCdlib object, and then create a new, empty ISO with the Joliet extensions.
foostr = b'foo\n'
iso.add_fp(BytesIO(foostr), len(foostr), '/FOO.;1', joliet_path='/foo')
As in earlier examples, add a new file to the ISO. Here we have provided both the ISO path ‘/FOO.;1’ and the Joliet path ‘/foo’, so the file implicitly has two links; one from the ISO context, and one from the Joliet context.
iso.add_hard_link(iso_old_path='/FOO.;1', iso_new_path='/BAR.;1')
Add a hard-link from the original ‘/FOO.;1’ location in the ISO context to a second location in the ISO context ‘/BAR.;1’. This takes up no additional space on the ISO for the data, only for the metadata.
iso.rm_hard_link(joliet_path='/foo')
Remove the link from the Joliet context for the file. Now this file is effectively hidden from the Joliet context, while still being visible in the ISO context.
outiso = BytesIO()
iso.write_fp(outiso)
As in earlier examples, write the ISO out to the BytesIO object.
iso.close()
Since we are done with the ISO object, close it out.