Git Product home page Git Product logo

mac_alias's Introduction

mac_alias

Python Versions PyPI Version Maturity MIT License Build Status

What is this?

mac_alias lets you generate or read binary Alias and Bookmark records from Python code.

While it is written in pure Python, some OS X specific code is required to generate a proper Alias or Bookmark record for a given file, so this module currently is not portable to other platforms.

Credit is due to Wim Lewis <[email protected]>, whose work on reverse engineering the alias format was extremely useful in writing this code. Alastair Houghton <[email protected]> was responsible for reverse engineering the bookmark format.

See the documentation for more information.

mac_alias's People

Contributors

akrieger avatar al45tair avatar brutusthebee avatar dependabot[bot] avatar droark avatar elliottkember avatar freakboy3742 avatar jakepetroules avatar jessepeterson avatar joachimmetz avatar larkost avatar olipo186 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

mac_alias's Issues

Bug? One too many values passed to format string

When reading an alias, the code currently has this:

            b.write(struct.pack(b'>hQ4shIIQI14s',
                                self.target.kind,
                                int(voldate * 65536),
                                self.volume.fs_type,
                                self.volume.disk_type,
                                self.target.folder_cnid,
                                self.target.cnid,
                                int(crdate * 65536),
                                self.volume.attribute_flags,
                                self.volume.fs_id,
                                b'\0'*14))

Putting the types and what's going in side by side, it looks like there is one more value being put in than the format string should be permitting:

format chars value
h self.target.kind
Q int(voldate * 65536)
4s self.volume.fs_type
h self.volume.disk_type
I self.target.folder_cnid
I self.target.cnid
Q int(crdate * 65536)
I self.volume.attribute_flags
14s self.volume.fs_id
b'\0'*14

I would hazard a guess that fs_id shouldn't be here.

`Alias.for_file` creates an `Alias` object which cannot be written due to containing out of range CNIDs

Currently, if I use Alias.for_file to produce an alias pointing to a file, it produces an Alias object, but then calling to_bytes() on that object fails.

On closer inspection, the generated cnid_path contains a couple of IDs which are out of range for an unsigned 32-bit int.

% python3       
Python 3.9.6 (default, Sep 26 2022, 11:37:49) 
[Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from mac_alias import Alias
>>> a = Alias.for_file("/Applications")
>>> a.target.cnid_path
[2, 1152921500311879701, 1152921500312727487]
>>> a.to_bytes()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "~/Library/Python/3.9/lib/python/site-packages/mac_alias/alias.py", line 754, in to_bytes
    self._to_fd(b)
  File "~/Library/Python/3.9/lib/python/site-packages/mac_alias/alias.py", line 636, in _to_fd
    cnid_path = struct.pack(
struct.error: 'I' format requires 0 <= number <= 4294967295
>>> 

I don't know what the CNID path is supposed to contain, but I'm guessing that 2 is the ID for /Applications (it's about as small as I'd expect /Applications to have) and the other two are presumably whatever macOS considers to be the parent.

If I look at carbon_path, that contains "Data:..:\x00..:\x00..:\x00Applications".

This is with current main branch, running on macOS Monterey.

We use this as part of building DMGs in our build as well, and those still seem to be working, so it could be that our build machines are still running an earlier version of macOS which is not affected. (Or, it could be we're getting an earlier version of mac_alias which doesn't have the issue, but that seems less likely.)

Broken imports from mac_alias in 2.2.1

$ pip3 install mac-alias==2.2.1
Collecting mac-alias==2.2.1
  Using cached mac_alias-2.2.1-py3-none-any.whl (21 kB)
Installing collected packages: mac-alias
Successfully installed mac-alias-2.2.1
$ python3 -c 'from mac_alias import *;'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AttributeError: module 'mac_alias' has no attribute 'kBookmarkVolumePathkBookmarkVolumeURL'

bookmark generator not really working

Great job on the library and documentation. I needed to generate bookmarks programmatically and without linking to C. Unfortunately while this library is probably working super well to parse aliases and bookmarks, the generator didn't seem to work.
I took upon myself to reimplement the library in Go, I didn't focus on the parser but the generator and got it barely working tonight: https://github.com/mattetti/cocoa

I'm not a pythonista so I couldn't quite tell what's off or send a PR but I figured you might be interested. Anyways, thanks again for the great research work, that saved me hours and hours of work.

Unicode issue when reading some non-ascii file names from HFS+ volumes

When working with files that are located on volumes what has some specific unicode characters in their names, mac_alias fails and throws an error.

This is caused by the fact that HFS+ stores files and folders in a form similar to Unicode Normalization Form D (NFD) (see https://en.wikipedia.org/wiki/HFS_Plus#Design) instead of NFC, which is more common. mac_alias must take this into account before comparing strings coming from the file systems with other strings.

I found this bug while building mac apps with electron-builder that internally uses the lib dmg-builder which in turn uses a hard copy of mac_alias. As soon as I tried to name my application anything with "Å" in it - the build process would fail due to this bug.

Steps to reproduce

  1. Create two disk images (.dmg files) with Disk Utility, one named A and one named Å (A with a ring on top): image
  2. You should now have created two .dmg files:image
  3. Mount them in OSX: image
  4. Open a terminal and run
touch /Volumes/A/hello
touch /Volumes/Å/hello
  1. Create test.py and paste the following:
# -*- coding: utf-8 -*-
from mac_alias import Alias
import os

def test(path):
    try:
        alias = Alias.for_file(path)
        alias.to_bytes()
        print "SUCCESS:", path
    except:
        print "FAIL:", path
        raise

test("/Volumes/A/hello")
test("/Volumes/Å/hello")
  1. Run the script
python path/to/test.py

Expected result

oliver@oliver-mbp Å % python ./test.py
SUCCESS: /Volumes/A/hello
SUCCESS: /Volumes/Å/hello

Actual result

oliver@oliver-mbp Å % python ./test.py
SUCCESS: /Volumes/A/hello
FAIL: /Volumes/Å/hello
Traceback (most recent call last):
  File "../macalias_unicode_bug.py", line 15, in <module>
    test("/Volumes/Å/hello")
  File "../macalias_unicode_bug.py", line 8, in test
    alias.to_bytes()
  File "/usr/local/lib/python2.7/site-packages/mac_alias/alias.py", line 594, in to_bytes
    self._to_fd(b)
  File "/usr/local/lib/python2.7/site-packages/mac_alias/alias.py", line 481, in _to_fd
    *self.target.cnid_path)
struct.error: 'I' format requires 0 <= number <= 4294967295

Resolution

In alias.py on line 13, add:

from unicodedata import normalize

In alias.py on line 347, replace

vol_path = st.f_mntonname

with

vol_path = st.f_mntonname

# File and folder names in HFS+ are normalized to a form similar to NFD.
# Must be normalized (NFD->NFC) before use to avoid unicode string comparison issues.
vol_path = normalize("NFC", vol_path.decode('utf-8')).encode('utf-8')

Is there perhaps a third format?

Hi! I just tried this out, and to test it I created a bookmark (Finder>Make Alias) for a screenshot.

I've tried loading it with this code:

for file in sys.argv[1:]:
    with open(file) as f:
        b = f.read()
        print 'read bytes:', len(b)
        a = Alias.from_bytes(b)
        print a

...and I get either ValueError('Incorrect alias length') for Alias, or ValueError('Not a bookmark file (header size too short)') for Bookmark.

The hexdump of the start of the alias file, is as follows:

$ xdump  Screen*alias
00000000    626f6f6b 00000000 6d61726b 00000000    book....mark....
00000010    38000000 38000000 30030000 00000410    8...8...0.......
00000020    00000000 00600000 068270f0 81f2bf41    .....`....p....A
00000030    00000000 01000000 14020000 04000000    ................
00000040    03030000 00040000 05000000 01010000    ................
00000050    55736572 73000000 05000000 01010000    Users...........
00000060    616c6563 6d000000 07000000 01010000    alecm...........
00000070    4465736b 746f7000 26000000 01010000    Desktop.&.......
00000080    53637265 656e2053 686f7420 32303137    Screen Shot 2017

I don't see reference to book0\0\0\0\mark\0\0\0\0 in the Bookmark format document, so I am wondering if this is a new, third format?

ARM64 doesn't need `$INODE64` suffix?

Hello!

I'm using this package as part of electron-builder and I've hit a snag using it on an M1 mac.

This is the traceback I get:

  ⨯ Exit code: 1. Command failed: /usr/bin/python /Users/elliott/appname/.yalc/dmg-builder/vendor/dmgbuild/core.py
Traceback (most recent call last):
  File "/Users/elliott/appname/.yalc/dmg-builder/vendor/dmgbuild/core.py", line 25, in <module>
    from mac_alias import *
  File "/Users/elliott/appname/.yalc/dmg-builder/vendor/mac_alias/__init__.py", line 1, in <module>
    from .alias import *
  File "/Users/elliott/appname/.yalc/dmg-builder/vendor/mac_alias/alias.py", line 16, in <module>
    from . import osx
  File "/Users/elliott/appname/.yalc/dmg-builder/vendor/mac_alias/osx.py", line 424, in <module>
    _statfs = libc['statfs$INODE64']
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ctypes/__init__.py", line 384, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: dlsym(0x100ed1690, statfs$INODE64): symbol not found

However, running the command with the arch -x64_64 prefix made the problem disappear.

I've found this page when looking for the $INODE64 suffix. I tested this out with Rosetta (arch -x86_64 prefix), and sure enough libc['statfs$INODE64'] is available under Rosetta, but not under native Python.

I'm wondering whether the approach of

try:
    getmntinfo = libc["getmntinfo$INODE64"]
except (KeyError, AttributeError):
    # HOPE THIS DOESN'T RETURN A DIFFERENT STATFS STRUCT.
    getmntinfo = libc["getmntinfo"]

would be a good addition?

Also linking this issue where I brought this up in electron-builder.

So...... How exactly do I create an alias?

All I want to do is one very simple thing: I want to create an alias for a file. I would like to pass the POSIX path of the source file and the POSIX path of the destination file as variables into an argument. Here's an example of what I want to do written in plain english.

Make alias of (POSIX path of source file) and place the alias in (POSIX path of destination file)

Now, how do I convert this into python code? I read through the documentation, but, unfortunately, it didn't help me :(

I would really appreciate the help, as I'm extremely new to coding.

EDIT: I did some more digging and, based on what I've read, mac_alias does not create aliases for macOS finder items. Does anyone know of a way of doing this via python?

missing pyproject.toml triggers a deprecation warning from pip

trying to install mac_alias on python3.11 with pip22.3 triggers the following warning:

DEPRECATION: mac_alias is being installed using the legacy 'setup.py install' method, because it does not have a 'pyproject.toml' and the 'wheel' package is not installed. pip 23.1 will enforce this behaviour change. A possible replacement is to enable the '--use-pep517' option. Discussion can be found at pypa/pip#8559

the warning points to the following discussion: pypa/pip#8559

NameError in Alias._from_fd

I think a python syntax issue was introduced in b58f938, causing:

Traceback (most recent call last):
  File "./contrib/macdeploy/macdeployqtplus", line 644, in <module>
    other_alias = Alias().from_bytes(other['.']['icvp']['backgroundImageAlias'])
  File "/.../.pyenv/versions/3.6.12/lib/python3.6/site-packages/mac_alias/alias.py", line 377, in from_bytes
    return cls._from_fd(b)
  File "/Users/michael/.pyenv/versions/3.6.12/lib/python3.6/site-packages/mac_alias/alias.py", line 282, in _from_fd
    kind, volname, voldate, fstype, disktype,
NameError: name 'kind' is not defined

https://github.com/al45tair/mac_alias/blob/master/mac_alias/alias.py#L282

Might be solved by re-adding the \ at the end of the lines or surrounding the tuple with paranthesis.

aliases for mounted files don't seem to work

So I found an interesting issue:

a = Alias.for_file('/Volumes/ExtHD/file.wav')
a = Alias.for_file('/Volumes/ExtHD/file.wav')
  File "/Users/mattetti/miniconda2/lib/python2.7/site-packages/mac_alias/alias.py", line 362, in for_file
    volinfo = osx.getattrlist(vol_path, attrs, 0)
  File "/Users/mattetti/miniconda2/lib/python2.7/site-packages/mac_alias/osx.py", line 818, in getattrlist
    raise OSError(err, os.strerror(err), path)
OSError: [Errno 22] Invalid argument: '/Volumes/ExtHD'

The issue seems to be with the way the path to the external hard drive is formatted. I'll dig deeper to figure out what's going on.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.