VirtualBox

source: vbox/trunk/src/VBox/Additions/solaris/SharedFolders/vboxvfs_vfsops.c@ 11602

最後變更 在這個檔案從11602是 11602,由 vboxsync 提交於 16 年 前

Solaris 10 build (with no additions); XPCOM Python works on Solaris

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keyword 設為 Id
  • 屬性 svn:keywords 設為 Id
檔案大小: 18.6 KB
 
1/* $Id: vboxvfs_vfsops.c 11602 2008-08-25 08:06:18Z vboxsync $ */
2/** @file
3 * VirtualBox File System Driver for Solaris Guests. VFS operations.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
8 *
9 * Sun Microsystems, Inc. confidential
10 * All rights reserved
11 */
12
13
14/*******************************************************************************
15* Header Files *
16*******************************************************************************/
17#include <sys/types.h>
18#include <sys/mntent.h>
19#include <sys/param.h>
20#include <sys/modctl.h>
21#include <sys/mount.h>
22#include <sys/policy.h>
23#include <sys/atomic.h>
24#include <sys/sysmacros.h>
25#include <sys/ddi.h>
26#include <sys/sunddi.h>
27#ifdef u
28#undef u
29#endif
30#include "vboxvfs.h"
31
32#if defined(DEBUG_ramshankar) && !defined(LOG_ENABLED)
33# define LOG_ENABLED
34# define LOG_TO_BACKDOOR
35#endif
36#include <VBox/log.h>
37#include <iprt/string.h>
38#include <iprt/mem.h>
39#include <iprt/err.h>
40#if defined(DEBUG_ramshankar)
41# undef LogFlow
42# define LogFlow LogRel
43# undef Log
44# define Log LogRel
45#endif
46
47/*******************************************************************************
48* Defined Constants And Macros *
49*******************************************************************************/
50/** Mount Options */
51#define MNTOPT_VBOXVFS_UID "uid"
52#define MNTOPT_VBOXVFS_GID "gid"
53
54
55/*******************************************************************************
56* Internal Functions *
57*******************************************************************************/
58static int VBoxVFS_Init(int fType, char *pszName);
59static int VBoxVFS_Mount(vfs_t *pVFS, vnode_t *pVNode, struct mounta *pMount, cred_t *pCred);
60static int VBoxVFS_Unmount(vfs_t *pVFS, int fFlags, cred_t *pCred);
61static int VBoxVFS_Root(vfs_t *pVFS, vnode_t **ppVNode);
62static int VBoxVFS_Statfs(register vfs_t *pVFS, struct statvfs64 *pStat);
63static int VBoxVFS_VGet(vfs_t *pVFS, vnode_t **ppVNode, struct fid *pFid);
64static void VBoxVFS_FreeVFS(vfs_t *pVFS);
65
66static int vboxvfs_CheckMountPerm(vfs_t *pVFS, struct mounta *pMount, vnode_t *pVNodeSpec, cred_t *pCred);
67static int vboxvfs_GetIntOpt(vfs_t *pVFS, char *pszOpt, int *pValue);
68
69
70/*******************************************************************************
71* Structures and Typedefs *
72*******************************************************************************/
73/**
74 * mntopts_t: mount options table array
75 */
76static mntopt_t g_VBoxVFSMountOptions[] =
77{
78 /* Option Name Cancel Opt. Default Arg Flags Data */
79 { MNTOPT_VBOXVFS_UID, NULL, NULL, MO_HASVALUE, NULL },
80 { MNTOPT_VBOXVFS_GID, NULL, NULL, MO_HASVALUE, NULL }
81};
82
83/**
84 * mntopts_t: mount options table prototype
85 */
86static mntopts_t g_VBoxVFSMountTableProt =
87{
88 sizeof(g_VBoxVFSMountOptions) / sizeof(mntopt_t),
89 g_VBoxVFSMountOptions
90};
91
92/**
93 * vfsdef_t: driver specific mount options
94 */
95static vfsdef_t g_VBoxVFSDef =
96{
97 VFSDEF_VERSION,
98 DEVICE_NAME,
99 VBoxVFS_Init,
100 VSW_HASPROTO,
101 &g_VBoxVFSMountTableProt
102};
103
104/**
105 * modlfs: loadable file system
106 */
107static struct modlfs g_VBoxVFSLoadMod =
108{
109 &mod_fsops, /* extern from kernel */
110 DEVICE_DESC,
111 &g_VBoxVFSDef
112};
113
114/**
115 * modlinkage: export install/remove/info to the kernel
116 */
117static struct modlinkage g_VBoxVFSModLinkage =
118{
119 MODREV_1, /* loadable module system revision */
120 &g_VBoxVFSLoadMod,
121 NULL /* terminate array of linkage structures */
122};
123
124
125/*******************************************************************************
126* Global Variables *
127*******************************************************************************/
128/** GCC C++ hack. */
129unsigned __gxx_personality_v0 = 0xdecea5ed;
130/** Global connection to the client. */
131VBSFCLIENT g_VBoxVFSClient;
132/** Global VFS Operations pointer. */
133vfsops_t *g_pVBoxVFS_vfsops;
134/** The file system type identifier. */
135static int g_VBoxVFSType;
136/** Major number of this module */
137static major_t g_VBoxVFSMajor;
138/** Minor instance number */
139static minor_t g_VBoxVFSMinor;
140/** Minor lock mutex protection */
141static kmutex_t g_VBoxVFSMinorMtx;
142
143
144/**
145 * Kernel entry points
146 */
147int _init(void)
148{
149 LogFlow((DEVICE_NAME ":_init\n"));
150
151 return mod_install(&g_VBoxVFSModLinkage);;
152}
153
154
155int _fini(void)
156{
157 LogFlow((DEVICE_NAME ":_fini\n"));
158
159 int rc = mod_remove(&g_VBoxVFSModLinkage);
160 if (rc)
161 return rc;
162
163 /* Blow away the operation vectors*/
164 vfs_freevfsops_by_type(g_VBoxVFSType);
165 vn_freevnodeops(g_pVBoxVFS_vnodeops);
166}
167
168
169int _info(struct modinfo *pModInfo)
170{
171 LogFlow((DEVICE_NAME ":_info\n"));
172
173 return mod_info(&g_VBoxVFSModLinkage, pModInfo);
174}
175
176
177static int VBoxVFS_Init(int fType, char *pszName)
178{
179 int rc;
180
181 LogFlow((DEVICE_NAME ":VBoxVFS_Init\n"));
182
183 g_VBoxVFSType = fType;
184
185 /* Initialize the R0 guest library. */
186 rc = vboxInit();
187 if (VBOX_SUCCESS(rc))
188 {
189 /* Connect to the host service. */
190 rc = vboxConnect(&g_VBoxVFSClient);
191 if (VBOX_SUCCESS(rc))
192 {
193 /* Use UTF-8 encoding. */
194 rc = vboxCallSetUtf8 (&g_VBoxVFSClient);
195 if (VBOX_SUCCESS(rc))
196 {
197 /* Fill up VFS user entry points. */
198 static const fs_operation_def_t s_VBoxVFS_vfsops_template[] =
199 {
200 VFSNAME_MOUNT, { .vfs_mount = VBoxVFS_Mount },
201 VFSNAME_UNMOUNT, { .vfs_unmount = VBoxVFS_Unmount },
202 VFSNAME_ROOT, { .vfs_root = VBoxVFS_Root },
203 VFSNAME_STATVFS, { .vfs_statvfs = VBoxVFS_Statfs },
204 VFSNAME_VGET, { .vfs_vget = VBoxVFS_VGet },
205 VFSNAME_FREEVFS, { .vfs_freevfs = VBoxVFS_FreeVFS },
206 NULL, NULL
207 };
208
209 rc = vfs_setfsops(fType, s_VBoxVFS_vfsops_template, &g_pVBoxVFS_vfsops);
210 if (!rc)
211 {
212 /* Set VNode operations. */
213 rc = vn_make_ops(pszName, g_VBoxVFS_vnodeops_template, &g_pVBoxVFS_vnodeops);
214 if (!rc)
215 {
216 /* Get a free major number. */
217 g_VBoxVFSMajor = getudev();
218 if (g_VBoxVFSMajor != (major_t)-1)
219 {
220 /* Initialize minor mutex here. */
221 mutex_init(&g_VBoxVFSMinorMtx, "VBoxVFSMinorMtx", MUTEX_DEFAULT, NULL);
222 LogFlow((DEVICE_NAME ":Successfully loaded vboxvfs.\n"));
223 return 0;
224 }
225 else
226 {
227 LogRel((DEVICE_NAME ":getudev failed.\n"));
228 rc = EMFILE;
229 }
230 }
231 else
232 LogRel((DEVICE_NAME ":vn_make_ops failed. rc=%d\n", rc));
233 }
234 else
235 LogRel((DEVICE_NAME ":vfs_setfsops failed. rc=%d\n", rc));
236 }
237 else
238 {
239 LogRel((DEVICE_NAME ":vboxCallSetUtf8 failed. rc=%d\n", rc));
240 rc = EPROTO;
241 }
242 vboxDisconnect(&g_VBoxVFSClient);
243 }
244 else
245 {
246 LogRel((DEVICE_NAME ":Failed to connect to host! rc=%d\n", rc));
247 rc = ENXIO;
248 }
249 vboxUninit();
250 }
251 else
252 {
253 LogRel((DEVICE_NAME ":Failed to initialize R0 lib. rc=%d\n", rc));
254 rc = ENXIO;
255 }
256 return rc;
257}
258
259static int VBoxVFS_Mount(vfs_t *pVFS, vnode_t *pVNode, struct mounta *pMount, cred_t *pCred)
260{
261 int rc = 0;
262 int Uid = 0;
263 int Gid = 0;
264 char *pszShare = NULL;
265 size_t cbShare = NULL;
266 pathname_t PathName;
267 vboxvfs_vnode_t *pVNodeRoot = NULL;
268 vnode_t *pVNodeSpec = NULL;
269 vnode_t *pVNodeDev = NULL;
270 dev_t Dev = 0;
271 SHFLSTRING *pShflShareName = NULL;
272 RTFSOBJINFO FSInfo;
273 size_t cbShflShareName = 0;
274 vboxvfs_globinfo_t *pVBoxVFSGlobalInfo = NULL;
275 int AddrSpace = (pMount->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE;
276
277 LogFlow((DEVICE_NAME ":VBoxVFS_Mount\n"));
278
279 /* Check user credentials for mounting in the specified target. */
280 rc = secpolicy_fs_mount(pCred, pVNode, pVFS);
281 if (rc)
282 {
283 LogRel((DEVICE_NAME ":VBoxVFS_Mount: secpolicy_fs_mount failed! invalid credentials.rc=%d\n", rc));
284 return EPERM;
285 }
286
287 /* We can mount to only directories. */
288 if (pVNode->v_type != VDIR)
289 return ENOTDIR;
290
291 /* We don't support remounting. */
292 if (pMount->flags & MS_REMOUNT)
293 return ENOTSUP;
294
295 mutex_enter(&pVNode->v_lock);
296 if ( !(pMount->flags & MS_OVERLAY)
297 && (pVNode->v_count > 1 || (pVNode->v_flag & VROOT)))
298 {
299 LogRel((DEVICE_NAME ":VBoxVFS_Mount: device busy.\n"));
300 mutex_exit(&pVNode->v_lock);
301 return EBUSY;
302 }
303 mutex_exit(&pVNode->v_lock);
304
305 /* From what I understood the options are already parsed at a higher level */
306 if ( (pMount->flags & MS_DATA)
307 && pMount->datalen > 0)
308 {
309 LogRel((DEVICE_NAME ":VBoxVFS_Mount: unparsed options not supported.\n"));
310 return EINVAL;
311 }
312
313 /* Get UID argument (optional). */
314 rc = vboxvfs_GetIntOpt(pVFS, MNTOPT_VBOXVFS_UID, &Uid);
315 if (rc < 0)
316 {
317 LogRel((DEVICE_NAME ":VBoxVFS_Mount: invalid uid value.\n"));
318 return EINVAL;
319 }
320
321 /* Get GID argument (optional). */
322 rc = vboxvfs_GetIntOpt(pVFS, MNTOPT_VBOXVFS_GID, &Gid);
323 if (rc < 0)
324 {
325 LogRel((DEVICE_NAME ":VBoxVFS_Mount: invalid gid value.\n"));
326 return EINVAL;
327 }
328
329 /* Get special (sharename). */
330 rc = pn_get(pMount->spec, AddrSpace, &PathName);
331 if (!rc)
332 {
333 /* Get an available minor instance number for this mount. */
334 mutex_enter(&g_VBoxVFSMinorMtx);
335 do
336 {
337 atomic_add_32_nv(&g_VBoxVFSMinor, 1) & L_MAXMIN32;
338 Dev = makedevice(g_VBoxVFSMajor, g_VBoxVFSMinor);
339 } while (vfs_devismounted(Dev));
340 mutex_exit(&g_VBoxVFSMinorMtx);
341
342 cbShare = strlen(PathName.pn_path);
343 pszShare = RTMemAlloc(cbShare);
344 if (pszShare)
345 memcpy(pszShare, PathName.pn_path, cbShare);
346 else
347 {
348 LogRel((DEVICE_NAME ":VBoxVFS_Mount: failed to alloc %d bytes for sharename.\n", cbShare));
349 rc = ENOMEM;
350 }
351 }
352 else
353 {
354 LogRel((DEVICE_NAME ":VBoxVFS_Mount: failed to get sharename.rc=%d\n", rc));
355 rc = EINVAL;
356 }
357 pn_free(&PathName);
358
359 if (rc)
360 {
361 if (pszShare)
362 RTMemFree(pszShare);
363 return rc;
364 }
365
366 /* Allocate the global info. structure. */
367 pVBoxVFSGlobalInfo = RTMemAlloc(sizeof(*pVBoxVFSGlobalInfo));
368 if (pVBoxVFSGlobalInfo)
369 {
370 cbShflShareName = offsetof(SHFLSTRING, String.utf8) + cbShare + 1;
371 pShflShareName = RTMemAllocZ(cbShflShareName);
372 if (pShflShareName)
373 {
374 pShflShareName->u16Length = cbShflShareName;
375 pShflShareName->u16Size = cbShflShareName + 1;
376 memcpy (pShflShareName->String.utf8, pszShare, cbShare + 1);
377
378 rc = vboxCallMapFolder(&g_VBoxVFSClient, pShflShareName, &pVBoxVFSGlobalInfo->Map);
379 RTMemFree(pShflShareName);
380 if (VBOX_SUCCESS(rc))
381 rc = 0;
382 else
383 {
384 LogRel((DEVICE_NAME ":VBoxVFS_Mount: vboxCallMapFolder failed rc=%d\n", rc));
385 rc = EPROTO;
386 }
387 }
388 else
389 {
390 LogRel((DEVICE_NAME ":VBoxVFS_Mount: RTMemAllocZ failed to alloc %d bytes for ShFlShareName.\n", cbShflShareName));
391 rc = ENOMEM;
392 }
393 RTMemFree(pVBoxVFSGlobalInfo);
394 }
395 else
396 {
397 LogRel((DEVICE_NAME ":VBoxVFS_Mount: RTMemAlloc failed to alloc %d bytes for global struct.\n", sizeof(*pVBoxVFSGlobalInfo)));
398 rc = ENOMEM;
399 }
400
401 /* Undo work on failure. */
402 if (rc)
403 goto mntError1;
404
405 /* Initialize the per-filesystem mutex */
406 mutex_init(&pVBoxVFSGlobalInfo->MtxFS, "VBoxVFS_FSMtx", MUTEX_DEFAULT, NULL);
407
408 pVBoxVFSGlobalInfo->Uid = Uid;
409 pVBoxVFSGlobalInfo->Gid = Gid;
410 pVBoxVFSGlobalInfo->pVFS = pVFS;
411 pVFS->vfs_data = pVBoxVFSGlobalInfo;
412 pVFS->vfs_fstype = g_VBoxVFSType;
413 pVFS->vfs_dev = Dev;
414 vfs_make_fsid(&pVFS->vfs_fsid, Dev, g_VBoxVFSType);
415
416 /* Allocate root vboxvfs_vnode_t object */
417 pVNodeRoot = RTMemAlloc(sizeof(*pVNodeRoot));
418 if (pVNodeRoot)
419 {
420 /* Initialize vnode protection mutex */
421 mutex_init(&pVNodeRoot->MtxContents, "VBoxVFS_VNodeMtx", MUTEX_DEFAULT, NULL);
422
423 pVBoxVFSGlobalInfo->pVNodeRoot = pVNodeRoot;
424
425 /* Allocate root path */
426 pVNodeRoot->pPath = RTMemAllocZ(sizeof(SHFLSTRING) + 1);
427 if (pVNodeRoot->pPath)
428 {
429 /* Initialize root path */
430 pVNodeRoot->pPath->u16Length = 1;
431 pVNodeRoot->pPath->u16Size = 2;
432 pVNodeRoot->pPath->String.utf8[0] = '/';
433 pVNodeRoot->pPath->String.utf8[1] = '\0';
434
435 /* Stat root node info from host */
436 rc = vboxvfs_Stat(__func__, pVBoxVFSGlobalInfo, pVNodeRoot->pPath, &FSInfo, B_FALSE);
437 if (!rc)
438 {
439 /* Initialize the root vboxvfs_node_t object */
440 vboxvfs_InitVNode(pVBoxVFSGlobalInfo, pVNodeRoot, &FSInfo);
441
442 /* Success! */
443 LogFlow((DEVICE_NAME ":VBoxVFS_Mount: success!\n", rc));
444 return 0;
445 }
446 else
447 LogRel((DEVICE_NAME ":VBoxVFS_Mount: vboxvfs_Stat failed rc(errno)=%d\n", rc));
448 RTMemFree(pVNodeRoot->pPath);
449 }
450 else
451 {
452 LogRel((DEVICE_NAME ":VBoxVFS_Mount: failed to alloc memory for root node path.\n"));
453 rc = ENOMEM;
454 }
455 RTMemFree(pVNodeRoot);
456 }
457 else
458 {
459 LogRel((DEVICE_NAME ":VBoxVFS_Mount: failed to alloc memory for root node.\n"));
460 rc = ENOMEM;
461 }
462
463 /* Undo work in reverse. */
464mntError1:
465 RTMemFree(pszShare);
466 return rc;
467}
468
469static int VBoxVFS_Unmount(vfs_t *pVFS, int fUnmount, cred_t *pCred)
470{
471 int rc;
472 vboxvfs_globinfo_t *pVBoxVFSGlobalInfo;
473
474 LogFlow((DEVICE_NAME ":VBoxVFS_Unmount.\n"));
475
476 /* Check if user can unmount. */
477 rc = secpolicy_fs_unmount(pCred, pVFS);
478 if (rc)
479 {
480 LogRel((DEVICE_NAME ":VBoxVFS_Unmount: insufficient privileges to unmount.rc=%d\n", rc));
481 return EPERM;
482 }
483
484 /* @todo -XXX - Not sure of supporting force unmounts. What this means is that a failed force mount could bring down
485 * the entire system as hanging about vnode releases would no longer be valid after unloading ourselves...
486 */
487 if (fUnmount & MS_FORCE)
488 pVFS->vfs_flag |= VFS_UNMOUNTED;
489
490 /* @todo implement ref-counting of active vnodes & check for busy state here. */
491 /* @todo mutex protection needed here */
492 pVBoxVFSGlobalInfo = VFS_TO_VBOXVFS(pVFS);
493
494 rc = vboxCallUnmapFolder(&g_VBoxVFSClient, &pVBoxVFSGlobalInfo->Map);
495 if (VBOX_FAILURE(rc))
496 LogRel((DEVICE_NAME ":VBoxVFS_Unmount: failed to unmap shared folder. rc=%d\n", rc));
497
498 VN_RELE(VBOXVN_TO_VN(pVBoxVFSGlobalInfo->pVNodeRoot));
499
500 RTMemFree(pVBoxVFSGlobalInfo->pVNodeRoot->pPath);
501 RTMemFree(pVBoxVFSGlobalInfo->pVNodeRoot);
502 RTMemFree(pVBoxVFSGlobalInfo);
503 pVBoxVFSGlobalInfo = NULL;
504 pVFS->vfs_data = NULL;
505
506 return 0;
507}
508
509static int VBoxVFS_Root(vfs_t *pVFS, vnode_t **ppVNode)
510{
511 vboxvfs_globinfo_t *pVBoxVFSGlobalInfo = VFS_TO_VBOXVFS(pVFS);
512 *ppVNode = VBOXVN_TO_VN(pVBoxVFSGlobalInfo->pVNodeRoot);
513 VN_HOLD(*ppVNode);
514
515 return 0;
516}
517
518static int VBoxVFS_Statfs(register vfs_t *pVFS, struct statvfs64 *pStat)
519{
520 SHFLVOLINFO VolumeInfo;
521 uint32_t cbBuffer;
522 vboxvfs_globinfo_t *pVBoxVFSGlobalInfo;
523 dev32_t Dev32;
524 int rc;
525
526 pVBoxVFSGlobalInfo = VFS_TO_VBOXVFS(pVFS);
527 cbBuffer = sizeof(VolumeInfo);
528 rc = vboxCallFSInfo(&g_VBoxVFSClient, &pVBoxVFSGlobalInfo->Map, 0, SHFL_INFO_GET | SHFL_INFO_VOLUME, &cbBuffer,
529 (PSHFLDIRINFO)&VolumeInfo);
530 if (VBOX_FAILURE(rc))
531 return RTErrConvertToErrno(rc);
532
533 bzero(pStat, sizeof(*pStat));
534 cmpldev(&Dev32, pVFS->vfs_dev);
535 pStat->f_fsid = Dev32;
536 pStat->f_flag = vf_to_stf(pVFS->vfs_flag);
537 pStat->f_bsize = VolumeInfo.ulBytesPerAllocationUnit;
538 pStat->f_frsize = VolumeInfo.ulBytesPerAllocationUnit;
539 pStat->f_bfree = VolumeInfo.ullAvailableAllocationBytes / VolumeInfo.ulBytesPerAllocationUnit;
540 pStat->f_bavail = VolumeInfo.ullAvailableAllocationBytes / VolumeInfo.ulBytesPerAllocationUnit;
541 pStat->f_blocks = VolumeInfo.ullTotalAllocationBytes / VolumeInfo.ulBytesPerAllocationUnit;
542 pStat->f_files = 1000;
543 pStat->f_ffree = 1000; /* don't return 0 here since the guest may think that it is not possible to create any more files */
544 pStat->f_namemax = 255; /* @todo is this correct?? */
545
546 strlcpy(pStat->f_basetype, vfssw[pVFS->vfs_fstype].vsw_name, sizeof(pStat->f_basetype));
547 strlcpy(pStat->f_fstr, DEVICE_NAME, sizeof(pStat->f_fstr));
548
549 return 0;
550}
551
552static int VBoxVFS_VGet(vfs_t *pVFS, vnode_t **ppVNode, struct fid *pFid)
553{
554 /* -- TODO -- */
555 return 0;
556}
557
558static void VBoxVFS_FreeVFS(vfs_t *pVFS)
559{
560 vboxDisconnect(&g_VBoxVFSClient);
561 vboxUninit();
562}
563
564static int vboxvfs_CheckMountPerm(vfs_t *pVFS, struct mounta *pMount, vnode_t *pVNodeSpec, cred_t *pCred)
565{
566 /* Check if user has the rights to mount the special file. */
567 int fOpen = FREAD | FWRITE;
568 int fAccess = VREAD | VWRITE;
569 int rc;
570
571 if (pVNodeSpec->v_type != VBLK)
572 return ENOTBLK;
573
574 if ( (pVFS->vfs_flag & VFS_RDONLY)
575 || (pMount->flags & MS_RDONLY))
576 {
577 fOpen = FREAD;
578 fAccess = VREAD;
579 }
580
581 rc = VOP_ACCESS(pVNodeSpec, fAccess, 0, pCred, NULL /* caller_context */);
582 if (!rc)
583 rc = secpolicy_spec_open(pCred, pVNodeSpec, fOpen);
584
585 return rc;
586}
587
588static int vboxvfs_GetIntOpt(vfs_t *pVFS, char *pszOpt, int *pValue)
589{
590 int rc;
591 long Val;
592 char *pchOpt = NULL;
593 char *pchEnd = NULL;
594
595 rc = vfs_optionisset(pVFS, pszOpt, &pchOpt);
596 if (rc)
597 {
598 rc = ddi_strtol(pchOpt, &pchEnd, 10 /* base */, &Val);
599 if ( !rc
600 && Val > INT_MIN
601 && Val < INT_MAX
602 && pchEnd == pchOpt + strlen(pchOpt))
603 {
604 *pValue = (int)Val;
605 return 0;
606 }
607 return -1;
608 }
609 return 1;
610}
611
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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