]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pnfsdscopymr/pnfsdscopymr.c
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / usr.sbin / pnfsdscopymr / pnfsdscopymr.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2017 Rick Macklem
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <err.h>
33 #include <errno.h>
34 #include <getopt.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <netdb.h>
40 #include <sys/param.h>
41 #include <sys/extattr.h>
42 #include <sys/mount.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <sys/sysctl.h>
47 #include <arpa/inet.h>
48 #include <netinet/in.h>
49 #include <nfs/nfssvc.h>
50
51 #include <fs/nfs/nfsproto.h>
52 #include <fs/nfs/nfskpiport.h>
53 #include <fs/nfs/nfs.h>
54 #include <fs/nfs/nfsrvstate.h>
55
56 static void usage(void);
57
58 static struct option longopts[] = {
59         { "migrate",    required_argument,      NULL,   'm'     },
60         { "mirror",     required_argument,      NULL,   'r'     },
61         { NULL,         0,                      NULL,   0       }
62 };
63
64 /*
65  * This program creates a copy of the file's (first argument) data on the
66  * new/recovering DS mirror.  If the file is already on the new/recovering
67  * DS, it will simply exit(0).
68  */
69 int
70 main(int argc, char *argv[])
71 {
72         struct nfsd_pnfsd_args pnfsdarg;
73         struct pnfsdsfile dsfile[NFSDEV_MAXMIRRORS];
74         struct stat sb;
75         struct statfs sf;
76         struct addrinfo hints, *res, *nres;
77         struct sockaddr_in sin;
78         struct sockaddr_in6 sin6;
79         ssize_t xattrsize, xattrsize2;
80         size_t mirlen;
81         int ch, fnd, fndzero, i, migrateit, mirrorcnt, mirrorit, ret;
82         int mirrorlevel;
83         char host[MNAMELEN + NI_MAXHOST + 2], *cp;
84
85         if (geteuid() != 0)
86                 errx(1, "Must be run as root/su");
87
88         mirrorit = migrateit = 0;
89         pnfsdarg.dspath = pnfsdarg.curdspath = NULL;
90         while ((ch = getopt_long(argc, argv, "m:r:", longopts, NULL)) != -1) {
91                 switch (ch) {
92                 case 'm':
93                         /* Migrate the file from the second DS to the first. */
94                         if (mirrorit != 0)
95                                 errx(1, "-r and -m are mutually exclusive");
96                         migrateit = 1;
97                         pnfsdarg.curdspath = optarg;
98                         break;
99                 case 'r':
100                         /* Mirror the file on the specified DS. */
101                         if (migrateit != 0)
102                                 errx(1, "-r and -m are mutually exclusive");
103                         mirrorit = 1;
104                         pnfsdarg.dspath = optarg;
105                         break;
106                 default:
107                         usage();
108                 }
109         }
110         argc -= optind;
111         argv += optind;
112         if (migrateit != 0) {
113                 if (argc != 2)
114                         usage();
115                 pnfsdarg.dspath = *argv++;
116         } else if (argc != 1)
117                 usage();
118
119         /* Get the pNFS service's mirror level. */
120         mirlen = sizeof(mirrorlevel);
121         ret = sysctlbyname("vfs.nfs.pnfsmirror", &mirrorlevel, &mirlen,
122             NULL, 0);
123         if (ret < 0)
124                 errx(1, "Can't get vfs.nfs.pnfsmirror");
125
126         if (pnfsdarg.dspath != NULL && pnfsdarg.curdspath != NULL &&
127             strcmp(pnfsdarg.dspath, pnfsdarg.curdspath) == 0)
128                 errx(1, "Can't migrate to same server");
129
130         /*
131          * The host address and directory where the data storage file is
132          * located is in the extended attribute "pnfsd.dsfile".
133          */
134         xattrsize = extattr_get_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
135             "pnfsd.dsfile", dsfile, sizeof(dsfile));
136         mirrorcnt = xattrsize / sizeof(struct pnfsdsfile);
137         xattrsize2 = mirrorcnt * sizeof(struct pnfsdsfile);
138         if (mirrorcnt < 1 || xattrsize != xattrsize2)
139                 errx(1, "Can't get extattr pnfsd.dsfile for %s", *argv);
140
141         /* See if there is a 0.0.0.0 entry. */
142         fndzero = 0;
143         for (i = 0; i < mirrorcnt; i++) {
144                 if (dsfile[i].dsf_sin.sin_family == AF_INET &&
145                     dsfile[i].dsf_sin.sin_addr.s_addr == 0)
146                         fndzero = 1;
147         }
148
149         /* If already mirrored for default case, just exit(0); */
150         if (mirrorit == 0 && migrateit == 0 && (mirrorlevel < 2 ||
151             (fndzero == 0 && mirrorcnt >= mirrorlevel) ||
152             (fndzero != 0 && mirrorcnt > mirrorlevel)))
153                 exit(0);
154
155         /* For the "-r" case, there must be a 0.0.0.0 entry. */
156         if (mirrorit != 0 && (fndzero == 0 || mirrorlevel < 2 ||
157             mirrorcnt < 2 || mirrorcnt > mirrorlevel))
158                 exit(0);
159
160         /* For pnfsdarg.dspath set, if it is already in list, just exit(0); */
161         if (pnfsdarg.dspath != NULL) {
162                 /* Check the dspath to see that it's an NFS mount. */
163                 if (stat(pnfsdarg.dspath, &sb) < 0)
164                         errx(1, "Can't stat %s", pnfsdarg.dspath);
165                 if (!S_ISDIR(sb.st_mode))
166                         errx(1, "%s is not a directory", pnfsdarg.dspath);
167                 if (statfs(pnfsdarg.dspath, &sf) < 0)
168                         errx(1, "Can't fsstat %s", pnfsdarg.dspath);
169                 if (strcmp(sf.f_fstypename, "nfs") != 0)
170                         errx(1, "%s is not an NFS mount", pnfsdarg.dspath);
171                 if (strcmp(sf.f_mntonname, pnfsdarg.dspath) != 0)
172                         errx(1, "%s is not the mounted-on dir for the new DS",
173                             pnfsdarg.dspath);
174         
175                 /*
176                  * Check the IP address of the NFS server against the entrie(s)
177                  * in the extended attribute.
178                  */
179                 strlcpy(host, sf.f_mntfromname, sizeof(host));
180                 cp = strchr(host, ':');
181                 if (cp == NULL)
182                         errx(1, "No <host>: in mount %s", host);
183                 *cp = '\0';
184                 memset(&hints, 0, sizeof(hints));
185                 hints.ai_family = PF_UNSPEC;
186                 hints.ai_socktype = SOCK_STREAM;
187                 if (getaddrinfo(host, NULL, &hints, &res) != 0)
188                         errx(1, "Can't get address for %s", host);
189                 for (i = 0; i < mirrorcnt; i++) {
190                         nres = res;
191                         while (nres != NULL) {
192                                 if (dsfile[i].dsf_sin.sin_family ==
193                                     nres->ai_family) {
194                                         /*
195                                          * If there is already an entry for this
196                                          * DS, just exit(0), since copying isn't
197                                          * required.
198                                          */
199                                         if (nres->ai_family == AF_INET &&
200                                             nres->ai_addrlen >= sizeof(sin)) {
201                                                 memcpy(&sin, nres->ai_addr,
202                                                     sizeof(sin));
203                                                 if (sin.sin_addr.s_addr ==
204                                                     dsfile[i].dsf_sin.sin_addr.s_addr)
205                                                         exit(0);
206                                         } else if (nres->ai_family ==
207                                             AF_INET6 && nres->ai_addrlen >=
208                                             sizeof(sin6)) {
209                                                 memcpy(&sin6, nres->ai_addr,
210                                                     sizeof(sin6));
211                                                 if (IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr,
212                                                     &dsfile[i].dsf_sin6.sin6_addr))
213                                                         exit(0);
214                                         }
215                                 }
216                                 nres = nres->ai_next;
217                         }
218                 }
219                 freeaddrinfo(res);
220         }
221
222         /* For "-m", the pnfsdarg.curdspath must be in the list. */
223         if (pnfsdarg.curdspath != NULL) {
224                 /* Check pnfsdarg.curdspath to see that it's an NFS mount. */
225                 if (stat(pnfsdarg.curdspath, &sb) < 0)
226                         errx(1, "Can't stat %s", pnfsdarg.curdspath);
227                 if (!S_ISDIR(sb.st_mode))
228                         errx(1, "%s is not a directory", pnfsdarg.curdspath);
229                 if (statfs(pnfsdarg.curdspath, &sf) < 0)
230                         errx(1, "Can't fsstat %s", pnfsdarg.curdspath);
231                 if (strcmp(sf.f_fstypename, "nfs") != 0)
232                         errx(1, "%s is not an NFS mount", pnfsdarg.curdspath);
233                 if (strcmp(sf.f_mntonname, pnfsdarg.curdspath) != 0)
234                         errx(1, "%s is not the mounted-on dir of the cur DS",
235                             pnfsdarg.curdspath);
236         
237                 /*
238                  * Check the IP address of the NFS server against the entrie(s)
239                  * in the extended attribute.
240                  */
241                 strlcpy(host, sf.f_mntfromname, sizeof(host));
242                 cp = strchr(host, ':');
243                 if (cp == NULL)
244                         errx(1, "No <host>: in mount %s", host);
245                 *cp = '\0';
246                 memset(&hints, 0, sizeof(hints));
247                 hints.ai_family = PF_UNSPEC;
248                 hints.ai_socktype = SOCK_STREAM;
249                 if (getaddrinfo(host, NULL, &hints, &res) != 0)
250                         errx(1, "Can't get address for %s", host);
251                 fnd = 0;
252                 for (i = 0; i < mirrorcnt && fnd == 0; i++) {
253                         nres = res;
254                         while (nres != NULL) {
255                                 if (dsfile[i].dsf_sin.sin_family ==
256                                     nres->ai_family) {
257                                         /*
258                                          * Note if the entry is found.
259                                          */
260                                         if (nres->ai_family == AF_INET &&
261                                             nres->ai_addrlen >= sizeof(sin)) {
262                                                 memcpy(&sin, nres->ai_addr,
263                                                     sizeof(sin));
264                                                 if (sin.sin_addr.s_addr ==
265                                                     dsfile[i].dsf_sin.sin_addr.s_addr) {
266                                                         fnd = 1;
267                                                         break;
268                                                 }
269                                         } else if (nres->ai_family ==
270                                             AF_INET6 && nres->ai_addrlen >=
271                                             sizeof(sin6)) {
272                                                 memcpy(&sin6, nres->ai_addr,
273                                                     sizeof(sin6));
274                                                 if (IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr,
275                                                     &dsfile[i].dsf_sin6.sin6_addr)) {
276                                                         fnd = 1;
277                                                         break;
278                                                 }
279                                         }
280                                 }
281                                 nres = nres->ai_next;
282                         }
283                 }
284                 freeaddrinfo(res);
285                 /*
286                  * If not found just exit(0), since it is not on the
287                  * source DS.
288                  */
289                 if (fnd == 0)
290                         exit(0);
291         }
292
293         /* Do the copy via the nfssvc() syscall. */
294         pnfsdarg.op = PNFSDOP_COPYMR;
295         pnfsdarg.mdspath = *argv;
296         ret = nfssvc(NFSSVC_PNFSDS, &pnfsdarg);
297         if (ret < 0 && errno != EEXIST)
298                 err(1, "Copymr failed for file %s", *argv);
299         exit(0);
300 }
301
302 static void
303 usage(void)
304 {
305
306         fprintf(stderr, "pnfsdscopymr [-r recovered-DS-mounted-on-path] "
307             "[-m soure-DS-mounted-on-path destination-DS-mounted-on-path] "
308             "mds-filename");
309         exit(1);
310 }
311