1 | /* $NetBSD: ntfs_vfsops.c,v 1.104 2015/03/28 19:24:05 maxv Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 1999 Semen Ustimenko |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. |
27 | * |
28 | * Id: ntfs_vfsops.c,v 1.7 1999/05/31 11:28:30 phk Exp |
29 | */ |
30 | |
31 | #include <sys/cdefs.h> |
32 | __KERNEL_RCSID(0, "$NetBSD: ntfs_vfsops.c,v 1.104 2015/03/28 19:24:05 maxv Exp $" ); |
33 | |
34 | #include <sys/param.h> |
35 | #include <sys/systm.h> |
36 | #include <sys/namei.h> |
37 | #include <sys/proc.h> |
38 | #include <sys/kernel.h> |
39 | #include <sys/vnode.h> |
40 | #include <sys/mount.h> |
41 | #include <sys/buf.h> |
42 | #include <sys/fcntl.h> |
43 | #include <sys/malloc.h> |
44 | #include <sys/sysctl.h> |
45 | #include <sys/device.h> |
46 | #include <sys/conf.h> |
47 | #include <sys/kauth.h> |
48 | #include <sys/module.h> |
49 | |
50 | #include <uvm/uvm_extern.h> |
51 | |
52 | #include <miscfs/genfs/genfs.h> |
53 | #include <miscfs/specfs/specdev.h> |
54 | |
55 | #include <fs/ntfs/ntfs.h> |
56 | #include <fs/ntfs/ntfs_inode.h> |
57 | #include <fs/ntfs/ntfs_subr.h> |
58 | #include <fs/ntfs/ntfs_vfsops.h> |
59 | #include <fs/ntfs/ntfs_ihash.h> |
60 | #include <fs/ntfs/ntfsmount.h> |
61 | |
62 | MODULE(MODULE_CLASS_VFS, ntfs, NULL); |
63 | |
64 | MALLOC_JUSTDEFINE(M_NTFSMNT, "NTFS mount" , "NTFS mount structure" ); |
65 | MALLOC_JUSTDEFINE(M_NTFSNTNODE,"NTFS ntnode" , "NTFS ntnode information" ); |
66 | MALLOC_JUSTDEFINE(M_NTFSDIR,"NTFS dir" , "NTFS dir buffer" ); |
67 | |
68 | static int ntfs_superblock_validate(struct ntfsmount *); |
69 | static int ntfs_mount(struct mount *, const char *, void *, size_t *); |
70 | static int ntfs_root(struct mount *, struct vnode **); |
71 | static int ntfs_start(struct mount *, int); |
72 | static int ntfs_statvfs(struct mount *, struct statvfs *); |
73 | static int ntfs_sync(struct mount *, int, kauth_cred_t); |
74 | static int ntfs_unmount(struct mount *, int); |
75 | static int ntfs_vget(struct mount *mp, ino_t ino, |
76 | struct vnode **vpp); |
77 | static int ntfs_loadvnode(struct mount *, struct vnode *, |
78 | const void *, size_t, const void **); |
79 | static int ntfs_mountfs(struct vnode *, struct mount *, |
80 | struct ntfs_args *, struct lwp *); |
81 | static int ntfs_vptofh(struct vnode *, struct fid *, size_t *); |
82 | |
83 | static void ntfs_init(void); |
84 | static void ntfs_reinit(void); |
85 | static void ntfs_done(void); |
86 | static int ntfs_fhtovp(struct mount *, struct fid *, |
87 | struct vnode **); |
88 | static int ntfs_mountroot(void); |
89 | |
90 | static const struct genfs_ops ntfs_genfsops = { |
91 | .gop_write = genfs_compat_gop_write, |
92 | }; |
93 | |
94 | static struct sysctllog *ntfs_sysctl_log; |
95 | |
96 | static int |
97 | ntfs_mountroot(void) |
98 | { |
99 | struct mount *mp; |
100 | struct lwp *l = curlwp; /* XXX */ |
101 | int error; |
102 | struct ntfs_args args; |
103 | |
104 | if (device_class(root_device) != DV_DISK) |
105 | return (ENODEV); |
106 | |
107 | if ((error = vfs_rootmountalloc(MOUNT_NTFS, "root_device" , &mp))) { |
108 | vrele(rootvp); |
109 | return (error); |
110 | } |
111 | |
112 | args.flag = 0; |
113 | args.uid = 0; |
114 | args.gid = 0; |
115 | args.mode = 0777; |
116 | |
117 | if ((error = ntfs_mountfs(rootvp, mp, &args, l)) != 0) { |
118 | vfs_unbusy(mp, false, NULL); |
119 | vfs_destroy(mp); |
120 | return (error); |
121 | } |
122 | |
123 | mountlist_append(mp); |
124 | (void)ntfs_statvfs(mp, &mp->mnt_stat); |
125 | vfs_unbusy(mp, false, NULL); |
126 | return (0); |
127 | } |
128 | |
129 | static void |
130 | ntfs_init(void) |
131 | { |
132 | |
133 | malloc_type_attach(M_NTFSMNT); |
134 | malloc_type_attach(M_NTFSNTNODE); |
135 | malloc_type_attach(M_NTFSDIR); |
136 | malloc_type_attach(M_NTFSNTVATTR); |
137 | malloc_type_attach(M_NTFSRDATA); |
138 | malloc_type_attach(M_NTFSDECOMP); |
139 | malloc_type_attach(M_NTFSRUN); |
140 | ntfs_nthashinit(); |
141 | ntfs_toupper_init(); |
142 | } |
143 | |
144 | static void |
145 | ntfs_reinit(void) |
146 | { |
147 | ntfs_nthashreinit(); |
148 | } |
149 | |
150 | static void |
151 | ntfs_done(void) |
152 | { |
153 | ntfs_nthashdone(); |
154 | malloc_type_detach(M_NTFSMNT); |
155 | malloc_type_detach(M_NTFSNTNODE); |
156 | malloc_type_detach(M_NTFSDIR); |
157 | malloc_type_detach(M_NTFSNTVATTR); |
158 | malloc_type_detach(M_NTFSRDATA); |
159 | malloc_type_detach(M_NTFSDECOMP); |
160 | malloc_type_detach(M_NTFSRUN); |
161 | } |
162 | |
163 | static int |
164 | ntfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) |
165 | { |
166 | struct lwp *l = curlwp; |
167 | int err = 0, flags; |
168 | struct vnode *devvp; |
169 | struct ntfs_args *args = data; |
170 | |
171 | if (args == NULL) |
172 | return EINVAL; |
173 | if (*data_len < sizeof *args) |
174 | return EINVAL; |
175 | |
176 | if (mp->mnt_flag & MNT_GETARGS) { |
177 | struct ntfsmount *ntmp = VFSTONTFS(mp); |
178 | if (ntmp == NULL) |
179 | return EIO; |
180 | args->fspec = NULL; |
181 | args->uid = ntmp->ntm_uid; |
182 | args->gid = ntmp->ntm_gid; |
183 | args->mode = ntmp->ntm_mode; |
184 | args->flag = ntmp->ntm_flag; |
185 | *data_len = sizeof *args; |
186 | return 0; |
187 | } |
188 | /* |
189 | *** |
190 | * Mounting non-root file system or updating a file system |
191 | *** |
192 | */ |
193 | |
194 | /* |
195 | * If updating, check whether changing from read-only to |
196 | * read/write; if there is no device name, that's all we do. |
197 | */ |
198 | if (mp->mnt_flag & MNT_UPDATE) { |
199 | printf("ntfs_mount(): MNT_UPDATE not supported\n" ); |
200 | return (EINVAL); |
201 | } |
202 | |
203 | /* |
204 | * Not an update, or updating the name: look up the name |
205 | * and verify that it refers to a sensible block device. |
206 | */ |
207 | err = namei_simple_user(args->fspec, |
208 | NSM_FOLLOW_NOEMULROOT, &devvp); |
209 | if (err) |
210 | return (err); |
211 | |
212 | if (devvp->v_type != VBLK) { |
213 | err = ENOTBLK; |
214 | goto fail; |
215 | } |
216 | if (bdevsw_lookup(devvp->v_rdev) == NULL) { |
217 | err = ENXIO; |
218 | goto fail; |
219 | } |
220 | if (mp->mnt_flag & MNT_UPDATE) { |
221 | #if 0 |
222 | /* |
223 | ******************** |
224 | * UPDATE |
225 | ******************** |
226 | */ |
227 | |
228 | if (devvp != ntmp->um_devvp) { |
229 | err = EINVAL; /* needs translation */ |
230 | goto fail; |
231 | } |
232 | |
233 | /* |
234 | * Update device name only on success |
235 | */ |
236 | err = set_statvfs_info(NULL, UIO_USERSPACE, args->fspec, |
237 | UIO_USERSPACE, mp->mnt_op->vfs_name, mp, p); |
238 | if (err) |
239 | goto fail; |
240 | |
241 | vrele(devvp); |
242 | #endif |
243 | } else { |
244 | /* |
245 | ******************** |
246 | * NEW MOUNT |
247 | ******************** |
248 | */ |
249 | |
250 | /* |
251 | * Since this is a new mount, we want the names for |
252 | * the device and the mount point copied in. If an |
253 | * error occurs, the mountpoint is discarded by the |
254 | * upper level code. |
255 | */ |
256 | |
257 | /* Save "last mounted on" info for mount point (NULL pad)*/ |
258 | err = set_statvfs_info(path, UIO_USERSPACE, args->fspec, |
259 | UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l); |
260 | if (err) |
261 | goto fail; |
262 | |
263 | if (mp->mnt_flag & MNT_RDONLY) |
264 | flags = FREAD; |
265 | else |
266 | flags = FREAD|FWRITE; |
267 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); |
268 | err = VOP_OPEN(devvp, flags, FSCRED); |
269 | VOP_UNLOCK(devvp); |
270 | if (err) |
271 | goto fail; |
272 | err = ntfs_mountfs(devvp, mp, args, l); |
273 | if (err) { |
274 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); |
275 | (void)VOP_CLOSE(devvp, flags, NOCRED); |
276 | VOP_UNLOCK(devvp); |
277 | goto fail; |
278 | } |
279 | } |
280 | |
281 | /* |
282 | * Initialize FS stat information in mount struct; uses both |
283 | * mp->mnt_stat.f_mntonname and mp->mnt_stat.f_mntfromname |
284 | * |
285 | * This code is common to root and non-root mounts |
286 | */ |
287 | (void)VFS_STATVFS(mp, &mp->mnt_stat); |
288 | return (err); |
289 | |
290 | fail: |
291 | vrele(devvp); |
292 | return (err); |
293 | } |
294 | |
295 | static int |
296 | ntfs_superblock_validate(struct ntfsmount *ntmp) |
297 | { |
298 | /* Sanity checks. XXX: More checks are probably needed. */ |
299 | if (strncmp(ntmp->ntm_bootfile.bf_sysid, NTFS_BBID, NTFS_BBIDLEN)) { |
300 | dprintf(("ntfs_superblock_validate: invalid boot block\n" )); |
301 | return EINVAL; |
302 | } |
303 | if (ntmp->ntm_bps == 0) { |
304 | dprintf(("ntfs_superblock_validate: invalid bytes per sector\n" )); |
305 | return EINVAL; |
306 | } |
307 | if (ntmp->ntm_spc == 0) { |
308 | dprintf(("ntfs_superblock_validate: invalid sectors per cluster\n" )); |
309 | return EINVAL; |
310 | } |
311 | return 0; |
312 | } |
313 | |
314 | /* |
315 | * Common code for mount and mountroot |
316 | */ |
317 | int |
318 | ntfs_mountfs(struct vnode *devvp, struct mount *mp, struct ntfs_args *argsp, struct lwp *l) |
319 | { |
320 | struct buf *bp; |
321 | struct ntfsmount *ntmp; |
322 | dev_t dev = devvp->v_rdev; |
323 | int error, i; |
324 | struct vnode *vp; |
325 | |
326 | ntmp = NULL; |
327 | |
328 | /* |
329 | * Flush out any old buffers remaining from a previous use. |
330 | */ |
331 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); |
332 | error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0); |
333 | VOP_UNLOCK(devvp); |
334 | if (error) |
335 | return (error); |
336 | |
337 | bp = NULL; |
338 | |
339 | error = bread(devvp, BBLOCK, BBSIZE, 0, &bp); |
340 | if (error) |
341 | goto out; |
342 | ntmp = malloc(sizeof(*ntmp), M_NTFSMNT, M_WAITOK|M_ZERO); |
343 | memcpy(&ntmp->ntm_bootfile, bp->b_data, sizeof(struct bootfile)); |
344 | brelse(bp, 0); |
345 | bp = NULL; |
346 | |
347 | if ((error = ntfs_superblock_validate(ntmp))) |
348 | goto out; |
349 | |
350 | { |
351 | int8_t cpr = ntmp->ntm_mftrecsz; |
352 | if (cpr > 0) |
353 | ntmp->ntm_bpmftrec = ntmp->ntm_spc * cpr; |
354 | else |
355 | ntmp->ntm_bpmftrec = (1 << (-cpr)) / ntmp->ntm_bps; |
356 | } |
357 | dprintf(("ntfs_mountfs(): bps: %d, spc: %d, media: %x, mftrecsz: %d (%d sects)\n" , |
358 | ntmp->ntm_bps, ntmp->ntm_spc, ntmp->ntm_bootfile.bf_media, |
359 | ntmp->ntm_mftrecsz, ntmp->ntm_bpmftrec)); |
360 | dprintf(("ntfs_mountfs(): mftcn: 0x%x|0x%x\n" , |
361 | (u_int32_t)ntmp->ntm_mftcn, (u_int32_t)ntmp->ntm_mftmirrcn)); |
362 | |
363 | ntmp->ntm_mountp = mp; |
364 | ntmp->ntm_dev = dev; |
365 | ntmp->ntm_devvp = devvp; |
366 | ntmp->ntm_uid = argsp->uid; |
367 | ntmp->ntm_gid = argsp->gid; |
368 | ntmp->ntm_mode = argsp->mode; |
369 | ntmp->ntm_flag = argsp->flag; |
370 | mp->mnt_data = ntmp; |
371 | |
372 | /* set file name encode/decode hooks XXX utf-8 only for now */ |
373 | ntmp->ntm_wget = ntfs_utf8_wget; |
374 | ntmp->ntm_wput = ntfs_utf8_wput; |
375 | ntmp->ntm_wcmp = ntfs_utf8_wcmp; |
376 | |
377 | dprintf(("ntfs_mountfs(): case-%s,%s uid: %d, gid: %d, mode: %o\n" , |
378 | (ntmp->ntm_flag & NTFS_MFLAG_CASEINS)?"insens." :"sens." , |
379 | (ntmp->ntm_flag & NTFS_MFLAG_ALLNAMES)?" allnames," :"" , |
380 | ntmp->ntm_uid, ntmp->ntm_gid, ntmp->ntm_mode)); |
381 | |
382 | /* |
383 | * We read in some system nodes to do not allow |
384 | * reclaim them and to have everytime access to them. |
385 | */ |
386 | { |
387 | int pi[3] = { NTFS_MFTINO, NTFS_ROOTINO, NTFS_BITMAPINO }; |
388 | for (i = 0; i < 3; i++) { |
389 | error = VFS_VGET(mp, pi[i], &(ntmp->ntm_sysvn[pi[i]])); |
390 | if (error) |
391 | goto out1; |
392 | ntmp->ntm_sysvn[pi[i]]->v_vflag |= VV_SYSTEM; |
393 | vref(ntmp->ntm_sysvn[pi[i]]); |
394 | vput(ntmp->ntm_sysvn[pi[i]]); |
395 | } |
396 | } |
397 | |
398 | /* read the Unicode lowercase --> uppercase translation table, |
399 | * if necessary */ |
400 | if ((error = ntfs_toupper_use(mp, ntmp))) |
401 | goto out1; |
402 | |
403 | /* |
404 | * Scan $BitMap and count free clusters |
405 | */ |
406 | error = ntfs_calccfree(ntmp, &ntmp->ntm_cfree); |
407 | if (error) |
408 | goto out1; |
409 | |
410 | /* |
411 | * Read and translate to internal format attribute |
412 | * definition file. |
413 | */ |
414 | { |
415 | int num,j; |
416 | struct attrdef ad; |
417 | |
418 | /* Open $AttrDef */ |
419 | error = VFS_VGET(mp, NTFS_ATTRDEFINO, &vp); |
420 | if (error) |
421 | goto out1; |
422 | |
423 | /* Count valid entries */ |
424 | for (num = 0; ; num++) { |
425 | error = ntfs_readattr(ntmp, VTONT(vp), |
426 | NTFS_A_DATA, NULL, |
427 | num * sizeof(ad), sizeof(ad), |
428 | &ad, NULL); |
429 | if (error) |
430 | goto out1; |
431 | if (ad.ad_name[0] == 0) |
432 | break; |
433 | } |
434 | |
435 | /* Alloc memory for attribute definitions */ |
436 | ntmp->ntm_ad = (struct ntvattrdef *) malloc( |
437 | num * sizeof(struct ntvattrdef), |
438 | M_NTFSMNT, M_WAITOK); |
439 | |
440 | ntmp->ntm_adnum = num; |
441 | |
442 | /* Read them and translate */ |
443 | for (i = 0; i < num; i++) { |
444 | error = ntfs_readattr(ntmp, VTONT(vp), |
445 | NTFS_A_DATA, NULL, |
446 | i * sizeof(ad), sizeof(ad), |
447 | &ad, NULL); |
448 | if (error) |
449 | goto out1; |
450 | j = 0; |
451 | do { |
452 | ntmp->ntm_ad[i].ad_name[j] = ad.ad_name[j]; |
453 | } while(ad.ad_name[j++]); |
454 | ntmp->ntm_ad[i].ad_namelen = j - 1; |
455 | ntmp->ntm_ad[i].ad_type = ad.ad_type; |
456 | } |
457 | |
458 | vput(vp); |
459 | } |
460 | |
461 | mp->mnt_stat.f_fsidx.__fsid_val[0] = dev; |
462 | mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_NTFS); |
463 | mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; |
464 | mp->mnt_stat.f_namemax = NTFS_MAXFILENAME; |
465 | mp->mnt_flag |= MNT_LOCAL; |
466 | spec_node_setmountedfs(devvp, mp); |
467 | return (0); |
468 | |
469 | out1: |
470 | for (i = 0; i < NTFS_SYSNODESNUM; i++) |
471 | if (ntmp->ntm_sysvn[i]) |
472 | vrele(ntmp->ntm_sysvn[i]); |
473 | |
474 | if (vflush(mp, NULLVP, 0)) { |
475 | dprintf(("ntfs_mountfs: vflush failed\n" )); |
476 | } |
477 | out: |
478 | spec_node_setmountedfs(devvp, NULL); |
479 | if (bp) |
480 | brelse(bp, 0); |
481 | |
482 | if (error) { |
483 | if (ntmp) { |
484 | if (ntmp->ntm_ad) |
485 | free(ntmp->ntm_ad, M_NTFSMNT); |
486 | free(ntmp, M_NTFSMNT); |
487 | } |
488 | } |
489 | |
490 | return (error); |
491 | } |
492 | |
493 | static int |
494 | ntfs_start(struct mount *mp, int flags) |
495 | { |
496 | return (0); |
497 | } |
498 | |
499 | static int |
500 | ntfs_unmount(struct mount *mp, int mntflags) |
501 | { |
502 | struct lwp *l = curlwp; |
503 | struct ntfsmount *ntmp; |
504 | int error, ronly = 0, flags, i; |
505 | |
506 | dprintf(("ntfs_unmount: unmounting...\n" )); |
507 | ntmp = VFSTONTFS(mp); |
508 | |
509 | flags = 0; |
510 | if (mntflags & MNT_FORCE) |
511 | flags |= FORCECLOSE; |
512 | |
513 | dprintf(("ntfs_unmount: vflushing...\n" )); |
514 | error = vflush(mp, NULLVP, flags | SKIPSYSTEM); |
515 | if (error) { |
516 | dprintf(("ntfs_unmount: vflush failed: %d\n" ,error)); |
517 | return (error); |
518 | } |
519 | |
520 | /* Check if only system vnodes are rest */ |
521 | for (i = 0; i < NTFS_SYSNODESNUM; i++) |
522 | if ((ntmp->ntm_sysvn[i]) && |
523 | (ntmp->ntm_sysvn[i]->v_usecount > 1)) |
524 | return (EBUSY); |
525 | |
526 | /* Dereference all system vnodes */ |
527 | for (i = 0; i < NTFS_SYSNODESNUM; i++) |
528 | if (ntmp->ntm_sysvn[i]) |
529 | vrele(ntmp->ntm_sysvn[i]); |
530 | |
531 | /* vflush system vnodes */ |
532 | error = vflush(mp, NULLVP, flags); |
533 | if (error) { |
534 | panic("ntfs_unmount: vflush failed(sysnodes): %d\n" ,error); |
535 | } |
536 | |
537 | /* Check if the type of device node isn't VBAD before |
538 | * touching v_specinfo. If the device vnode is revoked, the |
539 | * field is NULL and touching it causes null pointer derefercence. |
540 | */ |
541 | if (ntmp->ntm_devvp->v_type != VBAD) |
542 | spec_node_setmountedfs(ntmp->ntm_devvp, NULL); |
543 | |
544 | error = vinvalbuf(ntmp->ntm_devvp, V_SAVE, NOCRED, l, 0, 0); |
545 | KASSERT(error == 0); |
546 | |
547 | /* lock the device vnode before calling VOP_CLOSE() */ |
548 | vn_lock(ntmp->ntm_devvp, LK_EXCLUSIVE | LK_RETRY); |
549 | error = VOP_CLOSE(ntmp->ntm_devvp, ronly ? FREAD : FREAD|FWRITE, |
550 | NOCRED); |
551 | KASSERT(error == 0); |
552 | VOP_UNLOCK(ntmp->ntm_devvp); |
553 | |
554 | vrele(ntmp->ntm_devvp); |
555 | |
556 | /* free the toupper table, if this has been last mounted ntfs volume */ |
557 | ntfs_toupper_unuse(); |
558 | |
559 | dprintf(("ntfs_umount: freeing memory...\n" )); |
560 | mp->mnt_data = NULL; |
561 | mp->mnt_flag &= ~MNT_LOCAL; |
562 | free(ntmp->ntm_ad, M_NTFSMNT); |
563 | free(ntmp, M_NTFSMNT); |
564 | return (0); |
565 | } |
566 | |
567 | static int |
568 | ntfs_root(struct mount *mp, struct vnode **vpp) |
569 | { |
570 | struct vnode *nvp; |
571 | int error = 0; |
572 | |
573 | dprintf(("ntfs_root(): sysvn: %p\n" , |
574 | VFSTONTFS(mp)->ntm_sysvn[NTFS_ROOTINO])); |
575 | error = VFS_VGET(mp, (ino_t)NTFS_ROOTINO, &nvp); |
576 | if (error) { |
577 | printf("ntfs_root: VFS_VGET failed: %d\n" , error); |
578 | return (error); |
579 | } |
580 | |
581 | *vpp = nvp; |
582 | return (0); |
583 | } |
584 | |
585 | int |
586 | ntfs_calccfree(struct ntfsmount *ntmp, cn_t *cfreep) |
587 | { |
588 | struct vnode *vp; |
589 | u_int8_t *tmp; |
590 | int j, error; |
591 | cn_t cfree = 0; |
592 | size_t bmsize, i; |
593 | |
594 | vp = ntmp->ntm_sysvn[NTFS_BITMAPINO]; |
595 | bmsize = VTOF(vp)->f_size; |
596 | tmp = (u_int8_t *) malloc(bmsize, M_TEMP, M_WAITOK); |
597 | |
598 | error = ntfs_readattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL, |
599 | 0, bmsize, tmp, NULL); |
600 | if (error) |
601 | goto out; |
602 | |
603 | for (i = 0; i < bmsize; i++) |
604 | for (j = 0; j < 8; j++) |
605 | if (~tmp[i] & (1 << j)) |
606 | cfree++; |
607 | *cfreep = cfree; |
608 | |
609 | out: |
610 | free(tmp, M_TEMP); |
611 | return(error); |
612 | } |
613 | |
614 | static int |
615 | ntfs_statvfs(struct mount *mp, struct statvfs *sbp) |
616 | { |
617 | struct ntfsmount *ntmp = VFSTONTFS(mp); |
618 | u_int64_t mftallocated; |
619 | |
620 | dprintf(("ntfs_statvfs():\n" )); |
621 | |
622 | mftallocated = VTOF(ntmp->ntm_sysvn[NTFS_MFTINO])->f_allocated; |
623 | |
624 | sbp->f_bsize = ntmp->ntm_bps; |
625 | sbp->f_frsize = sbp->f_bsize; /* XXX */ |
626 | sbp->f_iosize = ntmp->ntm_bps * ntmp->ntm_spc; |
627 | sbp->f_blocks = ntmp->ntm_bootfile.bf_spv; |
628 | sbp->f_bfree = sbp->f_bavail = ntfs_cntobn(ntmp->ntm_cfree); |
629 | sbp->f_ffree = sbp->f_favail = sbp->f_bfree / ntmp->ntm_bpmftrec; |
630 | sbp->f_files = mftallocated / ntfs_bntob(ntmp->ntm_bpmftrec) + |
631 | sbp->f_ffree; |
632 | sbp->f_fresvd = sbp->f_bresvd = 0; /* XXX */ |
633 | sbp->f_flag = mp->mnt_flag; |
634 | copy_statvfs_info(sbp, mp); |
635 | return (0); |
636 | } |
637 | |
638 | static int |
639 | ntfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) |
640 | { |
641 | /*dprintf(("ntfs_sync():\n"));*/ |
642 | return (0); |
643 | } |
644 | |
645 | /*ARGSUSED*/ |
646 | static int |
647 | ntfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) |
648 | { |
649 | struct ntfid ntfh; |
650 | int error; |
651 | |
652 | if (fhp->fid_len != sizeof(struct ntfid)) |
653 | return EINVAL; |
654 | memcpy(&ntfh, fhp, sizeof(ntfh)); |
655 | ddprintf(("ntfs_fhtovp(): %s: %llu\n" , mp->mnt_stat.f_mntonname, |
656 | (unsigned long long)ntfh.ntfid_ino)); |
657 | |
658 | error = ntfs_vgetex(mp, ntfh.ntfid_ino, ntfh.ntfid_attr, "" , |
659 | LK_EXCLUSIVE, vpp); |
660 | if (error != 0) { |
661 | *vpp = NULLVP; |
662 | return (error); |
663 | } |
664 | |
665 | /* XXX as unlink/rmdir/mkdir/creat are not currently possible |
666 | * with NTFS, we don't need to check anything else for now */ |
667 | return (0); |
668 | } |
669 | |
670 | static int |
671 | ntfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) |
672 | { |
673 | struct ntnode *ntp; |
674 | struct ntfid ntfh; |
675 | struct fnode *fn; |
676 | |
677 | if (*fh_size < sizeof(struct ntfid)) { |
678 | *fh_size = sizeof(struct ntfid); |
679 | return E2BIG; |
680 | } |
681 | *fh_size = sizeof(struct ntfid); |
682 | |
683 | ddprintf(("ntfs_fhtovp(): %s: %p\n" , vp->v_mount->mnt_stat.f_mntonname, |
684 | vp)); |
685 | |
686 | fn = VTOF(vp); |
687 | ntp = VTONT(vp); |
688 | memset(&ntfh, 0, sizeof(ntfh)); |
689 | ntfh.ntfid_len = sizeof(struct ntfid); |
690 | ntfh.ntfid_ino = ntp->i_number; |
691 | ntfh.ntfid_attr = fn->f_attrtype; |
692 | #ifdef notyet |
693 | ntfh.ntfid_gen = ntp->i_gen; |
694 | #endif |
695 | memcpy(fhp, &ntfh, sizeof(ntfh)); |
696 | return (0); |
697 | } |
698 | |
699 | static int |
700 | ntfs_loadvnode(struct mount *mp, struct vnode *vp, |
701 | const void *key, size_t key_len, const void **new_key) |
702 | { |
703 | int error; |
704 | struct ntvattr *vap; |
705 | struct ntkey small_key, *ntkey; |
706 | struct ntfsmount *ntmp; |
707 | struct ntnode *ip; |
708 | struct fnode *fp = NULL; |
709 | enum vtype f_type = VBAD; |
710 | |
711 | if (key_len <= sizeof(small_key)) |
712 | ntkey = &small_key; |
713 | else |
714 | ntkey = kmem_alloc(key_len, KM_SLEEP); |
715 | memcpy(ntkey, key, key_len); |
716 | |
717 | dprintf(("ntfs_loadvnode: ino: %llu, attr: 0x%x:%s" , |
718 | (unsigned long long)ntkey->k_ino, |
719 | ntkey->k_attrtype, ntkey->k_attrname)); |
720 | |
721 | ntmp = VFSTONTFS(mp); |
722 | |
723 | /* Get ntnode */ |
724 | error = ntfs_ntlookup(ntmp, ntkey->k_ino, &ip); |
725 | if (error) { |
726 | printf("ntfs_loadvnode: ntfs_ntget failed\n" ); |
727 | goto out; |
728 | } |
729 | /* It may be not initialized fully, so force load it */ |
730 | if (!(ip->i_flag & IN_LOADED)) { |
731 | error = ntfs_loadntnode(ntmp, ip); |
732 | if (error) { |
733 | printf("ntfs_loadvnode: CAN'T LOAD ATTRIBUTES FOR INO:" |
734 | " %llu\n" , (unsigned long long)ip->i_number); |
735 | ntfs_ntput(ip); |
736 | goto out; |
737 | } |
738 | } |
739 | |
740 | /* Setup fnode */ |
741 | fp = kmem_zalloc(sizeof(*fp), KM_SLEEP); |
742 | dprintf(("%s: allocating fnode: %p\n" , __func__, fp)); |
743 | |
744 | error = ntfs_ntvattrget(ntmp, ip, NTFS_A_NAME, NULL, 0, &vap); |
745 | if (error) { |
746 | printf("%s: attr %x for ino %" PRId64 ": error %d\n" , |
747 | __func__, NTFS_A_NAME, ip->i_number, error); |
748 | ntfs_ntput(ip); |
749 | goto out; |
750 | } |
751 | fp->f_fflag = vap->va_a_name->n_flag; |
752 | fp->f_pnumber = vap->va_a_name->n_pnumber; |
753 | fp->f_times = vap->va_a_name->n_times; |
754 | ntfs_ntvattrrele(vap); |
755 | |
756 | if ((ip->i_frflag & NTFS_FRFLAG_DIR) && |
757 | (ntkey->k_attrtype == NTFS_A_DATA && |
758 | strcmp(ntkey->k_attrname, "" ) == 0)) { |
759 | f_type = VDIR; |
760 | } else { |
761 | f_type = VREG; |
762 | error = ntfs_ntvattrget(ntmp, ip, |
763 | ntkey->k_attrtype, ntkey->k_attrname, 0, &vap); |
764 | if (error == 0) { |
765 | fp->f_size = vap->va_datalen; |
766 | fp->f_allocated = vap->va_allocated; |
767 | ntfs_ntvattrrele(vap); |
768 | } else if (ntkey->k_attrtype == NTFS_A_DATA && |
769 | strcmp(ntkey->k_attrname, "" ) == 0 && |
770 | error == ENOENT) { |
771 | fp->f_size = 0; |
772 | fp->f_allocated = 0; |
773 | error = 0; |
774 | } else { |
775 | printf("%s: attr %x for ino %" PRId64 ": error %d\n" , |
776 | __func__, ntkey->k_attrtype, ip->i_number, error); |
777 | ntfs_ntput(ip); |
778 | goto out; |
779 | } |
780 | } |
781 | |
782 | if (key_len <= sizeof(fp->f_smallkey)) |
783 | fp->f_key = &fp->f_smallkey; |
784 | else |
785 | fp->f_key = kmem_alloc(key_len, KM_SLEEP); |
786 | fp->f_ip = ip; |
787 | fp->f_ino = ip->i_number; |
788 | strcpy(fp->f_attrname, ntkey->k_attrname); |
789 | fp->f_attrtype = ntkey->k_attrtype; |
790 | fp->f_vp = vp; |
791 | vp->v_data = fp; |
792 | |
793 | vp->v_tag = VT_NTFS; |
794 | vp->v_type = f_type; |
795 | vp->v_op = ntfs_vnodeop_p; |
796 | ntfs_ntref(ip); |
797 | vref(ip->i_devvp); |
798 | genfs_node_init(vp, &ntfs_genfsops); |
799 | |
800 | if (ip->i_number == NTFS_ROOTINO) |
801 | vp->v_vflag |= VV_ROOT; |
802 | |
803 | uvm_vnp_setsize(vp, fp->f_size); |
804 | ntfs_ntput(ip); |
805 | |
806 | *new_key = fp->f_key; |
807 | |
808 | fp = NULL; |
809 | |
810 | out: |
811 | if (ntkey != &small_key) |
812 | kmem_free(ntkey, key_len); |
813 | if (fp) |
814 | kmem_free(fp, sizeof(*fp)); |
815 | |
816 | return error; |
817 | } |
818 | |
819 | static int |
820 | ntfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) |
821 | { |
822 | return ntfs_vgetex(mp, ino, NTFS_A_DATA, "" , LK_EXCLUSIVE, vpp); |
823 | } |
824 | |
825 | int |
826 | ntfs_vgetex(struct mount *mp, ino_t ino, u_int32_t attrtype, |
827 | const char *attrname, u_long lkflags, struct vnode **vpp) |
828 | { |
829 | const int attrlen = strlen(attrname); |
830 | int error; |
831 | struct ntkey small_key, *ntkey; |
832 | |
833 | if (NTKEY_SIZE(attrlen) <= sizeof(small_key)) |
834 | ntkey = &small_key; |
835 | else |
836 | ntkey = malloc(NTKEY_SIZE(attrlen), M_TEMP, M_WAITOK); |
837 | ntkey->k_ino = ino; |
838 | ntkey->k_attrtype = attrtype; |
839 | strcpy(ntkey->k_attrname, attrname); |
840 | |
841 | error = vcache_get(mp, ntkey, NTKEY_SIZE(attrlen), vpp); |
842 | if (error) |
843 | goto out; |
844 | |
845 | if ((lkflags & (LK_SHARED | LK_EXCLUSIVE)) != 0) { |
846 | error = vn_lock(*vpp, lkflags); |
847 | if (error) { |
848 | vrele(*vpp); |
849 | *vpp = NULL; |
850 | } |
851 | } |
852 | |
853 | out: |
854 | if (ntkey != &small_key) |
855 | free(ntkey, M_TEMP); |
856 | return error; |
857 | } |
858 | |
859 | extern const struct vnodeopv_desc ntfs_vnodeop_opv_desc; |
860 | |
861 | const struct vnodeopv_desc * const ntfs_vnodeopv_descs[] = { |
862 | &ntfs_vnodeop_opv_desc, |
863 | NULL, |
864 | }; |
865 | |
866 | struct vfsops ntfs_vfsops = { |
867 | .vfs_name = MOUNT_NTFS, |
868 | .vfs_min_mount_data = sizeof (struct ntfs_args), |
869 | .vfs_mount = ntfs_mount, |
870 | .vfs_start = ntfs_start, |
871 | .vfs_unmount = ntfs_unmount, |
872 | .vfs_root = ntfs_root, |
873 | .vfs_quotactl = (void *)eopnotsupp, |
874 | .vfs_statvfs = ntfs_statvfs, |
875 | .vfs_sync = ntfs_sync, |
876 | .vfs_vget = ntfs_vget, |
877 | .vfs_loadvnode = ntfs_loadvnode, |
878 | .vfs_fhtovp = ntfs_fhtovp, |
879 | .vfs_vptofh = ntfs_vptofh, |
880 | .vfs_init = ntfs_init, |
881 | .vfs_reinit = ntfs_reinit, |
882 | .vfs_done = ntfs_done, |
883 | .vfs_mountroot = ntfs_mountroot, |
884 | .vfs_snapshot = (void *)eopnotsupp, |
885 | .vfs_extattrctl = vfs_stdextattrctl, |
886 | .vfs_suspendctl = (void *)eopnotsupp, |
887 | .vfs_renamelock_enter = genfs_renamelock_enter, |
888 | .vfs_renamelock_exit = genfs_renamelock_exit, |
889 | .vfs_fsync = (void *)eopnotsupp, |
890 | .vfs_opv_descs = ntfs_vnodeopv_descs |
891 | }; |
892 | |
893 | static int |
894 | ntfs_modcmd(modcmd_t cmd, void *arg) |
895 | { |
896 | int error; |
897 | |
898 | switch (cmd) { |
899 | case MODULE_CMD_INIT: |
900 | error = vfs_attach(&ntfs_vfsops); |
901 | if (error != 0) |
902 | break; |
903 | sysctl_createv(&ntfs_sysctl_log, 0, NULL, NULL, |
904 | CTLFLAG_PERMANENT, |
905 | CTLTYPE_NODE, "ntfs" , |
906 | SYSCTL_DESCR("NTFS file system" ), |
907 | NULL, 0, NULL, 0, |
908 | CTL_VFS, 20, CTL_EOL); |
909 | /* |
910 | * XXX the "20" above could be dynamic, thereby eliminating |
911 | * one more instance of the "number to vfs" mapping problem, |
912 | * but "20" is the order as taken from sys/mount.h |
913 | */ |
914 | break; |
915 | case MODULE_CMD_FINI: |
916 | error = vfs_detach(&ntfs_vfsops); |
917 | if (error != 0) |
918 | break; |
919 | sysctl_teardown(&ntfs_sysctl_log); |
920 | break; |
921 | default: |
922 | error = ENOTTY; |
923 | break; |
924 | } |
925 | |
926 | return (error); |
927 | } |
928 | |