←back to thread

279 points the_why_of_y | 1 comments | | HN request time: 0.206s | source
Show context
useerup ◴[] No.11154936[source]
This is a case of a leaky abstraction (https://en.wikipedia.org/wiki/Leaky_abstraction). The "everything is a file" philosophy is the real problem here.

Specifically, the way a mental model of a hierarchy is broken by mounting a higher-order ressource (UEFI variables) as a subordinate of a file system that is itself a subordinate of the OS.

UEFI vars are just hardware resources. Mapping them as a file system object is just unnatural and, yes, stupid.

Trying to use a permission model ("only root can do it") overlooks the real problem: The user do not expect higher order objects to be mapped as subordinates of the file system.

When you delete from the file system, you expect objects to be deleted from the disk - not UEFI variables to be altered or deleted! And because the user does not expect such behavior, there's a good chance she/he will override warnings and go ahead with the operation expecting only file system objects to be affected.

This is "everything is a file" taken a bridge too far.

replies(2): >>11155483 #>>11157292 #
mjhoy ◴[] No.11155483[source]
Where does Linux promise that files are bits on a disk? As a user I certainly don't expect that. Perhaps you have a problem with the name "file" but the abstraction itself still seems useful. (And yet I do find it quite odd when I have to do something like `echo "TPAD" > /proc/acpi/wakeup` to disable wake-on-trackpad.) That said I don't disagree with you that UEFI variables should not be delete-able, but there are many files on Linux that you can't delete.
replies(2): >>11155964 #>>11156189 #
useerup ◴[] No.11156189[source]
It is a file system, consisting of file system objects, like files and directories.

Already exposing processes as files is an abstraction. It somewhat works because you can imagine the file representation being maintained by the process. But it is an abstraction, because a process is not a file.

But what is more important: A file system is a hierarchy. At the root is the most fundamental object. Each level has subordinate objects. That the model you expect.

Having UEFI variables mounted as a file is a surprising loop back to something even more fundamental that the OS itself: The firmware of the physical computer. It a breach of the mental model.

It breaks one of the most fundamental principles that should be followed in man-machine interaction: The principle of least surprise.

I have a machine. I have installed an operating system on it. The OS manages several disks. On the disks the OS manages file systems. I expect the files of that system to be managed by the OS.

I do not expect that regular file system actions have effect outside the hierarchy of the directory on which I perform the actions. Specifically I do not expect files on that system to manage the physical computer.

replies(1): >>11156882 #
deathanatos ◴[] No.11156882[source]
> Already exposing processes as files is an abstraction.

No. The file system, is the abstraction. Adding /proc onto it is a use of that abstraction.

There's two basic extreme positions, and you're adopting one, your parent is adopting something closer to the other.

a. The filesystem only exposes filesystems actually on disk, mapped to some hierarchy. As you say, "On the disks the OS manages file systems."

b. The filesystem is (roughly) a hierarchical container of named binary blobs (called "files") with some defined associated metadata, such as permissions.

While you can adopt (a), and that's fine, some of us (myself included) see a lot of value in (b). The biggest problem with only exposing "real" file-storing FSes in the file hierarchy is that it leaves you with a ton of questions about how to expose all the other things. Taking the stance that we're only going to expose "real" files in the file hierarchy leaves us with several classes of objects that aren't files-on-disk, and you need to name them s.t. the user can interact with them. It is certainly possible to expose each different type of thing in a completely separate namespace. You'll probably also need to be able to associate permissions with those objects¹, as so now you've got a named, ACL'd list or hierarchy of objects, and it's starting to look a lot like a filesystem. You now also need another set of tooling to work with each of these classes of objects. You need another set of syscalls for each of these objects.

The great thing about having a unified file hierarchy in the (b) abstraction is that tooling works on all of these different classes of objects different. It's really just the "CRUD" idiom, and normally it allows things to interoperate quite smoothly. I can write a bash script that draws a progress bar of my battery, and it requires no knowledge other than where in the file hierarchy the battery is.

This is, of course, a case where the power is somewhat biting us. That doesn't make the abstraction wrong, nor does it mean the abstraction isn't leaky. (In fact, in this case, the abstraction works really well, I'd say. Any other implementation of UEFI variables is going to have a "delete" call, AFAICT. What bit us here is that all the objects are in one bucket together, and thus rm -rf / removes more than just files.)

> It breaks one of the most fundamental principles that should be followed in man-machine interaction: The principle of least surprise.

While I agree, that doesn't mean we need to throw out all the power of having a unified file system, but it might beget some way of ensuring the user understands what `rm -rf /` actually does. There's certainly more than one way to solve this, some of which don't involve limiting what can be done with the FS. (As some examples: perhaps rm shouldn't recurse to a different FS, and objects of similar types are on different FSs, which prevent the very error that got us here; perhaps some files force "user acknowledgement" of their removal; perhaps it really does get mounted read-only.)

¹While you might be able to get away with "only root accesses UEFI vars" in the scenario that they're not in the file hierarchy, if you remove all non-real-files then you've got a lot of other things to deal with: unix sockets, block devices, terminals, all the various I/O ports, temp sensors, battery data… the list is extensive.

replies(1): >>11157248 #
useerup ◴[] No.11157248[source]
> No. The file system, is the abstraction. Adding /proc onto it is a use of that abstraction.

Agree that the file system is an abstraction. Makes us think in terms of directories (containers) and files (items). Everything in the file system is designed around the idea of files and directories. Permissions (rwx), operations (create, move, copy, append, delete).

However, already adding /proc challenges that. What does it mean to have "execute" right to a process? It is already running? What does it mean to append to a process? to move it? If processes are "files", why can I not kill the process by deleting the file? Processes are not naturally files. Yes, it makes somewhat sense if you think of /proc as status information being maintained for each process, i.e. they are extracts, owned by the OS.

But UEFI vars makes absolutely no sense. It is a true leaky abstraction. If one need to be able to write to UEFI vars, then create an API for it, possibly some utilities. That way I need not risk altering fundamental firmware settings by performing seemingly file system operations whose effect I expect to be limited to the hierarchy!

> The great thing about having a unified file hierarchy in the (b) abstraction is that tooling works on all of these different classes of objects different. It's really just the "CRUD" idiom, and normally it allows things to interoperate quite smoothly. I can write a bash script that draws a progress bar of my battery, and it requires no knowledge other than where in the file hierarchy the battery is.

But it actually just sweeping complexity under the rug. I need documentation for what the file contains on each "line" - what it means to write to it, etc. It is not discoverable at all. If you expose system resources as actual resources and do not try to map them onto files, you can actually make a discoverable system. An example of such a regime is CIM. On Windows, PowerShell (or Python or VBScript or ...) can be used to interact with such fundamental system resources. To use your example of a progress bar of the battery, here is an example of how the entire process from discovering the correct ressource (the battery) to displaying a progress bar on Windows without consulting documentation:

			PS C:\> #there's probably some class for batteries. let's look for it by name
			PS C:\> get-cimclass *battery*

			   NameSpace: ROOT/cimv2

			CimClassName                        CimClassMethods      CimClassProperties                                                                                                                                                                      
			------------                        ---------------      ------------------                                                                                                                                                                      
			CIM_Battery                         {SetPowerState, R... {Caption, Description, InstallDate, Name...}                                                                                                                                            
			Win32_Battery                       {SetPowerState, R... {Caption, Description, InstallDate, Name...}                                                                                                                                            
			Win32_PortableBattery               {SetPowerState, R... {Caption, Description, InstallDate, Name...}                                                                                                                                            
			CIM_AssociatedBattery               {}                   {Antecedent, Dependent}                                                                                                                                                                 

			PS C:\> # the Win32_Battery probably offers the most specific information

			PS C:\> Get-CimInstance Win32_Battery

			Caption                     : Internal Battery
			Description                 : Internal Battery
			Name                        : DELL 1C75X31
			Status                      : OK
			Availability                : 2
			CreationClassName           : Win32_Battery
			DeviceID                    : 647Samsung SDIDELL 1C75X31
			PowerManagementCapabilities : {1}
			PowerManagementSupported    : False
			SystemCreationClassName     : Win32_ComputerSystem
			...
			BatteryStatus               : 2
			Chemistry                   : 6
			DesignCapacity              : 
			DesignVoltage               : 12992
			EstimatedChargeRemaining    : 94
			EstimatedRunTime            : 71582788
			ExpectedLife                : 
			MaxRechargeTime             : 
			...
			ExpectedBatteryLife         : 

			PS C:\> # yep - that's it. lets save this instance in a variable
			PS C:\> $bat = Get-CimInstance Win32_Battery

			PS C:\> # display a progress bar and update it continually every 10 secs
			PS C:\> for(){ Write-Progress Battery -PercentComplete $bat.EstimatedChargeRemaining -Status "Charge remaining"; sleep 10 }

> This is, of course, a case where the power is somewhat biting us.

No, what biting us is a leaky abstraction that surprises us: We can accidentally delete firmware variables because file system operations are not constrained to the directories/files they operate on.

> That doesn't make the abstraction wrong

It is an abuse of the abstraction.

> Any other implementation of UEFI variables is going to have a "delete" call, AFAICT.

Indeed. In PowerShell you can discover the commands for manipulating by gcm UEFI

> What bit us here is that all the objects are in one bucket together, and thus rm -rf / removes more than just files.

No, what bit us is the broken expectation (a surprise) that a higher-level resource was mapped below some file system directory.

replies(2): >>11157496 #>>11158754 #
1. mjhoy ◴[] No.11158754[source]
I still am not convinced it is a leaky abstraction because of this. Perhaps it is a surprising one, it certainly took me a while to learn about it, I don't think I really appreciated universal file I/O until I worked through a bit of The Linux Programming Interface (recommended!). I see the filesystem now as a standard interface to many parts of the system (including hardware, processes, kernel state, etc) through the kernel. I don't think that /proc/1 is a process, it is an interface to information about a process. It makes sense to me UEFI variables are exposed this way. In fact if anything I'd say the abstraction is not leaky enough here: deleting the files deletes information on firmware, isn't that the promise of a "regular" file? :) Again, I am not defending the current behavior! There's nothing about the abstraction that says such a file must obey to `rm` in this way.

I recently ran into a funny bug, however, that makes me more sympathetic to your point. In Emacs a version of TRAMP mode (which is used to connect to remote servers or to connect locally as a different user) would try to cleanup itself by deleting some sort of tramp history file after a session. And if the history file didn't exist to begin with (or if a setting disabled it, I'm not sure exactly) someone thought it appropriate to simply open up "/dev/null", throwing away all writes to the file -- OK, makes sense so far.

But in TRAMP I often connect as root to my own machine -- it makes it easy to edit files as root, or run a shell in root. And as root you have the power to delete /dev/null! So, TRAMP would delete it without my knowledge... what's odd is that it quickly gets created again (perhaps by Emacs) so that it appears to exist, except that suddenly a) it's a regular file and b) it's owned as root without world write/read permission, so that suddenly all sorts of things start to fail because they can't open /dev/null. Fun.