1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) Zilogic Systems Pvt. Ltd., 2018
4 * Email : code@zilogic.com
5 */
6
7 /*\
8 * [Description]
9 *
10 * This code tests the following flags:
11 *
12 * - AT_STATX_FORCE_SYNC
13 * - AT_STATX_DONT_SYNC
14 *
15 * By exportfs cmd creating NFS setup.
16 *
17 * A test file is created in server folder and statx is being
18 * done in client folder.
19 *
20 * BY AT_STATX_SYNC_AS_STAT getting predefined mode value.
21 * Then, by using AT_STATX_FORCE_SYNC getting new updated vaue
22 * from server file changes.
23 *
24 * BY AT_STATX_SYNC_AS_STAT getting predefined mode value.
25 * AT_STATX_FORCE_SYNC is called to create cache data of the file.
26 * Then, by using DONT_SYNC_FILE getting old cached data in client folder,
27 * but mode has been chaged in server file.
28 *
29 * The support for SYNC flags was implemented in NFS in:
30 *
31 * commit 9ccee940bd5b766b6dab6c1a80908b9490a4850d
32 * Author: Trond Myklebust <trond.myklebust@primarydata.com>
33 * Date: Thu Jan 4 17:46:09 2018 -0500
34 *
35 * Support statx() mask and query flags parameters
36 */
37
38 #define _GNU_SOURCE
39 #include <netdb.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <errno.h>
43 #include <linux/limits.h>
44 #include <sys/mount.h>
45 #include "tst_test.h"
46 #include "lapi/stat.h"
47
48 #define MODE(X) (X & (~S_IFMT))
49 #define FLAG_NAME(x) .flag = x, .flag_name = #x
50 #define BUFF_SIZE PATH_MAX
51 #define DEFAULT_MODE 0644
52 #define CURRENT_MODE 0777
53
54 #define CLI_PATH "client"
55 #define SERV_PATH "server"
56 #define CLI_FORCE_SYNC "client/force_sync_file"
57 #define CLI_DONT_SYNC "client/dont_sync_file"
58 #define SERV_FORCE_SYNC "server/force_sync_file"
59 #define SERV_DONT_SYNC "server/dont_sync_file"
60
61 static char *cwd;
62 static char cmd[BUFF_SIZE];
63 static int mounted;
64 static int exported;
65
get_mode(char * file_name,int flag_type,char * flag_name)66 static int get_mode(char *file_name, int flag_type, char *flag_name)
67 {
68 struct statx buf;
69
70 TEST(statx(AT_FDCWD, file_name, flag_type, STATX_ALL, &buf));
71
72 if (TST_RET == -1) {
73 tst_brk(TFAIL | TST_ERR,
74 "statx(AT_FDCWD, %s, %s, STATX_ALL, &buf)",
75 file_name, flag_name);
76 }
77
78 tst_res(TINFO, "statx(AT_FDCWD, %s, %s, STATX_ALL, &buf) = %o",
79 file_name, flag_name, buf.stx_mode);
80
81 return buf.stx_mode;
82 }
83
84 static const struct test_cases {
85 int flag;
86 char *flag_name;
87 char *server_file;
88 char *client_file;
89 unsigned int mode;
90 } tcases[] = {
91 {FLAG_NAME(AT_STATX_DONT_SYNC), SERV_DONT_SYNC, CLI_DONT_SYNC, DEFAULT_MODE},
92 {FLAG_NAME(AT_STATX_FORCE_SYNC), SERV_FORCE_SYNC, CLI_FORCE_SYNC, CURRENT_MODE}
93 };
94
test_statx(unsigned int i)95 static void test_statx(unsigned int i)
96 {
97 const struct test_cases *tc = &tcases[i];
98 unsigned int cur_mode;
99
100 get_mode(tc->client_file, AT_STATX_FORCE_SYNC, "AT_STATX_FORCE_SYNC");
101
102 SAFE_CHMOD(tc->server_file, CURRENT_MODE);
103 cur_mode = get_mode(tc->client_file, tc->flag, tc->flag_name);
104
105 if (MODE(cur_mode) == tc->mode) {
106 tst_res(TPASS,
107 "statx() with %s for mode %o",
108 tc->flag_name, tc->mode);
109 } else {
110 tst_res(TFAIL,
111 "statx() with %s for mode %o %o",
112 tc->flag_name, tc->mode, MODE(cur_mode));
113 }
114
115 SAFE_CHMOD(tc->server_file, DEFAULT_MODE);
116 }
117
setup(void)118 static void setup(void)
119 {
120 int ret;
121 char server_path[BUFF_SIZE];
122
123 cwd = tst_get_tmpdir();
124
125 SAFE_MKDIR(SERV_PATH, DEFAULT_MODE);
126 SAFE_MKDIR(CLI_PATH, DEFAULT_MODE);
127 SAFE_CREAT(SERV_FORCE_SYNC, DEFAULT_MODE);
128 SAFE_CREAT(SERV_DONT_SYNC, DEFAULT_MODE);
129
130 snprintf(server_path, sizeof(server_path), ":%s/%s", cwd, SERV_PATH);
131
132 snprintf(cmd, sizeof(cmd),
133 "exportfs -i -o no_root_squash,rw,sync,no_subtree_check,fsid=%d *%.1024s",
134 getpid(), server_path);
135 exported = 1;
136
137 ret = tst_system(cmd);
138 if (ret)
139 tst_brk(TBROK | TST_ERR, "failed to exportfs");
140
141 if (mount(server_path, CLI_PATH, "nfs", 0, "addr=127.0.0.1")) {
142 if (errno == EOPNOTSUPP || errno == ECONNREFUSED
143 || errno == ETIMEDOUT)
144 tst_brk(TCONF | TERRNO, "nfs server not set up?");
145 tst_brk(TBROK | TERRNO, "mount() nfs failed");
146 }
147 mounted = 1;
148 }
149
cleanup(void)150 static void cleanup(void)
151 {
152 if (!exported)
153 return;
154 snprintf(cmd, sizeof(cmd),
155 "exportfs -u *:%s/%s", cwd, SERV_PATH);
156
157 if (tst_system(cmd) == -1)
158 tst_res(TWARN | TST_ERR, "failed to clear exportfs");
159
160 if (mounted)
161 SAFE_UMOUNT(CLI_PATH);
162 }
163
164 static struct tst_test test = {
165 .tcnt = ARRAY_SIZE(tcases),
166 .test = test_statx,
167 .setup = setup,
168 .cleanup = cleanup,
169 .min_kver = "4.16",
170 .needs_tmpdir = 1,
171 .dev_fs_type = "nfs",
172 .needs_root = 1,
173 .needs_cmds = (const char *[]) {
174 "exportfs",
175 NULL
176 }
177 };
178