kernel/drm: Reapply 6d6a80766ccb6c27388c1d0d0db443b0ecd0f2f2
[dragonfly.git] / sys / vfs / hammer2 / hammer2_ioctl.c
1 /*
2  * Copyright (c) 2011-2013 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 /*
36  * Ioctl Functions.
37  *
38  * WARNING! The ioctl functions which manipulate the connection state need
39  *          to be able to run without deadlock on the volume's chain lock.
40  *          Most of these functions use a separate lock.
41  */
42
43 #include "hammer2.h"
44
45 static int hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data);
46 static int hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data);
47 static int hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data);
48 static int hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data);
49 static int hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data);
50 static int hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data);
51 static int hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data);
52 static int hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data);
53 static int hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data);
54 static int hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data);
55 static int hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data);
56 static int hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data);
57 static int hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data);
58 static int hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data);
59 static int hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data);
60 //static int hammer2_ioctl_inode_comp_set(hammer2_inode_t *ip, void *data);
61 //static int hammer2_ioctl_inode_comp_rec_set(hammer2_inode_t *ip, void *data);
62 //static int hammer2_ioctl_inode_comp_rec_set2(hammer2_inode_t *ip, void *data);
63
64 int
65 hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data, int fflag,
66               struct ucred *cred)
67 {
68         int error;
69
70         /*
71          * Standard root cred checks, will be selectively ignored below
72          * for ioctls that do not require root creds.
73          */
74         error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0);
75
76         switch(com) {
77         case HAMMER2IOC_VERSION_GET:
78                 error = hammer2_ioctl_version_get(ip, data);
79                 break;
80         case HAMMER2IOC_RECLUSTER:
81                 if (error == 0)
82                         error = hammer2_ioctl_recluster(ip, data);
83                 break;
84         case HAMMER2IOC_REMOTE_SCAN:
85                 if (error == 0)
86                         error = hammer2_ioctl_remote_scan(ip, data);
87                 break;
88         case HAMMER2IOC_REMOTE_ADD:
89                 if (error == 0)
90                         error = hammer2_ioctl_remote_add(ip, data);
91                 break;
92         case HAMMER2IOC_REMOTE_DEL:
93                 if (error == 0)
94                         error = hammer2_ioctl_remote_del(ip, data);
95                 break;
96         case HAMMER2IOC_REMOTE_REP:
97                 if (error == 0)
98                         error = hammer2_ioctl_remote_rep(ip, data);
99                 break;
100         case HAMMER2IOC_SOCKET_GET:
101                 if (error == 0)
102                         error = hammer2_ioctl_socket_get(ip, data);
103                 break;
104         case HAMMER2IOC_SOCKET_SET:
105                 if (error == 0)
106                         error = hammer2_ioctl_socket_set(ip, data);
107                 break;
108         case HAMMER2IOC_PFS_GET:
109                 if (error == 0)
110                         error = hammer2_ioctl_pfs_get(ip, data);
111                 break;
112         case HAMMER2IOC_PFS_LOOKUP:
113                 if (error == 0)
114                         error = hammer2_ioctl_pfs_lookup(ip, data);
115                 break;
116         case HAMMER2IOC_PFS_CREATE:
117                 if (error == 0)
118                         error = hammer2_ioctl_pfs_create(ip, data);
119                 break;
120         case HAMMER2IOC_PFS_DELETE:
121                 if (error == 0)
122                         error = hammer2_ioctl_pfs_delete(ip, data);
123                 break;
124         case HAMMER2IOC_PFS_SNAPSHOT:
125                 if (error == 0)
126                         error = hammer2_ioctl_pfs_snapshot(ip, data);
127                 break;
128         case HAMMER2IOC_INODE_GET:
129                 error = hammer2_ioctl_inode_get(ip, data);
130                 break;
131         case HAMMER2IOC_INODE_SET:
132                 if (error == 0)
133                         error = hammer2_ioctl_inode_set(ip, data);
134                 break;
135         /*case HAMMER2IOC_INODE_COMP_SET:
136                 error = hammer2_ioctl_inode_comp_set(ip, data);
137                 break;
138         case HAMMER2IOC_INODE_COMP_REC_SET:
139                 error = hammer2_ioctl_inode_comp_rec_set(ip, data);
140                 break;
141         case HAMMER2IOC_INODE_COMP_REC_SET2:
142                 error = hammer2_ioctl_inode_comp_rec_set2(ip, data);
143                 break;*/
144         default:
145                 error = EOPNOTSUPP;
146                 break;
147         }
148         return (error);
149 }
150
151 /*
152  * Retrieve version and basic info
153  */
154 static int
155 hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data)
156 {
157         hammer2_mount_t *hmp = ip->pmp->mount_cluster->hmp;
158         hammer2_ioc_version_t *version = data;
159
160         version->version = hmp->voldata.version;
161         return 0;
162 }
163
164 static int
165 hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data)
166 {
167         hammer2_ioc_recluster_t *recl = data;
168         struct file *fp;
169
170         fp = holdfp(curproc->p_fd, recl->fd, -1);
171         if (fp) {
172                 kprintf("reconnect to cluster\n");
173                 hammer2_cluster_reconnect(ip->pmp, fp);
174                 return 0;
175         } else {
176                 return EINVAL;
177         }
178 }
179
180 /*
181  * Retrieve information about a remote
182  */
183 static int
184 hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data)
185 {
186         hammer2_mount_t *hmp = ip->pmp->mount_cluster->hmp;
187         hammer2_ioc_remote_t *remote = data;
188         int copyid = remote->copyid;
189
190         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
191                 return (EINVAL);
192
193         hammer2_voldata_lock(hmp);
194         remote->copy1 = hmp->voldata.copyinfo[copyid];
195         hammer2_voldata_unlock(hmp, 0);
196
197         /*
198          * Adjust nextid (GET only)
199          */
200         while (++copyid < HAMMER2_COPYID_COUNT &&
201                hmp->voldata.copyinfo[copyid].copyid == 0) {
202                 ;
203         }
204         if (copyid == HAMMER2_COPYID_COUNT)
205                 remote->nextid = -1;
206         else
207                 remote->nextid = copyid;
208
209         return(0);
210 }
211
212 /*
213  * Add new remote entry
214  */
215 static int
216 hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data)
217 {
218         hammer2_ioc_remote_t *remote = data;
219         hammer2_pfsmount_t *pmp = ip->pmp;
220         hammer2_mount_t *hmp;
221         int copyid = remote->copyid;
222         int error = 0;
223
224         if (copyid >= HAMMER2_COPYID_COUNT)
225                 return (EINVAL);
226
227         hmp = pmp->mount_cluster->hmp;
228         hammer2_voldata_lock(hmp);
229         if (copyid < 0) {
230                 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
231                         if (hmp->voldata.copyinfo[copyid].copyid == 0)
232                                 break;
233                 }
234                 if (copyid == HAMMER2_COPYID_COUNT) {
235                         error = ENOSPC;
236                         goto failed;
237                 }
238         }
239         hammer2_modify_volume(hmp);
240         remote->copy1.copyid = copyid;
241         hmp->voldata.copyinfo[copyid] = remote->copy1;
242         hammer2_volconf_update(pmp, copyid);
243 failed:
244         hammer2_voldata_unlock(hmp, 1);
245         return (error);
246 }
247
248 /*
249  * Delete existing remote entry
250  */
251 static int
252 hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data)
253 {
254         hammer2_ioc_remote_t *remote = data;
255         hammer2_pfsmount_t *pmp = ip->pmp;
256         hammer2_mount_t *hmp;
257         int copyid = remote->copyid;
258         int error = 0;
259
260         hmp = pmp->mount_cluster->hmp;
261         if (copyid >= HAMMER2_COPYID_COUNT)
262                 return (EINVAL);
263         remote->copy1.path[sizeof(remote->copy1.path) - 1] = 0;
264         hammer2_voldata_lock(hmp);
265         if (copyid < 0) {
266                 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
267                         if (hmp->voldata.copyinfo[copyid].copyid == 0)
268                                 continue;
269                         if (strcmp(remote->copy1.path,
270                             hmp->voldata.copyinfo[copyid].path) == 0) {
271                                 break;
272                         }
273                 }
274                 if (copyid == HAMMER2_COPYID_COUNT) {
275                         error = ENOENT;
276                         goto failed;
277                 }
278         }
279         hammer2_modify_volume(hmp);
280         hmp->voldata.copyinfo[copyid].copyid = 0;
281         hammer2_volconf_update(pmp, copyid);
282 failed:
283         hammer2_voldata_unlock(hmp, 1);
284         return (error);
285 }
286
287 /*
288  * Replace existing remote entry
289  */
290 static int
291 hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data)
292 {
293         hammer2_ioc_remote_t *remote = data;
294         hammer2_mount_t *hmp;
295         int copyid = remote->copyid;
296
297         hmp = ip->pmp->mount_cluster->hmp;
298
299         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
300                 return (EINVAL);
301
302         hammer2_voldata_lock(hmp);
303         /*hammer2_volconf_update(pmp, copyid);*/
304         hammer2_voldata_unlock(hmp, 1);
305
306         return(0);
307 }
308
309 /*
310  * Retrieve communications socket
311  */
312 static int
313 hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data)
314 {
315         return (EOPNOTSUPP);
316 }
317
318 /*
319  * Set communications socket for connection
320  */
321 static int
322 hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data)
323 {
324         hammer2_ioc_remote_t *remote = data;
325         hammer2_mount_t *hmp;
326         int copyid = remote->copyid;
327
328         hmp = ip->pmp->mount_cluster->hmp;
329         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
330                 return (EINVAL);
331
332         hammer2_voldata_lock(hmp);
333         hammer2_voldata_unlock(hmp, 0);
334
335         return(0);
336 }
337
338 /*
339  * Used to scan and retrieve PFS information.  PFS's are directories under
340  * the super-root.
341  *
342  * To scan PFSs pass name_key=0.  The function will scan for the next
343  * PFS and set all fields, as well as set name_next to the next key.
344  * When no PFSs remain, name_next is set to (hammer2_key_t)-1.
345  *
346  * To retrieve the PFS associated with the file descriptor, pass
347  * name_key set to (hammer2_key_t)-1.
348  */
349 static int
350 hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data)
351 {
352         hammer2_inode_data_t *ipdata;
353         hammer2_mount_t *hmp;
354         hammer2_ioc_pfs_t *pfs;
355         hammer2_chain_t *parent;
356         hammer2_chain_t *chain;
357         hammer2_chain_t *rchain;
358         int error;
359
360         error = 0;
361         hmp = ip->pmp->mount_cluster->hmp;
362         pfs = data;
363         parent = hammer2_chain_lookup_init(hmp->schain, 0);
364         rchain = ip->pmp->mount_cluster->rchain;
365
366         /*
367          * Search for the first key or specific key.  Remember that keys
368          * can be returned in any order.
369          */
370         if (pfs->name_key == 0) {
371                 chain = hammer2_chain_lookup(&parent,
372                                              0, (hammer2_key_t)-1, 0);
373         } else if (pfs->name_key == (hammer2_key_t)-1) {
374                 chain = hammer2_chain_lookup(&parent,
375                                              rchain->data->ipdata.name_key,
376                                              rchain->data->ipdata.name_key,
377                                              0);
378         } else {
379                 chain = hammer2_chain_lookup(&parent,
380                                              pfs->name_key, pfs->name_key, 0);
381         }
382         while (chain && chain->bref.type != HAMMER2_BREF_TYPE_INODE) {
383                 chain = hammer2_chain_next(&parent, chain,
384                                            0, (hammer2_key_t)-1, 0);
385         }
386         if (chain) {
387                 /*
388                  * Load the data being returned by the ioctl.
389                  */
390                 ipdata = &chain->data->ipdata;
391                 pfs->name_key = ipdata->name_key;
392                 pfs->pfs_type = ipdata->pfs_type;
393                 pfs->pfs_clid = ipdata->pfs_clid;
394                 pfs->pfs_fsid = ipdata->pfs_fsid;
395                 KKASSERT(ipdata->name_len < sizeof(pfs->name));
396                 bcopy(ipdata->filename, pfs->name, ipdata->name_len);
397                 pfs->name[ipdata->name_len] = 0;
398                 ipdata = NULL;  /* safety */
399
400                 /*
401                  * Calculate the next field
402                  */
403                 do {
404                         chain = hammer2_chain_next(&parent, chain,
405                                                    0, (hammer2_key_t)-1, 0);
406                 } while (chain && chain->bref.type != HAMMER2_BREF_TYPE_INODE);
407                 if (chain) {
408                         pfs->name_next = chain->data->ipdata.name_key;
409                         hammer2_chain_unlock(chain);
410                 } else {
411                         pfs->name_next = (hammer2_key_t)-1;
412                 }
413         } else {
414                 pfs->name_next = (hammer2_key_t)-1;
415                 error = ENOENT;
416         }
417         hammer2_chain_lookup_done(parent);
418
419         return (error);
420 }
421
422 /*
423  * Find a specific PFS by name
424  */
425 static int
426 hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
427 {
428         hammer2_inode_data_t *ipdata;
429         hammer2_mount_t *hmp;
430         hammer2_ioc_pfs_t *pfs;
431         hammer2_chain_t *parent;
432         hammer2_chain_t *chain;
433         hammer2_key_t lhc;
434         int error;
435         size_t len;
436
437         error = 0;
438         hmp = ip->pmp->mount_cluster->hmp;
439         pfs = data;
440         parent = hammer2_chain_lookup_init(hmp->schain, HAMMER2_LOOKUP_SHARED);
441
442         pfs->name[sizeof(pfs->name) - 1] = 0;
443         len = strlen(pfs->name);
444         lhc = hammer2_dirhash(pfs->name, len);
445
446         chain = hammer2_chain_lookup(&parent,
447                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
448                                      HAMMER2_LOOKUP_SHARED);
449         while (chain) {
450                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
451                     len == chain->data->ipdata.name_len &&
452                     bcmp(pfs->name, chain->data->ipdata.filename, len) == 0) {
453                         break;
454                 }
455                 chain = hammer2_chain_next(&parent, chain,
456                                            lhc, lhc + HAMMER2_DIRHASH_LOMASK,
457                                            HAMMER2_LOOKUP_SHARED);
458         }
459
460         /*
461          * Load the data being returned by the ioctl.
462          */
463         if (chain) {
464                 ipdata = &chain->data->ipdata;
465                 pfs->name_key = ipdata->name_key;
466                 pfs->pfs_type = ipdata->pfs_type;
467                 pfs->pfs_clid = ipdata->pfs_clid;
468                 pfs->pfs_fsid = ipdata->pfs_fsid;
469                 ipdata = NULL;
470
471                 hammer2_chain_unlock(chain);
472         } else {
473                 error = ENOENT;
474         }
475         hammer2_chain_lookup_done(parent);
476         return (error);
477 }
478
479 /*
480  * Create a new PFS under the super-root
481  */
482 static int
483 hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
484 {
485         hammer2_inode_data_t *nipdata;
486         hammer2_mount_t *hmp;
487         hammer2_ioc_pfs_t *pfs;
488         hammer2_inode_t *nip;
489         hammer2_chain_t *nchain;
490         hammer2_trans_t trans;
491         int error;
492
493         hmp = ip->pmp->mount_cluster->hmp;
494         pfs = data;
495         nip = NULL;
496
497         if (pfs->name[0] == 0)
498                 return(EINVAL);
499         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure 0-termination */
500
501         hammer2_trans_init(&trans, ip->pmp, 0);
502         nip = hammer2_inode_create(&trans, hmp->sroot, NULL, NULL,
503                                      pfs->name, strlen(pfs->name),
504                                      &nchain, &error);
505         if (error == 0) {
506                 nipdata = hammer2_chain_modify_ip(&trans, nip, &nchain,
507                                                   HAMMER2_MODIFY_ASSERTNOCOPY);
508                 nipdata->pfs_type = pfs->pfs_type;
509                 nipdata->pfs_clid = pfs->pfs_clid;
510                 nipdata->pfs_fsid = pfs->pfs_fsid;
511                 hammer2_inode_unlock_ex(nip, nchain);
512         }
513         hammer2_trans_done(&trans);
514         return (error);
515 }
516
517 /*
518  * Destroy an existing PFS under the super-root
519  */
520 static int
521 hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
522 {
523         hammer2_mount_t *hmp;
524         hammer2_ioc_pfs_t *pfs = data;
525         hammer2_trans_t trans;
526         int error;
527
528         hmp = ip->pmp->mount_cluster->hmp;
529         hammer2_trans_init(&trans, ip->pmp, 0);
530         error = hammer2_unlink_file(&trans, hmp->sroot,
531                                     pfs->name, strlen(pfs->name),
532                                     2, NULL);
533         hammer2_trans_done(&trans);
534
535         return (error);
536 }
537
538 static int
539 hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
540 {
541         hammer2_ioc_pfs_t *pfs = data;
542         hammer2_trans_t trans;
543         hammer2_chain_t *parent;
544         int error;
545
546         if (pfs->name[0] == 0)
547                 return(EINVAL);
548         if (pfs->name[sizeof(pfs->name)-1] != 0)
549                 return(EINVAL);
550
551         hammer2_trans_init(&trans, ip->pmp, 0);
552         parent = hammer2_inode_lock_ex(ip);
553         error = hammer2_chain_snapshot(&trans, ip, pfs);
554         hammer2_inode_unlock_ex(ip, parent);
555         hammer2_trans_done(&trans);
556
557         return (error);
558 }
559
560 /*
561  * Retrieve the raw inode structure
562  */
563 static int
564 hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
565 {
566         hammer2_ioc_inode_t *ino = data;
567         hammer2_chain_t *parent;
568
569         parent = hammer2_inode_lock_sh(ip);
570         ino->ip_data = ip->chain->data->ipdata;
571         ino->kdata = ip;
572         hammer2_inode_unlock_sh(ip, parent);
573
574         return (0);
575 }
576
577 /*
578  * Set various parameters in an inode which cannot be set through
579  * normal filesystem VNOPS.
580  */
581 static int
582 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
583 {
584         hammer2_inode_data_t *ipdata;
585         hammer2_ioc_inode_t *ino = data;
586         hammer2_chain_t *chain;
587         hammer2_trans_t trans;
588         int error = 0;
589
590         hammer2_trans_init(&trans, ip->pmp, 0);
591         chain = hammer2_inode_lock_ex(ip);
592
593         if (ino->ip_data.comp_algo != chain->data->ipdata.comp_algo) {
594                 ipdata = hammer2_chain_modify_ip(&trans, ip, &chain, 0);
595                 ipdata->comp_algo = ino->ip_data.comp_algo;
596         }
597         ino->kdata = ip;
598         
599         /* Ignore these flags for now...*/
600         if (ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) {
601         }
602         if (ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) {
603         }
604         if (ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) {
605         }
606         hammer2_trans_done(&trans);
607         hammer2_inode_unlock_ex(ip, chain);
608
609         return (error);
610 }