HNHacker News
TopNewBestAskShowJobs

siebenmann

168 karma · joined August 20, 2011

http://utcc.utoronto.ca/~cks/space/blog/ among other places
submissionscomments
siebenmann··on The early Unix history of chown() being restricted to root
One reason why Unix quotas are generally not maintained and imposed by path is that it's a lot easier to update quotas as things are created, deleted, modified, and so on if the only thing that matters for who gets charged is some attribute of the inode, which you always have available. This was especially the case in the 1980s (when UCB added disk quotas), because that was before kernels tracked name to inode associations in RAM the way they generally do today. (But even today things like hardlinks raise questions.)

(I'm the author of the linked-to article.)

siebenmann··on I have a GPS bike computer
I've made a PDF of one of my bike club cue sheets from 2014 and put it at https://www.cs.toronto.edu/~cks/tbn/tbn-gatineau-gallop-2014... ; the GPS route that is more or less equivalent to it is https://ridewithgps.com/routes/28370340 (there may be minor differences because the route is more modern than the cue sheet, but it will give you orientation). The cue sheet is written for a group ride (where the group will stay together) and for people familiar with Toronto, so it might be challenging to follow solo unless you were already somewhat familiar with the ride (as the ride leader is expected to be).

The cue sheet is structured the way it is because it's expected it will be folded in half horizontally to fit in a map/cue sheet holder, and perhaps vertically as well (if people have a small holder; you fold vertically first, initially hiding the entire right column since you only need it after lunch, then horizontally). Cue sheet holders typically let you flip them up to see the back, so the exact division of a horizontal fold doesn't have to be perfect. Each numbered section covers a (relatively) distinct section of the ride to make it easier to keep track of where you are in the cue sheet overall.

Cue sheets for different circumstances need different sorts of structure. For example, for some cue sheets it would be quite important to include the distance (cumulative and/or from the previous cue). In others, such as this one, individually numbered cues and distances to them are mostly distractions.

(I'm the author of the linked-to blog entry, and as you can tell I have Opinions on cue sheet design.)

siebenmann··on The X Window System didn't immediately have X terminals
In 1989, the costs appear to have been significantly different, although on a casual search I don't see list prices for eg then-older Sun models like the 3/60. A brand new Sparcstation 1 (also 1989) was far more expensive than an NCD 16 or NCD 19, and a diskless Unix workstation would need more server support and disk space than an X terminal. Today is a different thing, but that's because PC prices have dropped so dramatically.
siebenmann··on The long (after)life of some of our old fileserver hardware
Pricing and charging for storage inside an organization is always ultimately a non-technical decision that has to balance who pays for it versus the consequences of it not being paid for. This is especially the case within organizations like universities, which have unusual funding and funding patterns (for instance, one time capex is usually much easier than guaranteed ongoing opex). We (the people providing the disk storage) know that there are ongoing costs to doing so, but the non-technical decision has been made to cover those costs in other ways than charging professors on a recurring basis.

(I'm the author of the linked-to entry.)

siebenmann··on Union types ('enum types') would be complicated in Go
The problem with allocating bit-packed storage is that then you are into the issue where types don't agree on where any pointers are. Interface values solve this today because they are always mono-typed (an interface value always stores two pointers), so the runtime is never forced to know the current pointer-containing shape of a specific interface value. And the values that interface values 'contain' are also always a fixed type, so they can be allocated and maintained with existing GC mechanisms (including special allocation pools for objects without pointers and etc etc).

I agree with you about the overall motivation for Rust-style enums. I just think it's surprisingly complex to get even the memory efficiency advantages, never mind anything more ambitious.

siebenmann··on Union types ('enum types') would be complicated in Go
Interfaces aren't bit-packed and they force storing all values as a separate allocation that the interface contains a pointer to (escape analysis may allow this separate value to be on the stack, along with the interface itself). I believe that Go used to have an optimization where values that fit in a pointer were stored directly in the interface value, but abandoned it, perhaps partly because of the GC 'is it a pointer or not' issue. In my view, some of what people want union types for is exactly efficient bit-packing that uses little or no additional storage, and they'd be unhappy with a 'union values are just interface values' implementation.

(I'm the author of the linked to article.)

siebenmann··on "Out of Band" network management is not trivial
Because of our (work) situation, I was thinking of an OOB network with its own dedicated connections between sites, instead of the situation where you can plug each site into a 'management' Internet link with protection for your management traffic. However, once your management network gets into each site, the physical management network at that site needs to worry about redundancy if it's the only way to manage critical things there. You don't want to be locked out of a site's router or firewall or the like because a cheap switch on the management network had its power supply fail (and they're likely to be inexpensive because the management network is usually low usage and low port count).
siebenmann··on "Out of Band" network management is not trivial
In a lot of environments, you can at least choose to restrict what networks can be used to manage equipment; sometimes this is forced on you because the equipment only has a single port it will use for management or must be set to be managed over a single VLAN. Even when it's not forced, you may want to restrict management access as a security measure. If you can't reach a piece of equipment with restricted management access over your management-enabled network or networks, for instance because a fiber link in the middle has failed, you can't manage it (well, remotely, you can usually go there physically to reset or reconfigure it).

You can cross-connect your out of band network to an in-band version of it (give it a VLAN tag, carry it across your regular infrastructure as a backup to its dedicated OOB links, have each location connect the VLAN to the dedicated OOB switches), but this gets increasingly complex as your OOB network itself gets complex (and you still need redundant OOB switches). As part of the complexity, this increases the chances an in-band failure affects your OOB network. For instance, if your OOB network is routed (because it's large), and you use your in-band routers as backup routing to the dedicated OOB routers, and you have an issue where the in-band routers start exporting a zillion routes to everyone they talk to (hi Rogers), you could crash your OOB network routers from the route flood. Oops. You can also do things like mis-configure switches and cross over VLANs, so that the VLAN'd version of your OOB network is suddenly being flooded with another VLAN's traffic.

(I am the author of the original article.)

siebenmann··on Go's old $GOPATH story for development and dependencies
The obvious advantage of using domain names and in general URLs as the package names is that the Go project doesn't have to run a registry for package names. Running a registry is both a technical and especially a political challenge, as you must deal with contention over names, people trying to recover access to their names, and so on. By using URLs, the Go project was able to avoid touching all of those issues; instead they're the problem of domain name registries, code hosting providers, and so on.
siebenmann··on Go's old $GOPATH story for development and dependencies
My badly communicated overall point is that I don't think it's right to say that Go started without any thought about dependency management. Instead, the Go developers had a theory for how it would work (with $GOPATH creating workspaces), but in practice their theory didn't work out (for various reasons). For me, this makes the evolution of Go modules much more interesting, because we can get a window into what didn't work.

(I'm the author of the linked-to entry. I wrote the entry because my impression is that a lot of modern Go programmers don't have this view of pre-module Go, and especially Go when you had to set $GOPATH and it was expected that you'd change it, instead of a default $HOME/go that you used almost all the time.)

siebenmann··on GNU Autoconf is not replaceable in any practical sense
Based on an extremely quick skim, this appears aimed only at projects that are using autoconf purely for portability across standard Unix environments. It admits up front that it drops features that people find valuable about configure, like --prefix et al and the entire feature selection cluster of options (now you have to edit Makefiles, which has various issues), and it appears to have nothing for projects that need their own checks for additional features of the environment (OpenZFS being an extreme example). If I was being unkind, I would say it's an autoconf replacement for people who don't need autoconf to start with (and don't care about --prefix et al).

There is an ecological niche for 'you don't need autoconf' (and don't care about aspects it gives you for free), just like there's an ecological niche for 'you don't need Javascript', but I don't think it's a significant one.

(I am the author of the linked-to article.)

siebenmann··on What ZIL metrics are exposed by (Open)ZFS on Linux
NFS v2 writes are all synchronous. NFS v3 added an option to make them asynchronous, along with an additional 'COMMIT' NFS operation that flushes them to storage. In theory how it works is an NFS v3 client sends some number of async writes, holding a copy of their data in its own memory, and then sends a COMMIT to flush them all. If the NFS server replies to the COMMIT with an error, the NFS client has to re-send those async writes and their data (possibly as sync writes this time around); otherwise, it can discard its copy of the written data. NFS v3 clients can still decide to send sync writes if they don't want to keep track of all of this on their end for some reason (including low memory to hold the write data locally). And an NFS v3 server can opt to immediately write out theoretically 'async' writes for similar reasons. All of this is still true in NFS v4, with I think even more elaborations on the theme.

(I'm the author of the linked-to article and I have a long-standing interest in weird NFS behavior, since we operate NFS servers.)

siebenmann··on Web CGI programs aren't particularly slow these days
The publishing system takes some pains to provide an accurate last-modified value for HTTP caching purposes (and an ETag too); internally it tracks the most recent modification time of all components that go into the page as it's (dynamically) assembled from various pieces.

(I am the author of the linked-to article and also the author of the software it's running. Said software also has (on-disk) caching, but that's not why the last-modified is back in December of last year.)

siebenmann··on Unix shells and the current directory
The current directory is a long-standing Unix concept, so you'd have to trace its history back quite far to hear arguments about why it was there. One obvious reason is that relative paths are convenient for all sorts of reasons and they require a point to be relative to, which is basically 'the current directory' in some form.

The kernel knowing the name for the current directory is not specific to current directories; it is part of a general system of caching the name mappings for directory entries ('dnodes' in Linux, a 'name cache' in FreeBSD). Unix kernels added these caches because Unix programs spend a lot of time looking up names, making the operation worth optimizing in general. Once you have a general name cache, you might as well pin the entries for actively used entities like current directories and open files so that they don't get expired out of the cache and you always know (some) name for them.

(One useful complexity of name caches is that you can cache negative entries, ie that a given name is not present in a directory. In the modern Unix shared library environment where shared libraries may be probed for in a whole collection of directories every time a program starts up, I suspect this saves a nice chunk of kernel CPU time.)

siebenmann··on The roots of an obscure Bourne shell error message
In this case, one reason to use printenv is that it is an external command, and so it is clearly and unambiguously seeing (and reporting) whatever the Bourne shell would export into the environment for a real command. In this specific case, it appears that set does not report such single-command variables (whether or not they have a value, eg 'FRED=barney set | grep FRED').

(I am the author of the linked-to entry.)

siebenmann··on File Locks on Linux
Oops, my fallible memory bit me. We weren't specifically running into OOM, but into strict overcommit (which we had turned on on some machines). OOM will only kill big things, so it would be weird for bash (as /bin/sh) or small Python programs to get killed off. But strict overcommit favors already running things (who've already claimed their memory) over newly started cron jobs.

(You could criticize the shell for needing to do dynamic allocation in failure paths and so being exposed to malloc() failing, but this is a hard area and lots of code assumes it can malloc() on demand, some of it in the C library.)

siebenmann··on File Locks on Linux
We used to use this sort of locking in (frequently running) system cron jobs and the like. Then these jobs started getting killed off by Linux OOM on some systems and we ran into the downsides of locks that don't automatically clear if something goes wrong, and switched to flock(1) based locks (fortunately on the local system, so we're not affected by NFS issues there).

(I'm the author of the linked-to entry.)

siebenmann··on Linux Certificate Authority root stores have a too simple view of 'trust'
I think it's more that Linux root stores date from an era when everyone approached CA trust as a binary thing (even browsers), and there has never been enough pressure and coordination to evolve them into a more complex system, unlike browsers. My memory is that browsers added conditional distrust and conditional limits on CAs and various similar things when they became convinced that it would be too bad of a user experience to simply remove CAs but also too dangerous to retain them in fully empowered form. Having conditional distrust also gave browsers more power over CAs, because now browsers had more options for dealing with marginal but (semi-)popular ones.

(I'm the author of the linked-to entry.)

siebenmann··on Linux Certificate Authority root stores have a too simple view of 'trust'
Chrome and Safari require that TLS certificates include cryptographic promises of future log inclusion ('SCTs') from N trusted CT logs. As far as I know, neither of them actually contact the log's API endpoints to make sure that this has gone through, but in practice IMHO it's not much of a security gap for various reasons.
siebenmann··on Modern HDDs have gotten somewhat better
A multi-node Prometheus setup is significantly more complex to design and operate than a Linux software RAID mirror, especially once you throw in Grafana, Alertmanager, and so on. Distributed anything makes it harder. The layer 7 may 'support' this, but it's not a plug and play setup.

(It also requires more hardware.)

(I'm the author of the linked-to entry.)

siebenmann··on How we wound up with Linux's kernel mode setting ('KMS')
You're absolutely right about the KMS requirement for kernel drivers being available and the social aspects of that; when I wrote the article I sort of blithely assumed they were, because they have been for a long time (even in limited form like noveau). But KMS certainly wouldn't have happened without open source drivers that could do enough to require being in the kernel, and that was a product of surprising openness on the part of vendors.

I think it's too strong to say UMS was just a mechanism to keep binary drivers out of the kernel. As far as I know, XFree86 was doing UMS from its beginnings in the early 1990s, which was well before graphics vendors were paying attention to Linux or other free Unixes. There were probably a whole host of reasons that XFree86 used UMS, including that it wanted to be portable across the free Unixes (and not need to coordinate releases with any of them).

(I'm the author of the linked-to article.)

siebenmann··on Filesystems can experience at least three different sorts of errors
Unfortunately ZFS scrubs are not as complete as fsck on a regular filesystem. ZFS scrubs only verify that checksums are intact. They don't verify that filesystem level metadata is correct (although they do verify ZFS structural metadata as part of walking everything, which isn't the same thing). For example, a ZFS scrub will not detect that a filesystem inode has certain sorts of crazy or invalid contents, or damaged ACLs. It doesn't even necessarily verify that the filesystem directory structure is correct and intact.

For more on this, see https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSScrubLim...

(The tl;dr is that a fsck on an ordinary filesystem has to walk the directory tree to find everything. However, ZFS maintains a separate list of active inodes and a scrub can just walk over them and check the checksums of all of their data blocks. It doesn't have to, for example, read a directory's contents to find further files to scrub.)

siebenmann··on Learning that you can use unions in C for grouping things into namespaces
You're right; thanks for noticing and I've updated the first example. My C is a bit rusty these days and I didn't check it with a compiler the way I should have.

(I'm the author of the linked-to article.)

siebenmann··on HTTP/3 needs us to make firewall changes
I'm in an academic department and we've historically blocked outgoing traffic for two separate reasons. First, we consider some protocols actively unsafe to use because they transmit credentials in the clear, and we didn't want our users to accidentally do that. Telnet? Rlogin? Sorry, no. Second, we're in an academic environment where the attacker may be inside our network and poking at someone else's, so we want to shield outside parties from bad traffic we may be generating. In both cases, we're responsive to our users; if someone says 'I need this port', we'll allow the traffic (although we try to be selective about the destination).

(The third answer is that when we set up firewalls in the beginning, we consciously decided to start with a 'default block' policy.)

(I'm the author of the linked-to entry.)

siebenmann··on HTTP/3 needs us to make firewall changes
My feeling (and why I wrote the article) is that it's easy to overlook that you need to do something active to enable your users to use HTTP/3. Previous upgrades to HTTPS of various forms generally required no firewall changes, unless you had a broken firewall that insisted on seeing specific things in TLS handshakes and dropped the connection otherwise. For instance, you can add HTTP/2 to your own servers or talk to HTTP/2 servers outside with no firewall change. But any shift to (or toward) HTTP/3 now requires firewall changes unless you're already passing UDP 443. You can't just deploy it on your own servers or assume that your users will be able to transparently take advantage of it; now you may need to do something active to enable it. It's easy to overlook that, especially since we're not likely to see active failures when HTTP/3 doesn't work, just a quietly degraded experience.

(I'm the author of the linked-to entry, and I should have done a better job of explaining this in the original entry.)

siebenmann··on Nil in Go is typed in theory and sort of untyped in practice
Attempting to call a method on a nil interface value produces a runtime panic. Otherwise, it is the type that implements an interface, and it's possible that the type works even if the specific value of the type is nil. For instance, you could have a 'myIntPointer' type that is a pointer to an int, and implement a String() method for myIntPointer, and at that point a nil myIntPointer still has a String() method and implements the fmt.Stringer interface (although it may get a runtime panic if you actually call the method).
siebenmann··on Nil in Go is typed in theory and sort of untyped in practice
The Go specification is careful to not call 'nil' a constant, and in fact at one point specifically says that it isn't ("Conversions", in the section on converting constants into typed constants, which actually uses '(*int)(nil)' as an example of something that is not a typed constant). Also, although it wasn't clear in the entry, the original article that it was a reaction to talked specifically about 'nil variables' (ie, variables with the value of nil).

(Even the concept of 'the value of nil' is tricky in Go; I believe the specification only talks about things being comparable to nil or allowing nil to be assigned to them. The specification really goes to a lot of work to not treat nil as a value, exactly. I suspect that the Go spec authors really did not want a rerun of the C idea that the NULL pointer is '0' and has an all-zero value and so on.)

(I'm the author of the linked-to entry.)

siebenmann··on FreeBSD kernel-mode WireGuard moves forward out-of-tree
That particular file seems to have been taken from Jason A. Donenfeld's original, which was dual-licensed GPLv2 or MIT and so legal to import (under MIT) into FreeBSD. I don't know which upstream version of the file it comes from, but it's definitely very close to the version in https://github.com/WireGuard/wireguard-monolithic-historical .
siebenmann··on FreeBSD kernel-mode WireGuard moves forward out-of-tree
It's possible to browse the before-changes-started version of the FreeBSD code, through either CVS or the FreeBSD Git mirror. To save people the effort of finding the right git revision and the path, the kernel module starts here: https://github.com/freebsd/freebsd-src/tree/95331c228a39b44c...

On a casual inspection, there are at least kernel printfs in crypto code in __chacha20poly1305_decrypt (in module/crypto/zinc/chacha20poly1305.c) that were not in the original version of this from Linux.

siebenmann··on In CPython, types implemented in C are part of the type tree
The 'class' versus 'type' distinction here is actually arbitrary. The same C level code is responsible for the message that you get from 'type(whatever)' in both Python 2 and Python 3, but in Python 2 it drew a distinction between heap-allocated things (which were reported as 'class') and things that were not heap allocated (which were reported as 'type'). Non heap allocated things had to be created in C; heap allocated things were usually implemented in Python and were usually made with 'class X(base): ...'.

(This change was introduced in Python 3.0a5, bug #2565. Looking at the bug, this is a followup of making the type() of new style classes be reported as 'class ...', but preserving old behavior of type() for built-ins, done in 2001.)

Page 1 of 2Next →