# Reproducing ENOSPC Error in C Program

This C program reproduces an `ENOSPC` (Error: No Space Left on Device) condition by demonstrating how filesystem fragmentation can cause allocation failures even when sufficient free space exists. The issue occurs when:

*   Filesystem is mounted with `allocsize=1M`
*   Many small files preallocate space across allocation groups
*   A large file is extended, followed by a small extension attempt

The program shows that while the initial large write succeeds, a subsequent small `posix_fallocate()` call fails with ENOSPC on the first attempt but succeeds on retry, proving that free space exists but isn't contiguous.

---

## Prerequisites

*   Filesystem: **XFS** with separate disk for journal
*   Mount option: `allocsize=1M` on the target mount point
*   Sufficient disk space (≥ 5 GB recommended)
*   Single-threaded execution
*   Linux environment with XFS development tools
*   C compiler (gcc/clang)

---

## Key Factors for Reproduction

| Factor                    | Recommended value | Purpose                                                                 |
| :------------------------ | :---------------- | :---------------------------------------------------------------------- |
| `allocsize` mount option  | 1M                | Forces 1MB preallocation units, increasing fragmentation risk           |
| small files (31MB each)   | 128               | Consumes allocation groups, fragmenting free space                      |
| Large initial write       | 748KB             | Creates substantial file growth within fragmented space                 |
| Small fallocate attempt   | 16KB              | Tests allocation of small contiguous space in fragmented environment    |
| Immediate retry           | on failure        | Demonstrates space exists but wasn't contiguous on first attempt        |

---

## Environment Setup

Create an XFS filesystem with separate journal device and mount with `allocsize=1M`:

```bash
# Format disk (replace /dev/sdX with your data disk, /dev/sdY with journal disk)
mkfs.xfs -f -d agcount=128 -l logdev=/dev/sdY,size=64m /dev/sdX

# Mount with allocsize=1M
mkdir /mnt/test
mount -t xfs -o logdev=/dev/sdY,allocsize=1048576 /dev/sdX /mnt/test
```

---

## Preparing C program
```bash
cat > test.c << 'EOF'
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main(int argc, char* argv[]) {
	const char *basedir = "mnt";
	char filename[256];
	char writebuf[1024];
	int inc1 = 748;
	int inc2 = 16;

	sprintf(filename, "%s/%03i.dat", basedir, 1);
	memset(writebuf, 1, 1024);

	printf("Opening file %s\n", filename);
	int fd = open(filename, O_RDWR);
	if (fd == -1) {
		printf("Error on open file %s (code=%i)\n", filename, errno);
		return 1;
	}
	off_t fs = lseek(fd, 0, SEEK_END);
	printf("Current file size: %li\n", fs);
	
	printf("Writing %i bytes at the file end\n", inc1 * 1024);
	for (int i = 0; i < inc1; i++) {
		int write_result = write(fd, writebuf, 1024);
		if (write_result == -1) {
			printf("Error on write (code=%i)\n", errno);
			close(fd);
			return 1;
		}
	}

	/* Test */
	int iteration = 1;
	int test_result = 0;
	do {
		printf("Allocate addtional %i bytes at the file end\n", inc2 * 1024);
		test_result = posix_fallocate(fd, fs + inc1 * 1024,  inc2 * 1024);
		if (test_result != 0) {
			if (test_result == ENOSPC) {
				printf("Error ENOSPC on posix_fallocate!\n");
				if (iteration++ < 2) {
					printf("Retrying operation...\n");
					continue;
				}
			}
			else
				printf("Error on posix_fallocate (code=%i)\n", test_result);
			close(fd);
			return 1;
		}
	} while ( test_result != 0 );
	printf("Done\n");
	close(fd);
	return 0;
}
EOF
echo "Compile test tool"
gcc -o test test.c
```

---


## Preparing data
```bash
# Create 128 files, each preallocating 31MB
for i in {000..127}; do
    fallocate -x -l 31M "/mnt/test/${i}.dat"
done
```

---


## Reproducing by C Program
```bash
test
# output:
#Writing 765952 bytes at the file end
#Allocate additional 16384 bytes at the file end
#Error ENOSPC on posix_fallocate!
#Retrying operation...
#Allocate additional 16384 bytes at the file end
#Done
```

---


## Important Notes
1. ENOSPC is intermittent: The first posix_fallocate() call fails with ENOSPC, but the identical retry succeeds immediately. This proves free space exists but wasn't contiguous on the first attempt.
2. Root cause: Filesystem fragmentation caused by:
* allocsize=1M forcing large preallocation units
* 128 small files consuming allocation groups
* Large file extension (748KB) fragmenting remaining space
3. Not a disk space issue: The retry success demonstrates sufficient free space exists. The failure is due to inability to find contiguous space for the small (16KB) allocation.
