Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions direct.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,80 @@ direct_readlinkat (struct ovl_layer *l, const char *path, char *buf, size_t bufs
return TEMP_FAILURE_RETRY (readlinkat (l->fd, path, buf, bufsiz));
}

static ino_t
direct_get_nfs_filehandle (const struct ovl_layer *l, const char *path)
{
int mount_id;
int ret = name_to_handle_at (l->fd, path, l->fh, &mount_id, 0);
if (ret == -1)
return 0;

ino_t h = 0xcbf29ce484222325ULL;
for (size_t i = 0; i < l->fh->handle_bytes; i++)
{
h ^= l->fh->f_handle[i];
h *= 0x100000001b3ULL;
}

return h;
}

/* Returns:
-1 - Error
0 - NFS filehandles not supported
1 - Can use NFS filehandles */
static int
has_nfs_filehandles (struct ovl_layer *l, const char *path)
{
struct file_handle *tmp_fh;
int mount_id;
int ret;

tmp_fh = malloc (sizeof (*tmp_fh));
if (tmp_fh == NULL)
return -1;

tmp_fh->handle_bytes = 0;

ret = name_to_handle_at (AT_FDCWD, path, tmp_fh, &mount_id, 0);
if (ret == -1 && errno == ENOTSUP)
{
free (tmp_fh);
return 0;
}
/* previous call should fail with EOVERFLOW and handle_bytes replaced with
* the size of the handle. EOVERFLOW can also occur if no filehandle is
* available in a system that does support file-handle lookup.
*/
if (ret != -1 || errno != EOVERFLOW || tmp_fh->handle_bytes == 0)
{
free (tmp_fh);
return -1;
}

l->fh = realloc (tmp_fh, tmp_fh->handle_bytes + sizeof (*l->fh));
if (! l->fh)
{
free (tmp_fh);
return -1;
}

ret = name_to_handle_at (AT_FDCWD, path, l->fh, &mount_id, 0);

if (ret == -1)
{
free (l->fh);
l->fh = NULL;
return 0;
}
return 1;
}

static int
direct_load_data_source (struct ovl_layer *l, const char *opaque, const char *path, int n_layer)
{
char tmp[64];
struct stat st;
l->path = realpath (path, NULL);
if (l->path == NULL)
{
Expand All @@ -184,6 +254,18 @@ direct_load_data_source (struct ovl_layer *l, const char *opaque, const char *pa
return l->fd;
}

if (fstat (l->fd, &st) == -1)
{
close (l->fd);
free (l->path);
l->path = NULL;
return -1;
}
else
l->st_dev = st.st_dev;

l->nfs_filehandles = has_nfs_filehandles (l, l->path);

if (fgetxattr (l->fd, XATTR_PRIVILEGED_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_PRIVILEGED;
else if (fgetxattr (l->fd, XATTR_OVERRIDE_CONTAINERS_STAT, tmp, sizeof (tmp)) >= 0)
Expand Down Expand Up @@ -230,4 +312,5 @@ struct data_source direct_access_ds = {
.listxattr = direct_listxattr,
.readlinkat = direct_readlinkat,
.support_acls = direct_support_acls,
.get_nfs_filehandle = direct_get_nfs_filehandle,
};
76 changes: 59 additions & 17 deletions fuse-overlayfs.1
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ fuse-overlayfs - overlayfs FUSE implementation
.SH SYNOPSIS
.PP
mounting
fuse-overlayfs [-f] [--debug] [-o OPTS] MOUNT_TARGET
fuse-overlayfs [\-f] [\-\-debug] [\-o OPTS] MOUNT_TARGET

.PP
unmounting
fusermount -u mountpoint
fusermount \-u mountpoint


.SH DESCRIPTION
Expand All @@ -25,26 +25,26 @@ namespace.

.SH OPTIONS
.PP
\fB--debug\fP
\fB\-\-debug\fP
Enable debugging mode, can be very noisy.

.PP
\fB-o lowerdir=low1[:low2...]\fP
\fB\-o lowerdir=low1[:low2...]\fP
A list of directories separated by \fB\fC:\fR\&. Their content is merged.

.PP
\fB-o upperdir=upperdir\fP
\fB\-o upperdir=upperdir\fP
A directory merged on top of all the lowerdirs where all the changes
done to the file system will be written.

.PP
\fB-o workdir=workdir\fP
\fB\-o workdir=workdir\fP
A directory used internally by fuse-overlays, must be on the same file
system as the upper dir.

.PP
\fB-o uidmapping=UID:MAPPED-UID:LEN[,UID2:MAPPED-UID2:LEN2]\fP
\fB-o gidmapping=GID:MAPPED-GID:LEN[,GID2:MAPPED-GID2:LEN2]\fP
\fB\-o uidmapping=UID:MAPPED-UID:LEN[,UID2:MAPPED-UID2:LEN2]\fP
\fB\-o gidmapping=GID:MAPPED-GID:LEN[,GID2:MAPPED-GID2:LEN2]\fP
Specifies the dynamic UID/GID mapping used by fuse-overlayfs when
reading/writing files to the system.

Expand All @@ -62,7 +62,7 @@ without requiring to chown the files.
For example, given on the host two files like:

.PP
$ stat -c %u:%g lower/a lower/b
$ stat \-c %u:%g lower/a lower/b
0:0
1:1

Expand All @@ -76,7 +76,7 @@ $ cat /proc/self/uid_map
We would see:

.PP
$ stat -c %u:%g merged/a merged/b
$ stat \-c %u:%g merged/a merged/b
65534:65534
65534:65534

Expand All @@ -87,32 +87,32 @@ mapped.

.PP
In the above example, if we mount the fuse-overlayfs file system using:
\fB\fC-ouidmapping=0:1000:1:1:110000:65536,gidmapping=0:1000:1:1:110000:65536\fR,
\fB\fC\-ouidmapping=0:1000:1:1:110000:65536,gidmapping=0:1000:1:1:110000:65536\fR,
which is the namespace configuration specified on a single line, we'd
see from the same user namespace:

.PP
$ stat -c %u:%g merged/a merged/b
$ stat \-c %u:%g merged/a merged/b
0:0
1:1

.PP
Those are the same IDs visible from outside the user namespace.

.PP
\fB-o squash_to_root\fP
\fB\-o squash_to_root\fP
Every file and directory is owned by the root user (0:0).

.PP
\fB-o squash_to_uid=uid\fP
\fB-o squash_to_gid=gid\fP
\fB\-o squash_to_uid=uid\fP
\fB\-o squash_to_gid=gid\fP
Every file and directory is owned by the specified uid or gid.

.PP
It has higher precedence over \fBsquash_to_root\fP\&.

.PP
\fB-o static_nlink\fP
\fB\-o static_nlink\fP
Set st_nlink to the static value 1 for all directories.

.PP
Expand All @@ -122,9 +122,51 @@ be a slow operation. With this option enabled, the number of hard
links reported when running stat for any directory is 1.

.PP
\fB-o noacl\fP
\fB\-o noacl\fP
Disable ACL support in the FUSE file system.

.PP
\fB\-o xino=off|auto|on\fP
Controls how \fIst_ino\fP values are generated for files exposed by fuse-overlayfs.

When all lower and upper layers reside on the same underlying device,
fuse-overlayfs exposes the real inode number from the underlying filesystem.
When layers span multiple devices, an opaque inode number is generated; by
default this value is not stable across mounts.

The \fBxino\fP option modifies this behavior:

.PP
\fB\-o xino=off\fP
Disables extended inode generation. This matches the default behavior:
when all layers are on the same device, the underlying inode number is used;
otherwise an opaque, non‑stable inode number is returned.

.PP
\fB\-o xino=auto\fP
Attempts to generate stable inode numbers across mounts by hashing the file
handle returned by \fIname_to_handle_at\fP(2).
This mode is used only if all layers support \fIname_to_handle_at\fP(2); if any
layer does not, behavior falls back to \fBxino=off\fP.
If all layers are on the same device, the underlying inode number is still
used, regardless of this setting.

.PP
\fB\-o xino=on\fP
Requires that all layers support \fIname_to_handle_at\fP(2). If they do, inode
numbers are derived from a hash of the file handle and remain stable across
mounts.
If any layer does not support \fIname_to_handle_at\fP(2), the mount fails.
As with other modes, when all layers are on the same device, the underlying
inode number always takes precedence.

.PP
\fB\-o ino32_t\fP
Forces all returned \fIst_ino\fP values to be truncated to 32 bits.

This option exists solely for compatibility with older 32‑bit userspaces that
cannot correctly handle 64‑bit inode numbers. It has no functional benefit on
modern systems and should not be used unless required for legacy compatibility.

.SH SEE ALSO
.PP
Expand Down
38 changes: 38 additions & 0 deletions fuse-overlayfs.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,44 @@ links reported when running stat for any directory is 1.
**-o noacl**
Disable ACL support in the FUSE file system.

**-o xino=off|auto|on**
Controls how `st_ino` values are generated for files returned by fuse-overlayfs.

When all lower and upper layers reside on the same underlying device,
fuse-overlayfs exposes the real inode number from the underlying filesystem.
When layers span multiple devices, an opaque inode number is generated; by
default this value is not stable across mounts.

The `xino` option modifies this behavior:

**xino=off**
Disables extended inode generation. This matches the default behavior:
when all layers are on the same device, the underlying inode number is used;
otherwise an opaque, non‑stable inode number is returned.

**xino=auto**
Attempts to generate stable inode numbers across mounts by hashing the file
handle returned by `name_to_handle_at(2)`.
This mode is used only if all layers support `name_to_handle_at(2)`; if any
layer does not, behavior falls back to `xino=off`.
If all layers are on the same device, the underlying inode number is still
used, regardless of this setting.

**xino=on**
Requires that all layers support `name_to_handle_at(2)`. If they do, inode
numbers are derived from a hash of the file handle and remain stable across
mounts.
If any layer does not support `name_to_handle_at(2)`, the mount fails.
As with other modes, when all layers are on the same device, the underlying
inode number always takes precedence.

**-o ino32_t**
Forces all returned `st_ino` values to be truncated to 32 bits.

This option exists solely for compatibility with older 32‑bit userspaces that
cannot correctly handle 64‑bit inode numbers. It has no functional benefit on
modern systems and should not be used unless required for legacy compatibility.

# SEE ALSO

**fuse**(8), **mount**(8), **user_namespaces**(7)
Expand Down
8 changes: 8 additions & 0 deletions fuse-overlayfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ struct ovl_data
int squash_to_uid;
int squash_to_gid;
int static_nlink;
int ino_passthrough;
int nfs_filehandles;
int ino_t_32;

int volatile_mode;

Expand Down Expand Up @@ -137,6 +140,10 @@ struct ovl_layer

void *data_source_private_data;
int stat_override_mode;

dev_t st_dev;
int nfs_filehandles;
struct file_handle *fh;
};

/* a data_source defines the methods for accessing a lower layer. */
Expand All @@ -156,6 +163,7 @@ struct data_source
int (*getxattr) (struct ovl_layer *l, const char *path, const char *name, char *buf, size_t size);
ssize_t (*readlinkat) (struct ovl_layer *l, const char *path, char *buf, size_t bufsiz);
bool (*support_acls) (struct ovl_layer *l);
ino_t (*get_nfs_filehandle) (const struct ovl_layer *l, const char *path);
};

/* passthrough to the file system. */
Expand Down
Loading
Loading