sync_file_range(2)

โœจ sync_file_range โ€” bestie edition โœจ

okay so this is sync_file_range(2) from the System Calls Manual, and it's a whole situation ๐Ÿ’…


๐ŸŽ€ NAME ๐ŸŽ€

sync_file_range โ€” sync a file segment with disk. that's it, that's the vibe, we're syncing a segment of a file to disk ๐Ÿฆ‹

๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library (libc, -lc). the classic. the blueprint. no cap ๐Ÿ’–

๐ŸŒธ SYNOPSIS ๐ŸŒธ

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#define _FILE_OFFSET_BITS 64
#include <fcntl.h>

int sync_file_range(int fd, off_t offset, off_t nbytes,
                    unsigned int flags);

like, bestie, you need those defines up top. _GNU_SOURCE is giving main character energy (peep feature_test_macros(7) for the lore), and _FILE_OFFSET_BITS 64 is there so your offsets don't get humbled on 32-bit platforms ๐Ÿซถ

๐Ÿ’– DESCRIPTION ๐Ÿ’–

sync_file_range() lets you have fine control when synchronizing the open file that the file descriptor fd points to, with disk. it's giving precision. it's giving artistry. โœจ

offset is the starting byte of the file range you wanna synchronize.

nbytes is how long that range is, in bytes โ€” and here's the spicy part: if nbytes is zero, then literally every byte from offset all the way to the end of file gets synchronized. ๐ŸŒˆ

and the units?? synchronization happens in units of the system page size, so:

  • offset gets rounded down to a page boundary
  • (offset+nbytes-1) gets rounded up to a page boundary

periodt. math is math. ๐Ÿงš

The flags bit-mask argument can include any of these values:

  • SYNC_FILE_RANGE_WAIT_BEFORE โ€” wait for write-out of every page in the specified range that's already been submitted to the device driver for write-out, before you do any writing at all. like waiting your turn, but for pages. ๐Ÿ’…
  • SYNC_FILE_RANGE_WRITE โ€” kick off write-out of all the dirty pages in the specified range that aren't currently submitted for write-out. ngl, even this one may block if you try to write more than the request queue size ๐Ÿ˜ญ
  • SYNC_FILE_RANGE_WAIT_AFTER โ€” wait for write-out of all the pages in the range after you've done any writing.

Specifying flags as 0 is permitted โ€” it's a no-op. literally just vibes, zero consequences ๐Ÿ’€

โš ๏ธ Warning โš ๏ธ

okay bestie, sit down for this one because it's serious:

This system call is extremely dangerous and should not be used in portable programs.

none of these operations writes out the file's metadata. so unless your application is strictly performing overwrites of already-instantiated disk blocks, there are no guarantees your data will survive a crash. ๐Ÿ˜ญ there's no user interface to tell you whether a write is purely an overwrite. and on filesystems using copy-on-write semantics (e.g. btrfs), an overwrite of existing allocated blocks is impossible. when writing into preallocated space, lots of filesystems also need calls into the block allocator โ€” and this system call does not sync that out to disk. and this system call does not flush disk write caches, so it provides no data integrity on systems with volatile disk write caches.

tl;dr: iconic? maybe. safe? absolutely not. ๐Ÿ™ˆ

๐Ÿ” Some details ๐Ÿ”

SYNC_FILE_RANGE_WAIT_BEFORE and SYNC_FILE_RANGE_WAIT_AFTER will detect any I/O errors or ENOSPC conditions and return them to the caller. so they're the snitches of the flag world and honestly? we respect it ๐Ÿซถ

useful combinations of the flag bits are:

  • SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE โ€” makes sure that all pages in the specified range which were dirty when sync_file_range() was called get placed under write-out. this is a start-write-for-data-integrity operation. โœจ
  • SYNC_FILE_RANGE_WRITE โ€” starts write-out of all dirty pages in the specified range that aren't presently under write-out. this is an asynchronous flush-to-disk operation. and it is not suitable for data integrity operations. ๐Ÿšฉ
  • SYNC_FILE_RANGE_WAIT_BEFORE (or SYNC_FILE_RANGE_WAIT_AFTER) โ€” waits for completion of write-out of all pages in the specified range. you can use this after an earlier SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE operation to wait for that op to finish and collect its result. it's giving follow-up text energy ๐Ÿ“ฑ
  • SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER โ€” this is a write-for-data-integrity operation that ensures all pages in the specified range which were dirty when sync_file_range() was called get committed to disk. the full ensemble. the whole girl group is on stage. ๐Ÿ‘‘

๐Ÿ’ซ RETURN VALUE ๐Ÿ’ซ

slay: on success, sync_file_range() returns 0.

on failure, -1 is returned and errno is set to indicate the error. not slay, but informative. ๐Ÿ’€

๐Ÿšจ ERRORS ๐Ÿšจ

  • EBADF โ€” fd is not a valid file descriptor. bestie that's not a file ๐Ÿซ 
  • EINVAL โ€” flags specifies an invalid bit; or offset or nbytes is invalid. read the room (and the arguments) ๐Ÿ˜ค
  • EIO โ€” I/O error. the disk said no ๐Ÿ’”
  • ENOMEM โ€” out of memory. she's full ๐Ÿ˜ญ
  • ENOSPC โ€” out of disk space. she's also full ๐Ÿ˜ญ๐Ÿ˜ญ
  • ESPIPE โ€” fd refers to something other than a regular file, a block device, or a directory. like... what even is that ๐Ÿ“

๐ŸŒŸ VERSIONS ๐ŸŒŸ

sync_file_range2()

some architectures (e.g. PowerPC, ARM) need 64-bit arguments aligned in a suitable pair of registers. on those architectures, the call signature of sync_file_range() shown in the SYNOPSIS would force a register to be wasted as padding between the fd and offset arguments. (peep syscall(2) for the details.) so these architectures define a different system call that orders the arguments suitably:

int sync_file_range2(int fd, unsigned int flags,
                     off_t offset, off_t nbytes);

the behavior of this system call is otherwise exactly the same as sync_file_range(). same girl, different outfit change. ๐Ÿ’…โœจ

๐Ÿ“œ STANDARDS ๐Ÿ“œ

Linux. that's the standard. we're Linux-pilled. ๐Ÿง

๐Ÿ•ฐ๏ธ HISTORY ๐Ÿ•ฐ๏ธ

Linux 2.6.17. she's been around a minute and she's still iconic ๐Ÿ‘‘

sync_file_range2()

a system call with this signature first appeared on the ARM architecture in Linux 2.6.20, under the name arm_sync_file_range(). it got renamed in Linux 2.6.22, when the analogous system call was added for PowerPC. on architectures where glibc support is provided, glibc transparently wraps sync_file_range2() under the name sync_file_range(). sneaky. elegant. we love a glow-up ๐ŸŒˆ

๐Ÿ“ NOTES ๐Ÿ“

_FILE_OFFSET_BITS should be defined to be 64 in code that takes the address of sync_file_range, if that code is meant to be portable to traditional 32-bit x86 and ARM platforms where off_t's width defaults to 32 bits. protect your offsets, babe ๐Ÿซถ

๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

fdatasync(2), fsync(2), msync(2), sync(2) โ€” go read the whole friend group, they're all valid ๐Ÿ’œ


and that's the page, bestie!! ๐ŸŽ€ this was pulled straight from Linux man-pages 6.20, dated 2026-02-08 โ€” so if you're reading this and the date's gone stale, that's a you-and-your-distro problem, not mine ๐Ÿ’–โœจ๐Ÿฆ‹

๐Ÿ“ฆ 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