VirtualBox

source: vbox/trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c@ 30357

Last change on this file since 30357 was 29147, checked in by vboxsync, 15 years ago

Additions/SharedFolders/Solaris: Pass 0 as Index to directory listing and some cleanup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.0 KB
Line 
1/** @file
2 * VirtualBox File System for Solaris Guests, provider implementation.
3 */
4
5/*
6 * Copyright (C) 2008 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16/*
17 * Provider interfaces for shared folder file system.
18 */
19
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <sys/mntent.h>
23#include <sys/param.h>
24#include <sys/modctl.h>
25#include <sys/mount.h>
26#include <sys/policy.h>
27#include <sys/atomic.h>
28#include <sys/sysmacros.h>
29#include <sys/ddi.h>
30#include <sys/sunddi.h>
31#include "vboxfs_prov.h"
32#ifdef u
33#undef u
34#endif
35#include "../../common/VBoxGuestLib/VBoxCalls.h"
36
37#define SFPROV_VERSION 1
38
39static VBSFCLIENT vbox_client;
40
41/*
42 * utility to create strings
43 */
44static SHFLSTRING *
45sfprov_string(char *path, int *sz)
46{
47 SHFLSTRING *str;
48 int len = strlen(path);
49
50 *sz = len + 1 + sizeof (*str) - sizeof (str->String);
51 str = kmem_zalloc(*sz, KM_SLEEP);
52 str->u16Size = len + 1;
53 str->u16Length = len;
54 strcpy(str->String.utf8, path);
55 return (str);
56}
57
58sfp_connection_t *
59sfprov_connect(int version)
60{
61 /*
62 * only one version for now, so must match
63 */
64 int rc = -1;
65 if (version != SFPROV_VERSION)
66 {
67 cmn_err(CE_WARN, "sfprov_connect: wrong version");
68 return NULL;
69 }
70 rc = vboxInit();
71 if (RT_SUCCESS(rc))
72 {
73 rc = vboxConnect(&vbox_client);
74 if (RT_SUCCESS(rc))
75 {
76 rc = vboxCallSetUtf8(&vbox_client);
77 if (RT_SUCCESS(rc))
78 {
79 return ((sfp_connection_t *)&vbox_client);
80 }
81 else
82 cmn_err(CE_WARN, "sfprov_connect: vboxCallSetUtf8() failed");
83
84 vboxDisconnect(&vbox_client);
85 }
86 else
87 cmn_err(CE_WARN, "sfprov_connect: vboxConnect() failed rc=%d", rc);
88 vboxUninit();
89 }
90 else
91 cmn_err(CE_WARN, "sfprov_connect: vboxInit() failed rc=%d", rc);
92}
93
94void
95sfprov_disconnect(sfp_connection_t *conn)
96{
97 if (conn != (sfp_connection_t *)&vbox_client)
98 cmn_err(CE_WARN, "sfprov_disconnect: bad argument");
99 vboxDisconnect(&vbox_client);
100 vboxUninit();
101}
102
103
104/*
105 * representation of an active mount point
106 */
107struct sfp_mount {
108 VBSFMAP map;
109};
110
111int
112sfprov_mount(sfp_connection_t *conn, char *path, sfp_mount_t **mnt)
113{
114 sfp_mount_t *m;
115 SHFLSTRING *str;
116 int size;
117 int rc;
118
119 m = kmem_zalloc(sizeof (*m), KM_SLEEP);
120 str = sfprov_string(path, &size);
121 rc = vboxCallMapFolder(&vbox_client, str, &m->map);
122 if (!RT_SUCCESS(rc)) {
123 cmn_err(CE_WARN, "sfprov_mount: vboxCallMapFolder() failed");
124 kmem_free(m, sizeof (*m));
125 *mnt = NULL;
126 rc = EINVAL;
127 } else {
128 *mnt = m;
129 rc = 0;
130 }
131 kmem_free(str, size);
132 return (rc);
133}
134
135int
136sfprov_unmount(sfp_mount_t *mnt)
137{
138 int rc;
139
140 rc = vboxCallUnmapFolder(&vbox_client, &mnt->map);
141 if (!RT_SUCCESS(rc)) {
142 cmn_err(CE_WARN, "sfprov_mount: vboxCallUnmapFolder() failed");
143 rc = EINVAL;
144 } else {
145 rc = 0;
146 }
147 kmem_free(mnt, sizeof (*mnt));
148 return (rc);
149}
150
151/*
152 * query information about a mounted file system
153 */
154int
155sfprov_get_blksize(sfp_mount_t *mnt, uint64_t *blksize)
156{
157 int rc;
158 SHFLVOLINFO info;
159 uint32_t bytes = sizeof(SHFLVOLINFO);
160
161 rc = vboxCallFSInfo(&vbox_client, &mnt->map, 0,
162 (SHFL_INFO_GET | SHFL_INFO_VOLUME), &bytes, (SHFLDIRINFO *)&info);
163 if (RT_FAILURE(rc))
164 return (EINVAL);
165 *blksize = info.ulBytesPerAllocationUnit;
166 return (0);
167}
168
169int
170sfprov_get_blksused(sfp_mount_t *mnt, uint64_t *blksused)
171{
172 int rc;
173 SHFLVOLINFO info;
174 uint32_t bytes = sizeof(SHFLVOLINFO);
175
176 rc = vboxCallFSInfo(&vbox_client, &mnt->map, 0,
177 (SHFL_INFO_GET | SHFL_INFO_VOLUME), &bytes, (SHFLDIRINFO *)&info);
178 if (RT_FAILURE(rc))
179 return (EINVAL);
180 *blksused = (info.ullTotalAllocationBytes -
181 info.ullAvailableAllocationBytes) / info.ulBytesPerAllocationUnit;
182 return (0);
183}
184
185int
186sfprov_get_blksavail(sfp_mount_t *mnt, uint64_t *blksavail)
187{
188 int rc;
189 SHFLVOLINFO info;
190 uint32_t bytes = sizeof(SHFLVOLINFO);
191
192 rc = vboxCallFSInfo(&vbox_client, &mnt->map, 0,
193 (SHFL_INFO_GET | SHFL_INFO_VOLUME), &bytes, (SHFLDIRINFO *)&info);
194 if (RT_FAILURE(rc))
195 return (EINVAL);
196 *blksavail =
197 info.ullAvailableAllocationBytes / info.ulBytesPerAllocationUnit;
198 return (0);
199}
200
201int
202sfprov_get_maxnamesize(sfp_mount_t *mnt, uint32_t *maxnamesize)
203{
204 int rc;
205 SHFLVOLINFO info;
206 uint32_t bytes = sizeof(SHFLVOLINFO);
207
208 rc = vboxCallFSInfo(&vbox_client, &mnt->map, 0,
209 (SHFL_INFO_GET | SHFL_INFO_VOLUME), &bytes, (SHFLDIRINFO *)&info);
210 if (RT_FAILURE(rc))
211 return (EINVAL);
212 *maxnamesize = info.fsProperties.cbMaxComponent;
213 return (0);
214}
215
216int
217sfprov_get_readonly(sfp_mount_t *mnt, uint32_t *readonly)
218{
219 int rc;
220 SHFLVOLINFO info;
221 uint32_t bytes = sizeof(SHFLVOLINFO);
222
223 rc = vboxCallFSInfo(&vbox_client, &mnt->map, 0,
224 (SHFL_INFO_GET | SHFL_INFO_VOLUME), &bytes, (SHFLDIRINFO *)&info);
225 if (RT_FAILURE(rc))
226 return (EINVAL);
227 *readonly = info.fsProperties.fReadOnly;
228 return (0);
229}
230
231/*
232 * File operations: open/close/read/write/etc.
233 *
234 * open/create can return any relevant errno, however ENOENT
235 * generally means that the host file didn't exist.
236 */
237struct sfp_file {
238 SHFLHANDLE handle;
239 VBSFMAP map; /* need this again for the close operation */
240};
241
242int
243sfprov_create(sfp_mount_t *mnt, char *path, sfp_file_t **fp)
244{
245 int rc;
246 SHFLCREATEPARMS parms;
247 SHFLSTRING *str;
248 int size;
249 sfp_file_t *newfp;
250
251 str = sfprov_string(path, &size);
252 parms.Handle = 0;
253 parms.Info.cbObject = 0;
254 parms.CreateFlags = SHFL_CF_ACT_CREATE_IF_NEW |
255 SHFL_CF_ACT_REPLACE_IF_EXISTS | SHFL_CF_ACCESS_READWRITE;
256 rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms);
257 kmem_free(str, size);
258
259 if (RT_FAILURE(rc))
260 return (EINVAL);
261 if (parms.Handle == SHFL_HANDLE_NIL) {
262 if (parms.Result == SHFL_FILE_EXISTS)
263 return (EEXIST);
264 return (ENOENT);
265 }
266 newfp = kmem_alloc(sizeof(sfp_file_t), KM_SLEEP);
267 newfp->handle = parms.Handle;
268 newfp->map = mnt->map;
269 *fp = newfp;
270 return (0);
271}
272
273int
274sfprov_open(sfp_mount_t *mnt, char *path, sfp_file_t **fp)
275{
276 int rc;
277 SHFLCREATEPARMS parms;
278 SHFLSTRING *str;
279 int size;
280 sfp_file_t *newfp;
281
282 /*
283 * First we attempt to open it read/write. If that fails we
284 * try read only.
285 */
286 bzero(&parms, sizeof(parms));
287 str = sfprov_string(path, &size);
288 parms.Handle = SHFL_HANDLE_NIL;
289 parms.Info.cbObject = 0;
290 parms.CreateFlags = SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READWRITE;
291 rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms);
292 if (RT_FAILURE(rc) && rc != VERR_ACCESS_DENIED) {
293 kmem_free(str, size);
294 return RTErrConvertToErrno(rc);
295 }
296 if (parms.Handle == SHFL_HANDLE_NIL) {
297 if (parms.Result == SHFL_PATH_NOT_FOUND ||
298 parms.Result == SHFL_FILE_NOT_FOUND) {
299 kmem_free(str, size);
300 return (ENOENT);
301 }
302 parms.CreateFlags =
303 SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ;
304 rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms);
305 if (RT_FAILURE(rc)) {
306 kmem_free(str, size);
307 return RTErrConvertToErrno(rc);
308 }
309 if (parms.Handle == SHFL_HANDLE_NIL) {
310 kmem_free(str, size);
311 return (ENOENT);
312 }
313 }
314 newfp = kmem_alloc(sizeof(sfp_file_t), KM_SLEEP);
315 newfp->handle = parms.Handle;
316 newfp->map = mnt->map;
317 *fp = newfp;
318 return (0);
319}
320
321int
322sfprov_trunc(sfp_mount_t *mnt, char *path)
323{
324 int rc;
325 SHFLCREATEPARMS parms;
326 SHFLSTRING *str;
327 int size;
328
329 /*
330 * open it read/write.
331 */
332 str = sfprov_string(path, &size);
333 parms.Handle = 0;
334 parms.Info.cbObject = 0;
335 parms.CreateFlags = SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READWRITE |
336 SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
337 rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms);
338
339 if (RT_FAILURE(rc)) {
340 kmem_free(str, size);
341 return (EINVAL);
342 }
343 (void)vboxCallClose(&vbox_client, &mnt->map, parms.Handle);
344 return (0);
345}
346
347int
348sfprov_close(sfp_file_t *fp)
349{
350 int rc;
351
352 rc = vboxCallClose(&vbox_client, &fp->map, fp->handle);
353 kmem_free(fp, sizeof(sfp_file_t));
354 return (0);
355}
356
357int
358sfprov_read(sfp_file_t *fp, char *buffer, uint64_t offset, uint32_t *numbytes)
359{
360 int rc;
361
362 rc = vboxCallRead(&vbox_client, &fp->map, fp->handle, offset,
363 numbytes, (uint8_t *)buffer, 0); /* what is that last arg? */
364 if (RT_FAILURE(rc))
365 return (EINVAL);
366 return (0);
367}
368
369int
370sfprov_write(sfp_file_t *fp, char *buffer, uint64_t offset, uint32_t *numbytes)
371{
372 int rc;
373
374 rc = vboxCallWrite(&vbox_client, &fp->map, fp->handle, offset,
375 numbytes, (uint8_t *)buffer, 0); /* what is that last arg? */
376 if (RT_FAILURE(rc))
377 return (EINVAL);
378 return (0);
379}
380
381
382static int
383sfprov_getinfo(sfp_mount_t *mnt, char *path, RTFSOBJINFO *info)
384{
385 int rc;
386 SHFLCREATEPARMS parms;
387 SHFLSTRING *str;
388 int size;
389
390 str = sfprov_string(path, &size);
391 parms.Handle = 0;
392 parms.Info.cbObject = 0;
393 parms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
394 rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms);
395 kmem_free(str, size);
396
397 if (RT_FAILURE(rc))
398 return (EINVAL);
399 if (parms.Result != SHFL_FILE_EXISTS)
400 return (ENOENT);
401 *info = parms.Info;
402 return (0);
403}
404
405/*
406 * get information about a file (or directory)
407 */
408int
409sfprov_get_mode(sfp_mount_t *mnt, char *path, mode_t *mode)
410{
411 int rc;
412 RTFSOBJINFO info;
413 mode_t m = 0;
414
415 rc = sfprov_getinfo(mnt, path, &info);
416 if (rc)
417 return (rc);
418 if (RTFS_IS_DIRECTORY(info.Attr.fMode))
419 m |= S_IFDIR;
420 else if (RTFS_IS_FILE(info.Attr.fMode))
421 m |= S_IFREG;
422 else if (RTFS_IS_FIFO(info.Attr.fMode))
423 m |= S_IFDIR;
424 else if (RTFS_IS_DEV_CHAR(info.Attr.fMode))
425 m |= S_IFCHR;
426 else if (RTFS_IS_DEV_BLOCK(info.Attr.fMode))
427 m |= S_IFBLK;
428 else if (RTFS_IS_SYMLINK(info.Attr.fMode))
429 m |= S_IFLNK;
430 else if (RTFS_IS_SOCKET(info.Attr.fMode))
431 m |= S_IFSOCK;
432
433 if (info.Attr.fMode & RTFS_UNIX_IRUSR)
434 m |= S_IRUSR;
435 if (info.Attr.fMode & RTFS_UNIX_IWUSR)
436 m |= S_IWUSR;
437 if (info.Attr.fMode & RTFS_UNIX_IXUSR)
438 m |= S_IXUSR;
439 if (info.Attr.fMode & RTFS_UNIX_IRGRP)
440 m |= S_IRGRP;
441 if (info.Attr.fMode & RTFS_UNIX_IWGRP)
442 m |= S_IWGRP;
443 if (info.Attr.fMode & RTFS_UNIX_IXGRP)
444 m |= S_IXGRP;
445 if (info.Attr.fMode & RTFS_UNIX_IROTH)
446 m |= S_IROTH;
447 if (info.Attr.fMode & RTFS_UNIX_IWOTH)
448 m |= S_IWOTH;
449 if (info.Attr.fMode & RTFS_UNIX_IXOTH)
450 m |= S_IXOTH;
451 if (info.Attr.fMode & RTFS_UNIX_ISUID)
452 m |= S_ISUID;
453 *mode = m;
454 return (0);
455}
456
457int
458sfprov_get_size(sfp_mount_t *mnt, char *path, uint64_t *size)
459{
460 int rc;
461 RTFSOBJINFO info;
462
463 rc = sfprov_getinfo(mnt, path, &info);
464 if (rc)
465 return (rc);
466 *size = info.cbObject;
467 return (0);
468}
469
470int
471sfprov_get_atime(sfp_mount_t *mnt, char *path, timestruc_t *time)
472{
473 int rc;
474 RTFSOBJINFO info;
475 uint64_t nanosec;
476
477 rc = sfprov_getinfo(mnt, path, &info);
478 if (rc)
479 return (rc);
480 nanosec = RTTimeSpecGetNano(&info.AccessTime);
481 time->tv_sec = nanosec / 1000000000;
482 time->tv_nsec = nanosec % 1000000000;
483 return (0);
484}
485
486int
487sfprov_get_mtime(sfp_mount_t *mnt, char *path, timestruc_t *time)
488{
489 int rc;
490 RTFSOBJINFO info;
491 uint64_t nanosec;
492
493 rc = sfprov_getinfo(mnt, path, &info);
494 if (rc)
495 return (rc);
496 nanosec = RTTimeSpecGetNano(&info.ModificationTime);
497 time->tv_sec = nanosec / 1000000000;
498 time->tv_nsec = nanosec % 1000000000;
499 return (0);
500}
501
502int
503sfprov_get_ctime(sfp_mount_t *mnt, char *path, timestruc_t *time)
504{
505 int rc;
506 RTFSOBJINFO info;
507 uint64_t nanosec;
508
509 rc = sfprov_getinfo(mnt, path, &info);
510 if (rc)
511 return (rc);
512 nanosec = RTTimeSpecGetNano(&info.ChangeTime);
513 time->tv_sec = nanosec / 1000000000;
514 time->tv_nsec = nanosec % 1000000000;
515 return (0);
516}
517
518/*
519 * Directory operations
520 */
521int
522sfprov_mkdir(sfp_mount_t *mnt, char *path, sfp_file_t **fp)
523{
524 int rc;
525 SHFLCREATEPARMS parms;
526 SHFLSTRING *str;
527 int size;
528 sfp_file_t *newfp;
529
530 str = sfprov_string(path, &size);
531 parms.Handle = 0;
532 parms.Info.cbObject = 0;
533 parms.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_CREATE_IF_NEW |
534 SHFL_CF_ACT_FAIL_IF_EXISTS | SHFL_CF_ACCESS_READ;
535 rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms);
536 kmem_free(str, size);
537
538 if (RT_FAILURE(rc))
539 return (EINVAL);
540 if (parms.Handle == SHFL_HANDLE_NIL) {
541 if (parms.Result == SHFL_FILE_EXISTS)
542 return (EEXIST);
543 return (ENOENT);
544 }
545 newfp = kmem_alloc(sizeof(sfp_file_t), KM_SLEEP);
546 newfp->handle = parms.Handle;
547 newfp->map = mnt->map;
548 *fp = newfp;
549 return (0);
550}
551
552int
553sfprov_remove(sfp_mount_t *mnt, char *path)
554{
555 int rc;
556 SHFLSTRING *str;
557 int size;
558
559 str = sfprov_string(path, &size);
560 rc = vboxCallRemove(&vbox_client, &mnt->map, str, SHFL_REMOVE_FILE);
561 kmem_free(str, size);
562 if (RT_FAILURE(rc))
563 return (EINVAL);
564 return (0);
565}
566
567int
568sfprov_rmdir(sfp_mount_t *mnt, char *path)
569{
570 int rc;
571 SHFLSTRING *str;
572 int size;
573
574 str = sfprov_string(path, &size);
575 rc = vboxCallRemove(&vbox_client, &mnt->map, str, SHFL_REMOVE_DIR);
576 kmem_free(str, size);
577 if (RT_FAILURE(rc))
578 return (RTErrConvertToErrno(rc));
579 return (0);
580}
581
582int
583sfprov_rename(sfp_mount_t *mnt, char *from, char *to, uint_t is_dir)
584{
585 int rc;
586 SHFLSTRING *old, *new;
587 int old_size, new_size;
588
589 old = sfprov_string(from, &old_size);
590 new = sfprov_string(to, &new_size);
591 rc = vboxCallRename(&vbox_client, &mnt->map, old, new,
592 (is_dir ? SHFL_RENAME_DIR : SHFL_RENAME_FILE) |
593 SHFL_RENAME_REPLACE_IF_EXISTS);
594 kmem_free(old, old_size);
595 kmem_free(new, new_size);
596 if (RT_FAILURE(rc))
597 return (RTErrConvertToErrno(rc));
598 return (0);
599}
600
601
602/*
603 * Read all filenames in a directory.
604 *
605 * - success - all entries read and returned
606 * - ENOENT - Couldn't open the directory for reading
607 * - EINVAL - Internal error of some kind
608 *
609 * On successful return, buffer[0] is the start of an array of "char *"
610 * pointers to the filenames. The array ends with a NULL pointer.
611 * The remaining storage in buffer after that NULL pointer is where the
612 * filename strings actually are.
613 *
614 * On input nents is the max number of filenames the requestor can handle.
615 * On output nents is the number of entries at buff[0]
616 *
617 * The caller is responsible for freeing the returned buffer.
618 */
619int
620sfprov_readdir(
621 sfp_mount_t *mnt,
622 char *path,
623 void **buffer,
624 size_t *buffersize,
625 uint32_t *nents)
626{
627 int error;
628 char *cp;
629 int len;
630 SHFLSTRING *mask_str = NULL; /* must be path with "/*" appended */
631 int mask_size;
632 sfp_file_t *fp;
633 void *buff_start = NULL;
634 size_t buff_size;
635 static char infobuff[2 * MAXNAMELEN]; /* not on stack!! */
636 SHFLDIRINFO *info = (SHFLDIRINFO *)&infobuff;
637 uint32_t numbytes = sizeof (infobuff);
638 uint32_t justone;
639 uint32_t cnt;
640 char **name_ptrs;
641
642 *buffer = NULL;
643 *buffersize = 0;
644 if (*nents == 0)
645 return (EINVAL);
646 error = sfprov_open(mnt, path, &fp);
647 if (error != 0)
648 return (ENOENT);
649
650 /*
651 * Create mask that VBox expects. This needs to be the directory path,
652 * plus a "*" wildcard to get all files.
653 */
654 len = strlen(path) + 3;
655 cp = kmem_alloc(len, KM_SLEEP);
656 strcpy(cp, path);
657 strcat(cp, "/*");
658 mask_str = sfprov_string(cp, &mask_size);
659 kmem_free(cp, len);
660
661 /*
662 * Allocate the buffer to use for return values. Each entry
663 * in the buffer will have a pointer and the string itself.
664 * The pointers go in the front of the buffer, the strings
665 * at the end.
666 */
667 buff_size = *nents * (sizeof(char *) + MAXNAMELEN);
668 name_ptrs = buff_start = kmem_alloc(buff_size, KM_SLEEP);
669 cp = (char *)buff_start + buff_size;
670
671 /*
672 * Now loop using vboxCallDirInfo to get one file name at a time
673 */
674 cnt = 0;
675 for (;;) {
676 justone = 1;
677 numbytes = sizeof (infobuff);
678 error = vboxCallDirInfo(&vbox_client, &fp->map, fp->handle,
679 mask_str, SHFL_LIST_RETURN_ONE, 0, &numbytes, info,
680 &justone);
681 if (error == VERR_NO_MORE_FILES) {
682 break;
683 }
684 else if (error == VERR_NO_TRANSLATION) {
685 continue; /* ?? just skip this one */
686 }
687 else if (error != VINF_SUCCESS || justone != 1) {
688 error = EINVAL;
689 goto done;
690 }
691
692 /*
693 * Put this name in the buffer, stop if we run out of room.
694 */
695 cp -= strlen(info->name.String.utf8) + 1;
696 if (cp < (char *)(&name_ptrs[cnt + 2]))
697 break;
698 strcpy(cp, info->name.String.utf8);
699 name_ptrs[cnt] = cp;
700 ++cnt;
701 }
702 error = 0;
703 name_ptrs[cnt] = NULL;
704 *nents = cnt;
705 *buffer = buff_start;
706 *buffersize = buff_size;
707done:
708 if (error != 0)
709 kmem_free(buff_start, buff_size);
710 kmem_free(mask_str, mask_size);
711 sfprov_close(fp);
712 return (error);
713}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette