pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS


Linux kernel linux6_17_rc4_rt3_fnctl_allocfree20260630_003 fnctl() F_ALLOCSP/F_FREESP patch
Posted by Anonymous on Tue 30th Jun 2026 12:23
raw | new post
modification of post by Anonymous (view diff)

  1. diff --git a/fs/fcntl.c b/fs/fcntl.c
  2. index 5598e4d57422..43150c2a293e 100644
  3. --- a/fs/fcntl.c
  4. +++ b/fs/fcntl.c
  5. @@ -14,6 +14,7 @@
  6.  #include <linux/file.h>
  7.  #include <linux/capability.h>
  8.  #include <linux/dnotify.h>
  9. +#include <linux/falloc.h>
  10.  #include <linux/slab.h>
  11.  #include <linux/module.h>
  12.  #include <linux/pipe_fs_i.h>
  13. @@ -36,6 +37,217 @@
  14.  
  15.  #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
  16.  
  17. +struct fcntl_space_range {
  18. +       loff_t start;
  19. +       loff_t len;
  20. +       bool to_eof;
  21. +};
  22. +
  23. +static int fcntl_space_resolve_range(struct file *file, int whence,
  24. +       loff_t offset, loff_t flen,
  25. +       struct fcntl_space_range *range)
  26. +{
  27. +       struct inode *inode = file_inode(file);
  28. +       loff_t base, start, len;
  29. +
  30. +       switch (whence) {
  31. +               case SEEK_SET:
  32. +                       base = 0;
  33. +                       break;
  34. +               case SEEK_CUR:
  35. +                       base = file->f_pos;
  36. +                       break;
  37. +               case SEEK_END:
  38. +                       base = i_size_read(inode);
  39. +                       break;
  40. +               default:
  41. +                       return -EINVAL;
  42. +       }
  43. +
  44. +       if (check_add_overflow(base, offset, &start))
  45. +               return -EOVERFLOW;
  46. +
  47. +       /*
  48. +        * Match POSIX byte-range convention:
  49. +        *
  50. +        *   flen > 0:  [start, start + flen)
  51. +        *   flen == 0: [start, EOF)
  52. +        *   flen < 0:  [start + flen, start)
  53. +        */
  54. +       if (flen < 0) {
  55. +               if (check_add_overflow(start, flen, &start))
  56. +                       return -EOVERFLOW;
  57. +               if (flen == LLONG_MIN)
  58. +                       return -EOVERFLOW;
  59. +               len = -flen;
  60. +               range->to_eof = false;
  61. +       } else if (flen == 0) {
  62. +               len = 0;
  63. +               range->to_eof = true;
  64. +       } else {
  65. +               len = flen;
  66. +               range->to_eof = false;
  67. +       }
  68. +
  69. +       if (start < 0)
  70. +               return -EINVAL;
  71. +
  72. +       range->start = start;
  73. +       range->len = len;
  74. +       return 0;
  75. +}
  76. +
  77. +static int fcntl_space_alloc(struct file *file,
  78. +       const struct fcntl_space_range *range)
  79. +{
  80. +       loff_t len = range->len;
  81. +       loff_t isize;
  82. +
  83. +       /*
  84. +        * For |len == 0|, allocate from start to current EOF.
  85. +        * If start is beyond EOF, this is an empty range and therefore a
  86. +        * no-op.
  87. +        */
  88. +       if (range->to_eof) {
  89. +               isize = i_size_read(file_inode(file));
  90. +               if (range->start >= isize)
  91. +                       return 0;
  92. +               len = isize - range->start;
  93. +       }
  94. +
  95. +       if (len == 0)
  96. +               return 0;
  97. +
  98. +       return vfs_fallocate(file, 0, range->start, len);
  99. +}
  100. +
  101. +static int fcntl_space_free(struct file *file,
  102. +       const struct fcntl_space_range *range)
  103. +{
  104. +       struct inode *inode = file_inode(file);
  105. +       loff_t isize = i_size_read(inode);
  106. +       loff_t end;
  107. +
  108. +       if (range->start >= isize)
  109. +               return 0;
  110. +
  111. +       if (range->to_eof)
  112. +               return vfs_truncate(&file->f_path, range->start);
  113. +
  114. +       if (check_add_overflow(range->start, range->len, &end))
  115. +               return -EOVERFLOW;
  116. +
  117. +       /*
  118. +        * Freeing through EOF must shrink the file. Hole punching keeps
  119. +        * |i_size| unchanged, which is not the desired |F_FREESP| tail
  120. +        * behavior.
  121. +        */
  122. +       if (end >= isize)
  123. +               return vfs_truncate(&file->f_path, range->start);
  124. +
  125. +       if (range->len == 0)
  126. +               return 0;
  127. +
  128. +       return vfs_fallocate(file,
  129. +               FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
  130. +               range->start, range->len);
  131. +}
  132. +
  133. +static int fcntl_space_common(struct file *file, unsigned int cmd,
  134. +       int whence, loff_t start, loff_t len)
  135. +{
  136. +       struct fcntl_space_range range;
  137. +       int error;
  138. +
  139. +       error = fcntl_space_resolve_range(file, whence, start, len, &range);
  140. +       if (error)
  141. +               return error;
  142. +
  143. +       switch (cmd) {
  144. +               case F_ALLOCSP:
  145. +               case F_ALLOCSP64:
  146. +                       return fcntl_space_alloc(file, &range);
  147. +               case F_FREESP:
  148. +               case F_FREESP64:
  149. +                       return fcntl_space_free(file, &range);
  150. +               default:
  151. +                       return -EINVAL;
  152. +       }
  153. +}
  154. +
  155. +static int fcntl_space_native(struct file *file, unsigned int cmd,
  156. +       unsigned long arg)
  157. +{
  158. +       struct flock fl;
  159. +
  160. +       if (copy_from_user(&fl, (void __user *)arg, sizeof(fl)))
  161. +               return -EFAULT;
  162. +
  163. +       return fcntl_space_common(file, cmd, fl.l_whence,
  164. +               fl.l_start, fl.l_len);
  165. +}
  166. +
  167. +static int fcntl_space_native64(struct file *file, unsigned int cmd,
  168. +       unsigned long arg)
  169. +{
  170. +       struct flock64 fl;
  171. +
  172. +       if (copy_from_user(&fl, (void __user *)arg, sizeof(fl)))
  173. +               return -EFAULT;
  174. +
  175. +       return fcntl_space_common(file, cmd, fl.l_whence,
  176. +               fl.l_start, fl.l_len);
  177. +}
  178. +
  179. +static int fcntl_space(struct file *file, unsigned int cmd, unsigned long arg)
  180. +{
  181. +       if (!(file->f_mode & FMODE_WRITE))
  182. +               return -EBADF;
  183. +
  184. +       switch (cmd) {
  185. +               case F_ALLOCSP:
  186. +               case F_FREESP:
  187. +                       return fcntl_space_native(file, cmd, arg);
  188. +               case F_ALLOCSP64:
  189. +               case F_FREESP64:
  190. +                       return fcntl_space_native64(file, cmd, arg);
  191. +               default:
  192. +                       return -EINVAL;
  193. +       }
  194. +}
  195. +
  196. +#ifdef CONFIG_COMPAT
  197. +static int fcntl_space_compat(struct file *file, unsigned int cmd,
  198. +       unsigned long arg)
  199. +{
  200. +       struct compat_flock fl;
  201. +
  202. +       if (!(file->f_mode & FMODE_WRITE))
  203. +               return -EBADF;
  204. +
  205. +       if (copy_from_user(&fl, compat_ptr(arg), sizeof(fl)))
  206. +               return -EFAULT;
  207. +
  208. +       return fcntl_space_common(file, cmd, fl.l_whence,
  209. +               fl.l_start, fl.l_len);
  210. +}
  211. +
  212. +static int fcntl_space_compat64(struct file *file, unsigned int cmd,
  213. +       unsigned long arg)
  214. +{
  215. +       struct compat_flock64 fl;
  216. +
  217. +       if (!(file->f_mode & FMODE_WRITE))
  218. +               return -EBADF;
  219. +
  220. +       if (copy_from_user(&fl, compat_ptr(arg), sizeof(fl)))
  221. +               return -EFAULT;
  222. +
  223. +       return fcntl_space_common(file, cmd, fl.l_whence,
  224. +               fl.l_start, fl.l_len);
  225. +}
  226. +#endif /* CONFIG_COMPAT */
  227. +
  228.  static int setfl(int fd, struct file * filp, unsigned int arg)
  229.  {
  230.         struct inode * inode = file_inode(filp);
  231. @@ -552,6 +764,12 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
  232.         case F_SET_RW_HINT:
  233.                 err = fcntl_set_rw_hint(filp, cmd, arg);
  234.                 break;
  235. +       case F_ALLOCSP:
  236. +       case F_FREESP:
  237. +       case F_ALLOCSP64:
  238. +       case F_FREESP64:
  239. +               err = fcntl_space(filp, cmd, arg);
  240. +               break;
  241.         default:
  242.                 break;
  243.         }
  244. @@ -785,6 +1003,14 @@ static long do_compat_fcntl64(unsigned int fd, unsigned int cmd,
  245.                         break;
  246.                 err = fcntl_setlk(fd, fd_file(f), convert_fcntl_cmd(cmd), &flock);
  247.                 break;
  248. +       case F_ALLOCSP:
  249. +       case F_FREESP:
  250. +               err = fcntl_space_compat(f.file, cmd, arg);
  251. +               break;
  252. +       case F_ALLOCSP64:
  253. +       case F_FREESP64:
  254. +               err = fcntl_space_compat64(f.file, cmd, arg);
  255. +               break;
  256.         default:
  257.                 err = do_fcntl(fd, cmd, arg, fd_file(f));
  258.                 break;
  259. diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
  260. index f291ab4f94eb..ece9cf1e4594 100644
  261. --- a/include/uapi/linux/fcntl.h
  262. +++ b/include/uapi/linux/fcntl.h
  263. @@ -79,6 +79,15 @@
  264.   */
  265.  #define RWF_WRITE_LIFE_NOT_SET RWH_WRITE_LIFE_NOT_SET
  266.  
  267. +/*
  268. + * { Solaris, FreeBSD, SUPER/UX, ... }-style file space allocation/freeing.
  269. + * These commands take |struct flock| or |struct flock64|.
  270. + */
  271. +#define F_ALLOCSP              (F_LINUX_SPECIFIC_BASE + 15)
  272. +#define F_FREESP               (F_LINUX_SPECIFIC_BASE + 16)
  273. +#define F_ALLOCSP64            (F_LINUX_SPECIFIC_BASE + 17)
  274. +#define F_FREESP64             (F_LINUX_SPECIFIC_BASE + 18)
  275. +
  276.  /*
  277.   * Types of directory notifications that may be requested.
  278.   */
  279. diff --git a/tools/testing/selftests/fcntl/Makefile b/tools/testing/selftests/fcntl/Makefile
  280. new file mode 100644
  281. index 000000000000..1bbaff237104
  282. --- /dev/null
  283. +++ b/tools/testing/selftests/fcntl/Makefile
  284. @@ -0,0 +1,7 @@
  285. +# SPDX-License-Identifier: GPL-2.0
  286. +
  287. +TEST_GEN_PROGS := fallocsp
  288. +
  289. +include ../lib.mk
  290. +
  291. +CFLAGS += -Wall -O2
  292. diff --git a/tools/testing/selftests/fcntl/fallocsp b/tools/testing/selftests/fcntl/fallocsp
  293. new file mode 100755
  294. index 000000000000..922c3d8f227a
  295. Binary files /dev/null and b/tools/testing/selftests/fcntl/fallocsp differ
  296. diff --git a/tools/testing/selftests/fcntl/fallocsp.c b/tools/testing/selftests/fcntl/fallocsp.c
  297. new file mode 100644
  298. index 000000000000..9e741955f239
  299. --- /dev/null
  300. +++ b/tools/testing/selftests/fcntl/fallocsp.c
  301. @@ -0,0 +1,239 @@
  302. +// SPDX-License-Identifier: GPL-2.0
  303. +
  304. +/*
  305. + * Test for { Solaris, FreeBSD, SUPER/UX, ... }-style |F_ALLOCSP|/|F_FREESP|
  306. + * Written by Roland Mainz <roland.mainz@nrubsig.org>
  307. + */
  308. +
  309. +#define _GNU_SOURCE
  310. +#define _FILE_OFFSET_BITS 64
  311. +
  312. +#include <errno.h>
  313. +#include <fcntl.h>
  314. +#include <stdio.h>
  315. +#include <stdlib.h>
  316. +#include <string.h>
  317. +#include <sys/stat.h>
  318. +#include <sys/types.h>
  319. +#include <unistd.h>
  320. +
  321. +#include "../kselftest.h"
  322. +
  323. +#ifndef F_LINUX_SPECIFIC_BASE
  324. +#define F_LINUX_SPECIFIC_BASE  1024
  325. +#endif
  326. +#ifndef F_ALLOCSP
  327. +#define F_ALLOCSP              (F_LINUX_SPECIFIC_BASE + 15)
  328. +#endif
  329. +#ifndef F_FREESP
  330. +#define F_FREESP               (F_LINUX_SPECIFIC_BASE + 16)
  331. +#endif
  332. +#ifndef F_ALLOCSP64
  333. +#define F_ALLOCSP64            (F_LINUX_SPECIFIC_BASE + 17)
  334. +#endif
  335. +#ifndef F_FREESP64
  336. +#define F_FREESP64             (F_LINUX_SPECIFIC_BASE + 18)
  337. +#endif
  338. +
  339. +static int write_pattern(int fd, off_t len)
  340. +{
  341. +       char buf[4096];
  342. +       off_t done = 0;
  343. +
  344. +       (void)memset(buf, 0x5a, sizeof(buf));
  345. +
  346. +       while (done < len) {
  347. +               size_t todo = sizeof(buf);
  348. +               ssize_t ret;
  349. +
  350. +               if (len - done < (off_t)todo)
  351. +                       todo = len - done;
  352. +
  353. +               ret = write(fd, buf, todo);
  354. +               if (ret < 0)
  355. +                       return -1;
  356. +               if (ret == 0) {
  357. +                       errno = EIO;
  358. +                       return -1;
  359. +               }
  360. +
  361. +               done += ret;
  362. +       }
  363. +
  364. +       return 0;
  365. +}
  366. +
  367. +static int is_zero_range(int fd, off_t off, size_t len)
  368. +{
  369. +       unsigned char buf[4096];
  370. +       size_t done = 0;
  371. +
  372. +       if (lseek(fd, off, SEEK_SET) < 0)
  373. +               return -1;
  374. +
  375. +       while (done < len) {
  376. +               size_t todo = sizeof(buf);
  377. +               ssize_t ret;
  378. +               size_t i;
  379. +
  380. +               if (len - done < todo)
  381. +                       todo = len - done;
  382. +
  383. +               ret = read(fd, buf, todo);
  384. +               if (ret < 0)
  385. +                       return -1;
  386. +               if (ret == 0)
  387. +                       return 0;
  388. +
  389. +               for (i = 0; i < (size_t)ret; i++) {
  390. +                       if (buf[i] != 0)
  391. +                               return 0;
  392. +               }
  393. +
  394. +               done += ret;
  395. +       }
  396. +
  397. +       return 1;
  398. +}
  399. +
  400. +static int test_allocsp_extends(int fd)
  401. +{
  402. +       struct flock fl = {
  403. +               .l_whence = SEEK_SET,
  404. +               .l_start = 0,
  405. +               .l_len = 1024 * 1024,
  406. +       };
  407. +       struct stat st;
  408. +
  409. +       if (fcntl(fd, F_ALLOCSP, &fl) < 0) {
  410. +               if (errno == EINVAL) {
  411. +                       ksft_test_result_skip("F_ALLOCSP unsupported\n");
  412. +                       return 0;
  413. +               }
  414. +               perror("F_ALLOCSP");
  415. +               return -1;
  416. +       }
  417. +
  418. +       if (fstat(fd, &st) < 0) {
  419. +               perror("fstat");
  420. +               return -1;
  421. +       }
  422. +
  423. +       if (st.st_size != fl.l_len) {
  424. +               (void)fprintf(stderr, "F_ALLOCSP size %lld != %lld\n",
  425. +                       (long long)st.st_size, (long long)fl.l_len);
  426. +               return -1;
  427. +       }
  428. +
  429. +       ksft_test_result_pass("F_ALLOCSP extends file\n");
  430. +       return 0;
  431. +}
  432. +
  433. +static int test_freesp_truncates(int fd)
  434. +{
  435. +       struct flock fl = {
  436. +               .l_whence = SEEK_SET,
  437. +               .l_start = 4096,
  438. +               .l_len = 0,
  439. +       };
  440. +       struct stat st;
  441. +
  442. +       if (ftruncate(fd, 0) < 0)
  443. +               return -1;
  444. +       if (write_pattern(fd, 16384) < 0)
  445. +               return -1;
  446. +
  447. +       if (fcntl(fd, F_FREESP, &fl) < 0) {
  448. +               if (errno == EINVAL) {
  449. +                       ksft_test_result_skip("F_FREESP unsupported\n");
  450. +                       return 0;
  451. +               }
  452. +               perror("F_FREESP truncate");
  453. +               return -1;
  454. +       }
  455. +
  456. +       if (fstat(fd, &st) < 0)
  457. +               return -1;
  458. +
  459. +       if (st.st_size != 4096) {
  460. +               (void)fprintf(stderr, "F_FREESP size %lld != 4096\n",
  461. +                       (long long)st.st_size);
  462. +               return -1;
  463. +       }
  464. +
  465. +       ksft_test_result_pass("F_FREESP truncates tail\n");
  466. +       return 0;
  467. +}
  468. +
  469. +static int test_freesp_punches_hole(int fd)
  470. +{
  471. +       struct flock fl = {
  472. +               .l_whence = SEEK_SET,
  473. +               .l_start = 4096,
  474. +               .l_len = 4096,
  475. +       };
  476. +       struct stat st;
  477. +       int zero;
  478. +
  479. +       if (ftruncate(fd, 0) < 0)
  480. +               return -1;
  481. +       if (write_pattern(fd, 16384) < 0)
  482. +               return -1;
  483. +
  484. +       if (fcntl(fd, F_FREESP, &fl) < 0) {
  485. +               if (errno == EINVAL || errno == EOPNOTSUPP) {
  486. +                       ksft_test_result_skip("F_FREESP hole punch unsupported\n");
  487. +                       return 0;
  488. +               }
  489. +               perror("F_FREESP punch");
  490. +               return -1;
  491. +       }
  492. +
  493. +       if (fstat(fd, &st) < 0)
  494. +               return -1;
  495. +
  496. +       if (st.st_size != 16384) {
  497. +               (void)fprintf(stderr, "hole punch changed size to %lld\n",
  498. +                       (long long)st.st_size);
  499. +               return -1;
  500. +       }
  501. +
  502. +       zero = is_zero_range(fd, 4096, 4096);
  503. +       if (zero < 0)
  504. +               return -1;
  505. +       if (!zero) {
  506. +               (void)fprintf(stderr, "punched range did not read as zeroes\n");
  507. +               return -1;
  508. +       }
  509. +
  510. +       ksft_test_result_pass("F_FREESP punches hole\n");
  511. +       return 0;
  512. +}
  513. +
  514. +int main(void)
  515. +{
  516. +       char tmpl[] = "/tmp/fallocsp-selftest-XXXXXX";
  517. +       int fd;
  518. +       int ret = 0;
  519. +
  520. +       ksft_print_header();
  521. +       ksft_set_plan(3);
  522. +
  523. +       fd = mkstemp(tmpl);
  524. +       if (fd < 0) {
  525. +               perror("mkstemp");
  526. +               return KSFT_FAIL;
  527. +       }
  528. +
  529. +       (void)unlink(tmpl);
  530. +
  531. +       if (test_allocsp_extends(fd) < 0)
  532. +               ret = KSFT_FAIL;
  533. +       if (test_freesp_truncates(fd) < 0)
  534. +               ret = KSFT_FAIL;
  535. +       if (test_freesp_punches_hole(fd) < 0)
  536. +               ret = KSFT_FAIL;
  537. +
  538. +       (void)close(fd);
  539. +       return ret;
  540. +}

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at