@@ -202,6 +202,24 @@ static int _fcaps_save(struct vfs_cap_data *rawvfscap, cap_t cap_d, int *bytes_p
* endian machine we need to fix this up.
*/
rawvfscap->rootid = FIXUP_32BITS(cap_d->rootid);
+ if (rawvfscap->rootid == 0) {
+ /* If libcap was compiled on a kernel supporting VFS_CAP_REVISION_3 but
+ * running on a kernel that does not support VFS_CAP_REVISION_3 we
+ * should always pass down a legacy struct vfs_cap_data if the rootid is
+ * 0. On kernels supporting VFS_CAP_REVISION_3 the kernel will take care
+ * of translating it from VFS_CAP_REVISION_2 to a VFS_CAP_REVISION_3
+ * version. We can elegantly handle both cases by setting magic to
+ * VFS_CAP_REVISION_2 and only passing down XATTR_CAPS_SZ_2 bytes which
+ * will leave out the rootid field. If the rootid field is not 0 then
+ * we will pass down the VFS_CAP_REVISION_3 and XATTR_CAPS_SZ_3. On
+ * kernels supporting VFS_CAP_REVISION_3 this will succeed on kernels
+ * not supporting VFS_CAP_REVISION_3 this will fail. The failure on kernels
+ * not supporting VFS_CAP_REVISION_3 is wanted since the user explicitly
+ * requested an unprivileged file capability but the kernel does not
+ * actually support it. So fail hard. */
+ magic = VFS_CAP_REVISION_2;
+ *bytes_p = XATTR_CAPS_SZ_2;
+ }
#endif
if (eff_not_zero == 0) {