setfsgid(2)

πŸ’– setfsgid(2) β€” the "which group am I, filesystem-wise" situation πŸ’–

okay bestie, we're in setfsgid(2) territory, which lives in the System Calls Manual and is genuinely one of those "why does this even exist" girlies. let's get into it 🫢


✨ NAME ✨

setfsgid β€” set group identity used for filesystem checks


πŸ“š LIBRARY πŸ“š

Standard C library (libc, -lc) β€” the usual bestie, no weird extra linking needed πŸ’…


🌸 SYNOPSIS 🌸

#include <sys/fsuid.h>

[[deprecated]] int setfsgid(gid_t fsgid);

not me pointing out that [[deprecated]] sitting right there, front and center 😭 the compiler is literally trying to warn you. listen to it.


πŸ’œ DESCRIPTION πŸ’œ

so on Linux, a process is carrying two group IDs around, not one. iconic, messy, very main-character:

  • the filesystem group ID β€” a Linux-specific lil guy, used for permission checking when you're accessing filesystem objects
  • the effective group ID β€” used for some other kinds of permission checks (peep credentials(7) if you wanna go deeper, iykyk)

normally these two are the same value, fr fr. that's because any time a process's effective group ID changes, the kernel is like "ok and I'm changing the filesystem group ID too, to match the new effective group ID," automatically. no notes, very efficient.

but a process can make them diverge (split them apart, give them separate eras ✨) by calling setfsgid() and setting its filesystem group ID to the value you pass in fsgid.

now, the gatekeeping part β€” setfsgid() will succeed only if the caller is the superuser, or if fsgid matches one of these:

  • the caller's real group ID
  • the caller's effective group ID
  • the caller's saved set-group-ID
  • or the current filesystem user ID

yes, it says filesystem user ID there, and that's exactly what the original says, so we're keeping it verbatim, no editing the source material, periodt. πŸŽ€


🌈 RETURN VALUE 🌈

plot twist that is also extremely annoying: on both success AND failure, this call returns the previous filesystem group ID of the caller. same value either way. no differentiation. it's giving "I'm not going to tell you what happened" πŸ’€


⭐ STANDARDS ⭐

Linux. that's it. that's the whole section. Linux. πŸ‘‘


πŸ¦‹ HISTORY πŸ¦‹

Linux 1.2. baby. vintage. ✨

πŸŽ€ C library/kernel differences πŸŽ€

listen up because this one is a specific little historical artefact: in glibc 2.15 and earlier, when the wrapper for this system call figured out that the argument can't be passed to the kernel without integer truncation (because the kernel is old and does not support 32-bit group IDs), it would return -1 and set errno to EINVAL β€” and it would do that without even attempting the system call. didn't even try. πŸ™ˆ


πŸ’… NOTES πŸ’…

the whole filesystem group ID concept and the setfsgid() system call were invented for historical reasons that are no longer applicable on modern Linux kernels. yes, really. the lore is expired.

go read setfsuid(2) for the full discussion of why using both setfsuid(2) and setfsgid() is nowadays unneeded. consider it required reading, bestie. πŸ“–

more lore: the original Linux setfsgid() system call only supported 16-bit group IDs. then Linux 2.4 came along and added setfsgid32(), which supports 32-bit IDs. and the glibc setfsgid() wrapper function? it transparently deals with the variation across kernel versions for you. queen behavior, honestly. πŸ‘‘


πŸ”₯ BUGS πŸ”₯

okay this is the part where we get real, because it is not a good time:

no error indications of any kind are returned to the caller. none. zero. and because both successful and unsuccessful calls return the same value, it is impossible to directly determine whether the call succeeded or failed. 😭

so what do you do instead? the caller must resort to looking at the return value from a further call, such as:

setfsgid(-1)

which will always fail β€” that's your little probe. you use it to figure out whether a preceding call to setfsgid() actually changed the filesystem group ID. yes, you have to call the thing again, with a definitely-bad argument, to interrogate the last one. it's giving detective work. πŸ”

and here's what the manpage straight up says should happen: at the very least, EPERM should be returned when the call fails (because the caller lacks the CAP_SETGID capability). like. at minimum. please. πŸ₯²


🧚 SEE ALSO 🧚


okay that's the whole page, bestie!! and before I dip β€” this was Linux man-pages 6.20, dated 2026-02-08, and that's the setfsgid(2) page, signed, sealed, delivered πŸ’–βœ¨πŸŽ€ go call setfsuid(2) next, it's the sibling, they're a duo, it's giving duo energy πŸ¦‹

πŸ“¦ where this page comes from
  • arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • πŸ“œ the page's license: Linux-man-pages-copyleft
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms