Blog

A Stable Name for a Restless USB Disk

My backup script failed because the USB disk appeared under a different device node after a reboot. The script had one job and had apparently outsourced it to probe order.

I first added a loop that searched /dev/sd* and selected the first disk of roughly the right size. That is not identification; it is speed dating with block devices. One wrong match would turn a backup problem into a restoration problem.

The better route starts with the event. When the kernel discovers hardware, hotplug information reaches user space. sysfs exposes attributes and the device hierarchy. udev applies naming policy and creates nodes or symbolic links. A rule can therefore match something intrinsic to this disk, such as a serial attribute, rather than its temporary kernel name.

Before writing the rule, I inspected the sysfs path associated with the block device and walked upward to understand where the useful attribute lived. This distinction matters: a rule may receive attributes from the device itself and from parent devices, but blindly matching an attribute seen somewhere in /sys is an excellent way to create a rule that never fires.

The result was a stable symbolic link under /dev while the ordinary sd name remained available. My script uses the stable link and checks the filesystem before mounting it. udev supplies naming policy; it does not absolve the script from verifying what it is about to write.

I tested by unplugging the disk, attaching another one first, and reconnecting in different orders. Watching the hotplug environment and udev’s diagnostics was much faster than repeatedly editing a rule and staring at /dev with wounded optimism.

Cold-plug startup deserved the same test. A useful rule must behave consistently whether the disk is present during boot or attached later.

The division of responsibility is clean. The kernel reports what appeared and where it sits in the device model. User space decides what local name is useful. My machine’s nickname for a disk does not belong in a kernel driver.

Dynamic device management still has an intimidating number of moving parts, but the principle is reliable: match stable attributes, create a stable link, and keep transient enumeration out of scripts. Hardware order is an observation, not a promise.

KDE Moves the History, Not Just the Code

KDE has moved its source from CVS to Subversion, so I updated my working habits and immediately tried to use the new checkout as if only the command name had changed. That worked just long enough to preserve my bad assumptions.

The first practical difference I noticed was that directories and renames are represented deliberately. CVS encouraged me to think mainly in files, with directory changes handled through conventions and patience. Subversion versions the directory tree, making a move an operation the repository can describe rather than a delete and unrelated addition that humans must recognize.

Atomic commits are an even larger improvement. A change touching C++ sources, a header, and build files becomes one repository revision. Someone updating sees the complete change, not an awkward interval in which half of it has arrived. That matters in KDE, where a small API adjustment can cross several files and leave the tree temporarily unbuildable if treated as independent events.

My naive migration plan included copying old CVS administrative knowledge forward. Better to check out cleanly, inspect svn status, and learn the new meanings. Subversion uses a local administrative area to track the working copy, which makes common comparisons possible without contacting the server. It also means I should stop treating those metadata directories as mysterious litter.

$ svn status
M      widget.cpp
A      widgettest.cpp

The output is plain enough to become a pre-commit habit. I review the diff, update, build, test, and then commit one coherent change with a useful message. The tool cannot make the change coherent on my behalf, sadly.

Repository-wide revision numbers also give discussions a compact reference point. They do not replace a descriptive message, but they make it easier to state exactly which tree was built or tested.

No revision control migration fixes project communication. Large commits can still be incomprehensible, and an atomic mistake remains a mistake. But Subversion’s model fits the shape of a large C++ desktop better than pretending directories have no history.

The KDE move is therefore more than administrative churn. It gives contributors a clearer representation of the work they are already doing. I expect some scraped knees while scripts and muscle memory catch up, but preserving relationships between files is worth relearning a few commands.

Watching Linux History Move to Git

The interesting part of Git this week is no longer that it exists. Kernel developers are beginning to use it for actual Linux work, which changes the test from “can these objects be written?” to “can people exchange and trust history under pressure?”

I tried to imitate the old centralized habit by treating one repository as the place where truth lived and every other copy as a temporary client. That misses Git’s shape. Each repository can hold commits and their parent relationships. Fetching obtains objects and updates selected references; merging combines lines of development rather than asking a central server to manufacture history.

The kernel workflow makes the reason concrete. Maintainers can collect changes for their subsystem, preserve those commits, and pass a resulting line of history upward. The top-level maintainer can inspect and merge it. Signed e-mail and established review practices still matter; a fast object database does not decide whether a patch is wise.

I also learned not to confuse content identity with human trust. A hash can reveal that an object changed. It cannot tell me whether the author understood locking or whether I fetched from the person I intended. Technical integrity and project trust reinforce one another, but they are not synonyms.

Performance is already part of the appeal. Operations over the kernel tree need to be quick enough that developers use them freely. Cheap local history and branches encourage smaller experiments because recording work does not require a conversation with a remote machine.

That changes behavior as much as speed. A maintainer can inspect and organize work before publishing it instead of using the shared repository as a scratch pad.

The interface remains under construction, and I would not recommend that every project switch during lunch. Linux has an urgent need, unusually capable maintainers, and a willingness to work close to the machinery. That is a special environment, not a universal migration plan.

But adoption by the kernel gives Git a severe and useful proving ground. If it can preserve a large, branching C codebase while maintainers exchange changes independently, the design will have earned attention. I am keeping my test repository now. Calling it comfortable would be generous; calling it merely a toy would already be unfair.

Git Starts With Objects

The Linux kernel suddenly needs a new revision control system, and Linus has started writing one. That sentence sounds like the opening of either an excellent engineering story or a very efficient disaster. I downloaded Git to understand what was actually being built.

My naive attempt was to look for a familiar source-control interface. This first version doesn’t have one. It makes more sense as a handful of low-level programs over a content-addressed object store, plus an index used to assemble the next tree.

A file’s contents become a blob identified by its SHA-1 name. A tree names blobs and other trees. A commit object can point to a tree, identify a parent, and carry metadata. At this stage those raw objects are the useful thing to inspect; convenient names and comfortable workflows haven’t arrived yet.

The index is the interesting bit between the working directory and a tree object. update-cache records paths and their current object names in the index. write-tree turns that prepared index into a tree. It won’t quietly infer a polished commit operation from whatever happens to be in my directory.

I added a small file with update-cache, wrote the tree, and used cat-file to inspect what had been stored. The blob held content; the tree supplied the filename and mode. Changing one file produced a new blob and tree while unchanged blobs kept the same names. That reuse falls straight out of immutable, content-named objects.

I am not ready to entrust ordinary work to it. The commands are young, rough, and obviously aimed at solving the kernel project’s immediate emergency. Anyone claiming the user experience is finished has perhaps enjoyed too much fsck.

Still, the mechanism is compelling. commit-tree can wrap a prepared tree in a commit object, but the machinery remains exposed. It’s a very C-like start: small primitives, explicit input, and an operator who’s expected to pay attention.

For now I’m experimenting in a disposable directory and looking at .dircache/objects and the index rather than pretending this is a finished tool. Two days in, the raw storage model is already worth watching.

QObject Ownership Is Not Garbage Collection

I found a leak in a small Qt 3.3 dialog and responded by deleting every child widget in the destructor. Then I noticed that the widgets already had the dialog as their parent. My heroic cleanup was redundant; the actual leak was an object with no parent at all.

Qt’s object trees are straightforward once I stop pretending they are magic. A QObject with a parent joins that parent’s child list. Destroying the parent destroys its children. Top-level objects and objects without parents remain my responsibility.

My useful check is now mechanical. At construction, I ask who owns each allocation. If the answer is a parent object, I do not delete it separately. If there is no parent, I give the lifetime an explicit home in C++ code. Layouts arrange widgets; they do not replace ownership reasoning.

QLabel *label = new QLabel(tr("Status"), this);

Here this is the relevant part, not new. The parent makes the intended lifetime visible and ties cleanup to the dialog.

This is not garbage collection. Qt does not discover arbitrary unreachable C++ objects, and a parent cannot rescue a dangling pointer used after destruction. It is a disciplined ownership convention layered over C++.

The conclusion is pleasantly small: establish one owner and make it obvious at construction. My destructor became shorter, which is about the nicest possible result of debugging C++ memory management.