To free dynamically allocated Rust data, C functions registering callbacks should have a signature like the one shown below: ```C void widget_register_callback(   widget_t *widget,   void *data,   void (*callback)(void*, event_t),   void (*destroy)(void*) ); ``` See this blog post [Neat Rust Tricks: Passing Rust Closures to C](http://blog.sagetheprogrammer.com/neat-rust-tricks-passing-rust-closures-to-c) for a presentation of what I dream `util-linux` had. (���)� While reading `util-linux`'s source code, I identified three types of functions to register callbacks: - functions for which it is essentially impossible to create Rust bindings,  case in point: `fdisk_table_sort_partitions` (see documentation further down).  This function has a signature that looks like the example below.  ```C  void widget_register_callback(    widget_t *widget,    void (*callback)(event_t),  );  ```  You will notice that it does not:  + have a data pointer (`void *data`),  + provide a callback function that accepts a `void*` parameter,  + offer a way to set a destructor. - functions for registering long-lived callbacks with a signature like the one  shown below  ```C  void widget_register_callback(    widget_t *widget,    void (*callback)(void*, event_t),  );  ```  For example, `mnt_table_set_parser_errcb` that acts on a `struct  libmnt_table`. This structure has an internal field to store a data pointer,  along with specialized accessor functions to get/set it (respectively  `mnt_table_get_userdata `, and `mnt_table_set_userdata `). However, lacking a  way to define a destructor, this function will leak memory each time it is  called to replace a callback. - functions for registering short-lived callbacks with a signature like the one  shown below  ```C  void widget_register_callback(    widget_t *widget,    void *data,    void (*callback)(void*, event_t),  );  ```  For example, `mnt_table_find_next_fs` which is the closest to the ideal form.  Although it lacks a way to define a destructor, being short-lived, it allows  the enclosing Rust binding to take care of releasing resources allocated on  the heap. https://github.com/util-linux/util-linux/blob/stable/v2.39/libfdisk/src/fdiskP.h#L200-L205 ```C struct fdisk_table { struct list_head parts; /* partitions */ int refcount; size_t nents; /* number of partitions */ }; ``` https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.39/libfdisk-docs/libfdisk-Table.html#fdisk-table-sort-partitions ```text int fdisk_table_sort_partitions (struct fdisk_table *tb,                int (*cmp) (struct fdisk_partition *, struct fdisk_partition *)); Sort partition in the table. Parameters tb table cmp compare function Returns 0 on success, <0 on error. ``` https://github.com/util-linux/util-linux/blob/stable/v2.39/libmount/src/mountP.h#L249-L273 ```C /*  * fstab/mountinfo file  */ struct libmnt_table { int fmt; /* MNT_FMT_* file format */ int nents; /* number of entries */ int refcount; /* reference counter */ int comms; /* enable/disable comment parsing */ char *comm_intro; /* First comment in file */ char *comm_tail; /* Last comment in file */ struct libmnt_cache *cache; /* canonicalized paths/tags cache */     int (*errcb)(struct libmnt_table *tb, const char *filename, int line); int (*fltrcb)(struct libmnt_fs *fs, void *data); void *fltrcb_data; int noautofs; /* ignore autofs mounts */ struct list_head ents; /* list of entries (libmnt_fs) */ void *userdata; }; ``` https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.39/libmount-docs/libmount-Table-of-filesystems.html#mnt-table-set-parser-errcb ```text int mnt_table_set_parser_errcb (struct libmnt_table *tb,               int (*cb) (struct libmnt_table *tb, const char *filename, int line)); The error callback function is called by table parser (mnt_table_parse_file()) in case of a syntax error. The callback function could be used for error evaluation, libmount will continue/stop parsing according to callback return codes: <0 : fatal error (abort parsing) 0 : success (parsing continues) >0 : recoverable error (the line is ignored, parsing continues). Parameters tb pointer to table cb pointer to callback function Returns 0 on success or negative number in case of error. ``` https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.39/libmount-docs/libmount-Table-of-filesystems.html#mnt-table-get-userdata ```text void * mnt_table_get_userdata (struct libmnt_table *tb); Parameters tb pointer to tab Returns pointer to user's data. ``` https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.39/libmount-docs/libmount-Table-of-filesystems.html#mnt-table-set-userdata ```text int mnt_table_set_userdata (struct libmnt_table *tb,             void *data); Sets pointer to the private user data. Parameters tb pointer to tab data pointer to user data Returns 0 on success or negative number in case of error. ``` https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.39/libmount-docs/libmount-Table-of-filesystems.html#mnt-table-find-next-fs ```text int mnt_table_find_next_fs (struct libmnt_table *tb,             struct libmnt_iter *itr,             int (*match_func) (struct libmnt_fs *, void *),             void *userdata,             struct libmnt_fs **fs); This function allows searching in tb . Parameters tb table itr iterator match_func function returning 1 or 0 userdata extra data for match_func fs NULL or returns pointer to the next matching table entry Returns negative number in case of error, 1 at end of table or 0 o success. ``` G2 - How can I create device images that will trigger errors when running unit-tests? For example, files in `util-linux/tests/expected/blkid` show the expected outputs after probing the properties of device images in `util-linux/tests/ts/blkid/images-fs`. Each image in the directory has a well-formed partition table, so tests will always walk the "happy path". There is no image that could, for example, trigger a collision error between probing results. Rust provides its own test framework, with which it is easy to check functions (including error cases) at a more granular level than with the test suite in `util-linux`. Knowing how to create an intentionally broken device image would be wonderful. It would allow me to increase test coverage of my Rust-bindings. ## libblkid B1 - Are there public accessor functions on the roadmap of `libblkid` for reading the private fields `bid_pri`, `bid_devno`, and `bid_time` in `blkid_dev_struct`? For the moment, I can not accurately reproduce the way data about a `blkid_struct_dev` is printed in `/run/blkid/blkid.tab` due to not being able to access the relevant fields. Example of a line in `/run/blkid/blkid.tab` ```text <device DEVNO="0xfe01" TIME="1687337407.788618" PRI="45" LABEL="root" UUID="9e4adae9-4122-47fe-848f-67a9eb726207" BLOCK_SIZE="4096" TYPE="ext4">/dev/mapper/vg_nixos-root</device> ``` What I can currently output from Rust ```text <device LABEL="root" UUID="9e4adae9-4122-47fe-848f-67a9eb726207" BLOCK_SIZE="4096" TYPE="ext4">/dev/mapper/vg_nixos-root</device> ``` https://github.com/util-linux/util-linux/blob/8aa25617467a1249669cff7240ca31973bf9a127/libblkid/src/blkidP.h#L45-L61 ```C struct blkid_struct_dev { struct list_head bid_devs; /* All devices in the cache */ struct list_head bid_tags; /* All tags for this device */ blkid_cache bid_cache; /* Dev belongs to this cache */ char *bid_name; /* Device real path (as used in cache) */ char *bid_xname; /* Device path as used by application (maybe symlink..) */ char *bid_type; /* Preferred device TYPE */ int bid_pri; /* Device priority */ dev_t bid_devno; /* Device major/minor number */ time_t bid_time; /* Last update time of device */ suseconds_t bid_utime; /* Last update time (microseconds) */ unsigned int bid_flags; /* Device status bitflags */ char *bid_label; /* Shortcut to device LABEL */ char *bid_uuid; /* Shortcut to binary UUID */ }; ``` B2 - Does anyone know what the function `blkid_dev_set_search` is supposed to do? There are no docstrings for this function. Although I read its source code, I could not figure out its purpose.  <('^-^) https://github.com/util-linux/util-linux/blob/stable/v2.39/libblkid/src/dev.c#L150-L172 ```C int blkid_dev_set_search(blkid_dev_iterate iter, const char *search_type, const char *search_value) { char *new_type, *new_value; if (!iter || iter->magic != DEV_ITERATE_MAGIC || !search_type ||   !search_value) return -1; new_type = malloc(strlen(search_type)+1); new_value = malloc(strlen(search_value)+1); if (!new_type || !new_value) { free(new_type); free(new_value); return -1; } strcpy(new_type, search_type); strcpy(new_value, search_value); free(iter->search_type); free(iter->search_value); iter->search_type = new_type; iter->search_value = new_value; return 0; } ``` B3 -  Do I define `size` as `uint64_t` or `int64_t`? The `size` field in `struct blkid_struct_probe` is of type `uint64_t`. But, according to the documentation, its accessor function `blkid_probe_get_sectors` may return `-1` in case of error. Reading the code of `blkid_probe_get_sectors`, I can see that it just divides `size` by 512 which would never return a negative value (see below). https://github.com/util-linux/util-linux/blob/stable/v2.39/libblkid/src/blkidP.h#L203-L233 ```C /*  * Low-level probing control struct  */ struct blkid_struct_probe { int fd; /* device file descriptor */ uint64_t off; /* begin of data on the device */ uint64_t size; /* end of data on the device */ dev_t devno; /* device number (st.st_rdev) */ dev_t disk_devno; /* devno of the whole-disk or 0 */ unsigned int blkssz; /* sector size (BLKSSZGET ioctl) */ mode_t mode; /* struct stat.sb_mode */ uint64_t zone_size; /* zone size (BLKGETZONESZ ioctl) */ int flags; /* private library flags */ int prob_flags; /* always zeroized by blkid_do_*() */ uint64_t wipe_off; /* begin of the wiped area */ uint64_t wipe_size; /* size of the wiped area */ struct blkid_chain *wipe_chain; /* superblock, partition, ... */ struct list_head buffers; /* list of buffers */ struct list_head hints; struct blkid_chain chains[BLKID_NCHAINS]; /* array of chains */ struct blkid_chain *cur_chain; /* current chain */ struct list_head values; /* results */ struct blkid_struct_probe *parent; /* for clones */ struct blkid_struct_probe *disk_probe; /* whole-disk probing */ }; ``` https://github.com/util-linux/util-linux/blob/stable/v2.39/libblkid/src/blkid.h.in#L85-L90 ```C /**  * blkid_loff_t:  *  * 64-bit signed number for offsets and sizes  */ typedef int64_t blkid_loff_t; ``` https://github.com/util-linux/util-linux/blob/8aa25617467a1249669cff7240ca31973bf9a127/libblkid/src/probe.c#L1971-L1983 ```C /**  * blkid_probe_get_sectors:  * @pr: probe  *  * Returns: 512-byte sector count or -1 in case of error.  */ blkid_loff_t blkid_probe_get_sectors(blkid_probe pr) { return (blkid_loff_t) (pr->size >> 9); } ``` B4 - Is the documentation out-of-date or just anticipating a future change? The function `blkid_probe_set_sectorsize` always returns `0`, but its docstring says otherwise. https://github.com/util-linux/util-linux/blob/8aa25617467a1249669cff7240ca31973bf9a127/libblkid/src/probe.c#L2014-L2030 ```C /**  * blkid_probe_set_sectorsize:  * @pr: probe  * @sz: new size (to overwrite system default)  *  * Note that blkid_probe_set_device() resets this setting. Use it after  * blkid_probe_set_device() and before any probing call.  *  * Since: 2.30  *  * Returns: 0 or <0 in case of error  */ int blkid_probe_set_sectorsize(blkid_probe pr, unsigned int sz) { pr->blkssz = sz; return 0; } ``` B5 - Is there a list of all file systems supported by `libblkid`? By searching the source code, I managed to constitute a list of supported file systems gathered in the enum [`FileSystem`](https://docs.rs/rsblkid/latest/rsblkid/partition/enum.FileSystem.html#variants) in `rsblkid`. It would be nice if I could double-check its accuracy from an official reference. B6 - Is there a list of all tags supported by `libblkid`? By searching the source code, I managed to constitute a list of supported tags gathered in the enum [`Tag`](https://docs.rs/rsblkid/latest/rsblkid/device/enum.Tag.html#variants) in `rsblkid`. It would be nice if I could double-check its accuracy from an official reference. ## libfdisk F1 - What is a `grain size`? The documentation of `fdisk_get_grain_size` says "grain in bytes used to align partitions (usually 1MiB)" but does not elaborate. https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.39/libfdisk-docs/libfdisk-Context.html#fdisk-get-grain-size I stumbled on this answer about partition alignment during my search: "What is partition alignment and why whould I need it?" https://superuser.com/a/393918 Is it what `fdisk_get_grain_size` is refering to? F2 - Are `fdisk_labelitem_*` supposed to have globally unique values, or is the overlap intentional? The way they are currently defined `SUN_LABELITEM_LABELID`, `BSD_LABELITEM_TYPE`,`SGI_LABELITEM_PCYLCOUNT` , and `GPT_LABELITEM_FIRSTLBA` end up having the same numerical value i.e.  `__FDISK_NLABELITEMS = 8`. As I understand it, these LABELITEM are fields in a partition table header for their respective partition table types. To represent them in Rust, I use [HeaderEntry](https://docs.rs/rsfdisk/latest/rsfdisk/partition_table/enum.HeaderEntry.html#) which groups the LABELITEMs in a unified enum. This enum uses a workaround to convert a `HeaderEntry` to the native `u32` value used by `libfdisk` https://github.com/nickpiaddo/rsfdisk/blob/main/src/core/partition_table/header_entry_enum.rs But converting back from `u32` to `HeaderEntry` is all but impossible if the value of each `fdisk_labelitem_*` is not globally unique. See their definitions below. https://github.com/util-linux/util-linux/blob/8aa25617467a1249669cff7240ca31973bf9a127/libfdisk/src/libfdisk.h.in#L414-L425 ```C  /**   * fdisk_labelitem_gen:   * @FDISK_LABELITEM_ID: Unique disk identifier   * @__FDISK_NLABELITEMS: Specifies reserved range for generic items (0..7)   *   * Generic disklabel items.   */  enum fdisk_labelitem_gen {   FDISK_LABELITEM_ID = 0,   __FDISK_NLABELITEMS = 8  }; ``` https://github.com/util-linux/util-linux/blob/8aa25617467a1249669cff7240ca31973bf9a127/libfdisk/src/libfdisk.h.in#L634-L655 ```C   /**   * fdisk_labelitem_sun:   * @SUN_LABELITEM_LABELID: Label ID   * @SUN_LABELITEM_VTOCID: Volume ID   * @SUN_LABELITEM_RPM: Rpm   * @SUN_LABELITEM_ACYL: Alternate cylinders   * @SUN_LABELITEM_PCYL: Physical cylinders   * @SUN_LABELITEM_APC: Extra sects/cyl   * @SUN_LABELITEM_INTRLV: Interleave   *   * SUN specific label items.   */  enum fdisk_labelitem_sun {   SUN_LABELITEM_LABELID = __FDISK_NLABELITEMS,   SUN_LABELITEM_VTOCID,   SUN_LABELITEM_RPM,   SUN_LABELITEM_ACYL,   SUN_LABELITEM_PCYL,   SUN_LABELITEM_APC,   SUN_LABELITEM_INTRLV  }; ``` https://github.com/util-linux/util-linux/blob/8aa25617467a1249669cff7240ca31973bf9a127/libfdisk/src/libfdisk.h.in#L661-L697 ```C   /**   * fdisk_labelitem_bsd:   * @BSD_LABELITEM_TYPE: type   * @BSD_LABELITEM_DISK: disk   * @BSD_LABELITEM_PACKNAME: packname   * @BSD_LABELITEM_FLAGS: flags (removable, ecc, badsect)   * @BSD_LABELITEM_SECSIZE: Bytes/Sector   * @BSD_LABELITEM_NTRACKS: Tracks/Cylinder   * @BSD_LABELITEM_SECPERCYL: Sectors/Cylinder   * @BSD_LABELITEM_CYLINDERS: Cylinders   * @BSD_LABELITEM_RPM: rpm   * @BSD_LABELITEM_INTERLEAVE: interleave   * @BSD_LABELITEM_TRACKSKEW: trackskew   * @BSD_LABELITEM_CYLINDERSKEW: cylinderskew   * @BSD_LABELITEM_HEADSWITCH: headswitch   * @BSD_LABELITEM_TRKSEEK: track-to-track seek   *   * BSD specific label items.   */  enum fdisk_labelitem_bsd {   /* specific */   BSD_LABELITEM_TYPE = __FDISK_NLABELITEMS,   BSD_LABELITEM_DISK,   BSD_LABELITEM_PACKNAME,   BSD_LABELITEM_FLAGS,   BSD_LABELITEM_SECSIZE,   BSD_LABELITEM_NTRACKS,   BSD_LABELITEM_SECPERCYL,   BSD_LABELITEM_CYLINDERS,   BSD_LABELITEM_RPM,   BSD_LABELITEM_INTERLEAVE,   BSD_LABELITEM_TRACKSKEW,   BSD_LABELITEM_CYLINDERSKEW,   BSD_LABELITEM_HEADSWITCH,   BSD_LABELITEM_TRKSEEK  }; ``` https://github.com/util-linux/util-linux/blob/8aa25617467a1249669cff7240ca31973bf9a127/libfdisk/src/libfdisk.h.in#L704-L719 ```C   /**   * fdisk_labelitem_sgi:   * @SGI_LABELITEM_PCYLCOUNT: Physical cylinders   * @SGI_LABELITEM_SPARECYL: Extra sects/cyl   * @SGI_LABELITEM_ILFACT: nterleave   * @SGI_LABELITEM_BOOTFILE: Bootfile   *   * SGI specific label items.   */  enum fdisk_labelitem_sgi {   SGI_LABELITEM_PCYLCOUNT = __FDISK_NLABELITEMS,   SGI_LABELITEM_SPARECYL,   SGI_LABELITEM_ILFACT,   SGI_LABELITEM_BOOTFILE  }; ``` https://github.com/util-linux/util-linux/blob/8aa25617467a1249669cff7240ca31973bf9a127/libfdisk/src/libfdisk.h.in#L775-L798 ```C   /**   * fdisk_labelitem_gpt:   * @GPT_LABELITEM_ID: GPT disklabel UUID (!= partition UUID)   * @GPT_LABELITEM_FIRSTLBA: First Usable LBA   * @GPT_LABELITEM_LASTLBA: Last Usable LBA   * @GPT_LABELITEM_ALTLBA: Alternative LBA (backup header LBA)   * @GPT_LABELITEM_ENTRIESLBA: Partitions entries array LBA   * @GPT_LABELITEM_ENTRIESALLOC: Number of allocated entries in entries array   * @GPT_LABELITEM_ENTRIESLASTLBA: Last LBA where is entries array   *   * GPT specific label items.   */  enum fdisk_labelitem_gpt {   /* generic */   GPT_LABELITEM_ID = FDISK_LABELITEM_ID,   /* specific */   GPT_LABELITEM_FIRSTLBA = __FDISK_NLABELITEMS,   GPT_LABELITEM_LASTLBA,   GPT_LABELITEM_ALTLBA,   GPT_LABELITEM_ENTRIESLBA,   GPT_LABELITEM_ENTRIESALLOC,   GPT_LABELITEM_ENTRIESLASTLBA  }; ``` F3 - In the function `fdisk_locate_disklabel`, in what unit are `offset`, and `size` expressed? bytes? sectors? https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.39/libfdisk-docs/libfdisk-Label.html#fdisk-locate-disklabel ```text  int  fdisk_locate_disklabel (struct fdisk_context *cxt,              int n,              const char **name,              uint64_t *offset,              size_t *size);  Locate disklabel and returns info about n item of the label.  For example GPT is composed from three items, PMBR and GPT, n=0 return offset  to PMBR and n=1 return offset to GPT Header and n=2 returns offset to GPT array  of partitions, n=3 and n=4 returns location of the backup GPT label at the end  of the disk.  The function returns the current in-memory situation. It's possible that a  header location is modified by write operation, for example when enabled  minimization (see fdisk_gpt_enable_minimize()). In this case it's better to  call this function after fdisk_write_disklabel().  For more details see 'D' expert fdisk command.  Parameters   cxt context   n N item   name return item name   offset return offset where is item   size of the item  Returns   0 on success, <0 on error, 1 no more items. ``` ## libmount M1 - Does `libmount` plan to provide public functions to access/set the private `fmt` field in `struct libmnt_table`?