clalancette / pycdlib

Python library to read and write ISOs
GNU Lesser General Public License v2.1
147 stars 38 forks source link

AttributeError: orig_extent_loc #100

Closed vtgdias closed 1 year ago

vtgdias commented 2 years ago

I am trying to set interchange_level=3 into iso.new method (files bigger than 4GB), but now i am getting AttributeError: orig_extent_loc from return self.orig_extent_loc (\pycdlib\inode.py", line 114, in extent_location).

My line of code:

self.iso = pycdlib.PyCdlib()
self.vol_ident="XXXXXXX"
self.iso.new(interchange_level=3, xa=True, vol_ident=self.vol_ident, udf='2.60')

Is there another parameter to pass?

clalancette commented 2 years ago

Hi there, There is nothing obviously wrong with the code you have above. But I also could not reproduce your failure with the following code:

import pycdlib

class Foo(object):
    def __init__(self):
        self.iso = pycdlib.PyCdlib()
        self.vol_ident="XXXXXXXX"
        self.iso.new(interchange_level=3, xa=True, vol_ident=self.vol_ident, udf='2.60')

x = Foo()

Can you please let me know which version of pycdlib you are using, and possibly give a more complete example that shows the problem? Thanks

vtgdias commented 2 years ago

Yeap, for sure! Iam using the latest pycdlib (v 1.13.0)

My isoimg class is as follow:

class IsoImg():
    def __init__(self, vol_ident: str, isoburnpath: str) -> bool:
        self.iso = pycdlib.PyCdlib()
        self.vol_ident=vol_ident
        self.iso.new(sys_ident=self.vol_ident, vol_ident=self.vol_ident, vol_set_ident=self.vol_ident, udf='2.60')
        # Altered bellow lines for vol_set_ident to work with udf format:
        # 4042 in pycdlib.py (pycdlib module) -------> self.udf_file_set.new(vol_ident=vol_ident) 
        # 3657 in udf.py (pycdlib module) -----------> self.log_vol_ident = _ostaunicode_zero_pad(vol_ident, 128) 
        self.__targetname__ = ""
        self.__isoburnpath__ = isoburnpath

    @property
    def isoname(self) -> str:
        return self.__targetname__

    @isoname.setter
    def isoname(self, value):
        self.__targetname__ = value

    def addtoiso(self, pathofitem: str) -> bool:
        name = f"/{path.basename(path.normpath(pathofitem))}"
        if path.isfile(pathofitem):
            self.iso.add_file(pathofitem, udf_path=name)
            return True
        elif path.isdir(pathofitem):
            baseroot = f"/{path.basename(path.normpath(pathofitem))}"
            self.iso.add_directory(udf_path=f'{baseroot}')
            for root, dirnames, filenames in walk(pathofitem):
                root_udf=f"{baseroot}{root.split(baseroot)[1]}"
                root_udf=root_udf.replace("\\","/")
                for directory in dirnames:
                    self.iso.add_directory(udf_path=f'{root_udf}/{directory}')
                for file in filenames:
                    self.iso.add_file(path.join(root, file),udf_path=f'{root_udf}/{file}')
            return True
        return False

    def writeiso(self) -> bool:
        if self.isoname:
            self.iso.write(self.isoname)
            self.iso.close()
            return True
        else:
            logging.warning(f"No image path found")
            return False  

From there i instantiate in my main class calling IsoImg methods

...
imagemiso = IsoImg(vol_ident=f"{vol_id}", isoburnpath=constants.ISOBURNPATH)
imagemiso.addtoiso(reportcase)
imagemiso.addtoiso(path.join(parent_casedir,'hashes.txt'))
imagemiso.addtoiso(dest_name)

I resumed my code for brevity (variables as parameters are set earlier). When i run my code with interchange_level=3 i get the error opened issue (orig_extent_loc). When i remove this parameter i get error saying that file limits apply (to 4GB), as i am indeed trying to create a ISO with a 8GB file inside. I was hoping that with interchange_level it would break the file in smaller chunks...

I made some modifications to my local pycdlib files as you can see in comments on my IsoImg class, so i could name udf label ident (it was not working with default vol_ident, always came up with "CD-ROM"), but i do not think those changes are getting in the way of interchange_level somehow.

Is it more clear? let me know, tks in advance!

clalancette commented 2 years ago

Hm, I still can't reproduce the issue with this code:

import logging
from os import path

import pycdlib

class IsoImg():
    def __init__(self, vol_ident: str, isoburnpath: str) -> bool:
        self.iso = pycdlib.PyCdlib()
        self.vol_ident=vol_ident
        self.iso.new(interchange_level=3, sys_ident=self.vol_ident, vol_ident=self.vol_ident, vol_set_ident=self.vol_ident, udf='2.60')
        # Altered bellow lines for vol_set_ident to work with udf format:
        # 4042 in pycdlib.py (pycdlib module) -------> self.udf_file_set.new(vol_ident=vol_ident)
        # 3657 in udf.py (pycdlib module) -----------> self.log_vol_ident = _ostaunicode_zero_pad(vol_ident, 128)
        self.__targetname__ = ""
        self.__isoburnpath__ = isoburnpath

    @property
    def isoname(self) -> str:
        return self.__targetname__

    @isoname.setter
    def isoname(self, value):
        self.__targetname__ = value

    def addtoiso(self, pathofitem: str) -> bool:
        name = f"/{path.basename(path.normpath(pathofitem))}"
        if path.isfile(pathofitem):
            self.iso.add_file(pathofitem, udf_path=name)
            return True
        elif path.isdir(pathofitem):
            baseroot = f"/{path.basename(path.normpath(pathofitem))}"
            self.iso.add_directory(udf_path=f'{baseroot}')
            for root, dirnames, filenames in walk(pathofitem):
                root_udf=f"{baseroot}{root.split(baseroot)[1]}"
                root_udf=root_udf.replace("\\","/")
                for directory in dirnames:
                    self.iso.add_directory(udf_path=f'{root_udf}/{directory}')
                for file in filenames:
                    self.iso.add_file(path.join(root, file),udf_path=f'{root_udf}/{file}')
            return True
        return False

    def writeiso(self) -> bool:
        if self.isoname:
            self.iso.write(self.isoname)
            self.iso.close()
            return True
        else:
            logging.warning(f"No image path found")
            return False

i = IsoImg("XXXXXX", "foo.iso")
i.addtoiso('time-copy.py')
i.isoname = 'foo.iso'
i.writeiso()

Maybe it has to do with some of the files you are adding? I'm not sure. Can you try the above and see if that works for you? Also, maybe you can give me a better idea of the directory structure you are trying to add?

clalancette commented 1 year ago

I haven't heard a response in a couple of months, so I'm going to close this out. If you are still having problems, please feel free to reopen and we can continue debugging.

JoshuaVandaele commented 1 year ago

Hello,

I suffer from the same problem, I am on Windows running Python 3.11.3, and the latest version of pycdlib.

Here is the traceback I receive:

Traceback (most recent call last):
  File "D:\WimPatcher\main.py", line 397, in <module>
    main()
  File "D:\WimPatcher\main.py", line 117, in main
    create_iso_from_folder(root, "out.iso")
  File "D:\WimPatcher\main.py", line 38, in create_iso_from_folder
    iso.write(iso_path)
  File "C:\Users\folfy\AppData\Local\Programs\Python\Python311\Lib\site-packages\pycdlib\pycdlib.py", line 4385, in write
    self._write_fp(fp, blocksize, progress_cb, progress_opaque)
  File "C:\Users\folfy\AppData\Local\Programs\Python\Python311\Lib\site-packages\pycdlib\pycdlib.py", line 3014, in _write_fp
    progress.call(self._output_file_data(outfp, blocksize, ino))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\folfy\AppData\Local\Programs\Python\Python311\Lib\site-packages\pycdlib\pycdlib.py", line 2649, in _output_file_data
    outfp.seek(ino.extent_location() * self.logical_block_size)
               ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\folfy\AppData\Local\Programs\Python\Python311\Lib\site-packages\pycdlib\inode.py", line 114, in extent_location
    return self.orig_extent_loc
           ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Inode' object has no attribute 'orig_extent_loc'. Did you mean: 'new_extent_loc'?

And the code causing this problem:

def add_files_to_iso(iso, source_path, iso_path):
    for root, dirs, files in os.walk(source_path):
        for d in dirs:
            abs_dir = os.path.join(root, d)
            rel_dir = os.path.relpath(abs_dir, source_path)
            iso_dir = os.path.join(iso_path, rel_dir).replace("\\", "/")
            iso.add_directory(udf_path=iso_dir)

        for file in files:
            abs_path = os.path.join(root, file)
            rel_path = os.path.relpath(abs_path, source_path)
            iso_path_file = os.path.join(iso_path, rel_path).replace("\\", "/")

            iso.add_file(abs_path, udf_path=iso_path_file)

def create_iso_from_folder(folder_path, iso_path):
    iso = pycdlib.PyCdlib()
    iso.new(udf="2.60", interchange_level=3)

    add_files_to_iso(iso, folder_path, "/")

    iso.write(iso_path)
    iso.close()
JoshuaVandaele commented 1 year ago

@clalancette Oh and also if it helps: I am trying to repack a Windows 11 installation ISO that's been extracted and modified. It's downloadable over on Microsoft's website: https://www.microsoft.com/software-download/windows11

xiaohao1977 commented 4 days ago

I have encountered the same issue. If the file added to the UDF contains large files (I tried with 6GB and 10GB files), this error occurs. AttributeError: 'Inode' object has no attribute 'orig_extent_loc'. Did you mean: 'new_extent_loc'? @clalancette