1/*
2 * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/* $FreeBSD: src/sys/msdosfs/msdosfs_vnops.c,v 1.99 2000/05/05 09:58:36 phk Exp $ */
24/*	$NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $	*/
25
26/*-
27 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
28 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
29 * All rights reserved.
30 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 *    notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 *    notice, this list of conditions and the following disclaimer in the
39 *    documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 *    must display the following acknowledgement:
42 *	This product includes software developed by TooLs GmbH.
43 * 4. The name of TooLs GmbH may not be used to endorse or promote products
44 *    derived from this software without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
48 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
49 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
52 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
54 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
55 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57/*
58 * Written by Paul Popelka (paulp@uts.amdahl.com)
59 *
60 * You can do anything you want with this software, just don't say you wrote
61 * it, and don't remove this notice.
62 *
63 * This software is provided "as is".
64 *
65 * The author supplies this software to be publicly redistributed on the
66 * understanding that the author is not responsible for the correct
67 * functioning of this software in any circumstances and is not liable for
68 * any damages caused by this software.
69 *
70 * October 1992
71 */
72#include <sys/param.h>
73#include <sys/systm.h>
74#include <sys/vnode.h>
75#include <sys/kernel.h>
76#include <sys/stat.h>
77#include <sys/buf.h>
78#include <sys/proc.h>
79#include <sys/mount.h>
80#include <sys/unistd.h>
81#include <sys/vnode.h>
82#include <miscfs/specfs/specdev.h>
83#include <sys/malloc.h>
84#include <sys/dirent.h>
85#include <sys/signalvar.h>
86#include <sys/ubc.h>
87#include <sys/utfconv.h>
88#include <sys/attr.h>
89#include <sys/namei.h>
90#include <libkern/crypto/md5.h>
91#include <sys/disk.h>
92#include <mach/boolean.h>
93#include <libkern/OSMalloc.h>
94
95#include "bpb.h"
96#include "direntry.h"
97#include "denode.h"
98#include "msdosfsmount.h"
99#include "fat.h"
100#include "msdosfs_kdebug.h"
101
102#ifndef DEBUG
103#define DEBUG 0
104#endif
105
106/*
107 * The maximum file size on FAT is 4GB-1, which is the largest value that fits
108 * in an unsigned 32-bit integer.
109 */
110#define	DOS_FILESIZE_MAX	0xffffffff
111
112union msdosfs_dirbuf {
113	struct dirent   dirent;
114	struct direntry direntry;
115};
116
117/*
118 * Prototypes for MSDOSFS vnode operations
119 */
120int msdosfs_vnop_create(struct vnop_create_args *);
121int msdosfs_vnop_mknod(struct vnop_mknod_args *);
122int msdosfs_vnop_open(struct vnop_open_args *ap);
123int msdosfs_vnop_close(struct vnop_close_args *);
124int msdosfs_vnop_getattr(struct vnop_getattr_args *);
125int msdosfs_vnop_setattr(struct vnop_setattr_args *);
126int msdosfs_vnop_getxattr(struct vnop_getxattr_args *ap);
127int msdosfs_vnop_setxattr(struct vnop_setxattr_args *ap);
128int msdosfs_vnop_removexattr(struct vnop_removexattr_args *ap);
129int msdosfs_vnop_listxattr(struct vnop_listxattr_args *ap);
130int msdosfs_vnop_read(struct vnop_read_args *);
131int msdosfs_vnop_write(struct vnop_write_args *);
132int msdosfs_vnop_pagein(struct vnop_pagein_args *);
133int msdosfs_vnop_fsync(struct vnop_fsync_args *);
134int msdosfs_vnop_remove(struct vnop_remove_args *);
135int msdosfs_vnop_rename(struct vnop_rename_args *);
136int msdosfs_vnop_mkdir(struct vnop_mkdir_args *);
137int msdosfs_vnop_rmdir(struct vnop_rmdir_args *);
138int msdosfs_vnop_readdir(struct vnop_readdir_args *);
139int msdosfs_vnop_strategy(struct vnop_strategy_args *);
140int msdosfs_vnop_pathconf(struct vnop_pathconf_args *ap);
141int msdosfs_vnop_symlink(struct vnop_symlink_args *ap);
142int msdosfs_vnop_readlink(struct vnop_readlink_args *ap);
143int msdosfs_vnop_ioctl(struct vnop_ioctl_args *ap);
144int msdosfs_vnop_pageout(struct vnop_pageout_args *ap);
145
146/* Other prototypes */
147void msdosfs_lock_two(struct denode *dep1, struct denode *dep2);
148void msdosfs_sort_denodes(struct denode *deps[4]);
149void msdosfs_lock_four(struct denode *dep0, struct denode *dep1, struct denode *dep2, struct denode *dep3);
150void msdosfs_unlock_four(struct denode *dep0, struct denode *dep1, struct denode *dep2, struct denode *dep3);
151void msdosfs_md5_digest(void *text, size_t length, char digest[33]);
152ssize_t msdosfs_dirbuf_size(union msdosfs_dirbuf *buf, size_t name_length, int flags);
153
154
155/*
156 * Some general notes:
157 *
158 * In the ufs filesystem the inodes, superblocks, and indirect blocks are
159 * read/written using the vnode for the filesystem. Blocks that represent
160 * the contents of a file are read/written using the vnode for the file
161 * (including directories when they are read/written as files). This
162 * presents problems for the dos filesystem because data that should be in
163 * an inode (if dos had them) resides in the directory itself.  Since we
164 * must update directory entries without the benefit of having the vnode
165 * for the directory we must use the vnode for the filesystem.  This means
166 * that when a directory is actually read/written (via read, write, or
167 * readdir, or seek) we must use the vnode for the filesystem instead of
168 * the vnode for the directory as would happen in ufs. This is to insure we
169 * retreive the correct block from the buffer cache since the hash value is
170 * based upon the vnode address and the desired block number.
171 */
172
173
174/*
175 * msdosfs_lock_two
176 *
177 * Acquire the denode locks for two denodes.  The locks are always
178 * acquired in order of increasing address of the denode.
179 */
180void msdosfs_lock_two(struct denode *dep1, struct denode *dep2)
181{
182	if (dep1 == NULL)
183		panic("msdosfs_lock_two: dep1 == NULL\n");
184	if (dep2 == NULL)
185		panic("msdosfs_lock_two: dep2 == NULL\n");
186	if (dep1 == dep2)
187		panic("msdosfs_lock_two: dep1 == dep2\n");
188
189	if (dep1 < dep2)
190	{
191		lck_mtx_lock(dep1->de_lock);
192		lck_mtx_lock(dep2->de_lock);
193	}
194	else
195	{
196		lck_mtx_lock(dep2->de_lock);
197		lck_mtx_lock(dep1->de_lock);
198	}
199}
200
201/*
202 * Sort a list of denodes into increasing address order.  Remove duplicate addresses.
203 * Any unused entries will be NULL.  Some of the entries may be NULL on input.
204 */
205void msdosfs_sort_denodes(struct denode *deps[4])
206{
207	int i, j;
208	struct denode *temp;
209
210	/* A simple bubble sort */
211	for (j=3; j>0; --j)
212		for (i=0; i<j; ++i)
213			if (deps[i] > deps[i+1])
214			{
215				temp = deps[i];
216				deps[i] = deps[i+1];
217				deps[i+1] = temp;
218			}
219
220	/* Remove duplicates */
221	for (i=0; i<3; ++i)
222		if (deps[i] == deps[i+1])
223			deps[i] = NULL;
224}
225
226void msdosfs_lock_four(struct denode *dep0, struct denode *dep1, struct denode *dep2, struct denode *dep3)
227{
228	int i;
229	struct denode *deps[4] = {dep0, dep1, dep2, dep3};
230
231	msdosfs_sort_denodes(deps);
232
233	for (i=0; i<4; ++i)
234		if (deps[i] != NULL)
235			lck_mtx_lock(deps[i]->de_lock);
236}
237
238void msdosfs_unlock_four(struct denode *dep0, struct denode *dep1, struct denode *dep2, struct denode *dep3)
239{
240	int i;
241	struct denode *deps[4] = {dep0, dep1, dep2, dep3};
242
243	/*
244	 * We don't actually care about the order of the denodes when unlocking.
245	 * But we do care about removing duplicates.
246	 */
247	msdosfs_sort_denodes(deps);
248
249	for (i=0; i<4; ++i)
250		if (deps[i] != NULL)
251			lck_mtx_unlock(deps[i]->de_lock);
252}
253
254/*
255 * Create a regular file.
256 */
257int msdosfs_vnop_create(struct vnop_create_args *ap)
258/* {
259		vnode_t a_dvp;
260		vnode_t *a_vpp;
261		struct componentname *a_cnp;
262		struct vnode_attr *a_vap;
263		vfs_context_t a_context;
264	} */
265{
266	vnode_t dvp = ap->a_dvp;
267	struct denode *pdep = VTODE(dvp);
268	struct componentname *cnp = ap->a_cnp;
269	vfs_context_t context = ap->a_context;
270	struct vnode_attr *vap = ap->a_vap;
271	struct denode ndirent;
272	struct denode *dep = NULL;
273	struct timespec ts;
274	int error;
275	uint32_t offset = 0;		/* byte offset in directory for new entry */
276	uint32_t long_count = 0;	/* number of long name entries needed */
277	int needs_generation;
278
279    KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_CREATE|DBG_FUNC_START, pdep->de_pmp, pdep, 0, 0, 0);
280
281	lck_mtx_lock(pdep->de_lock);
282
283	/*
284	 * Make sure the parent directory hasn't been deleted.
285	 */
286	if (pdep->de_refcnt <= 0)
287	{
288		cache_purge(dvp);
289		error = ENOENT;
290		goto exit;
291	}
292
293	/*
294	 * Make sure the name does not exist in the parent directory.  (It didn't
295	 * exist during VNOP_LOOKUP, but another thread may have created the name
296	 * before we got the lock on the parent.)
297	 */
298	error = msdosfs_lookup_name(pdep, cnp, NULL, NULL, NULL, NULL, NULL, NULL, context);
299	if (error != ENOENT)
300	{
301		error = EEXIST;
302		goto exit;
303	}
304
305	/*
306	 * Find space in the directory to place the new name.
307	 */
308	bzero(&ndirent, sizeof(ndirent));
309	error = msdosfs_findslots(pdep, cnp, ndirent.de_Name, &needs_generation, &ndirent.de_LowerCase, &offset, &long_count, context);
310	if (error)
311		goto exit;
312
313	/*
314	 * If this is the root directory and there is no space left we
315	 * can't do anything.  This is because the root directory can not
316	 * change size.  (FAT12 and FAT16 only)
317	 *
318	 * On FAT32, we can grow the root directory, and de_StartCluster
319	 * will be the actual cluster number of the root directory.
320	 */
321	if (pdep->de_StartCluster == MSDOSFSROOT && offset >= pdep->de_FileSize)
322	{
323        printf("msdosfs_vnop_create: Cannot grow the root directory on FAT12 or FAT16; returning ENOSPC.\n");
324		error = ENOSPC;
325		goto exit;
326	}
327
328	/*
329	 * Create a directory entry for the file, then call msdosfs_createde() to
330	 * have it installed. NOTE: DOS files are always executable.
331	 * The supplied mode is ignored (DOS doesn't store mode, so
332	 * all files on a volume have a constant mode).  The immutable
333	 * flag is used to set DOS's read-only attribute.
334	 */
335	error = msdosfs_uniqdosname(pdep, ndirent.de_Name, needs_generation ? offset - long_count * sizeof(struct dosdirentry) : 1, context);
336	if (error)
337	{
338		if (DEBUG) panic("msdosfs_vnop_create: msdosfs_uniqdosname returned %d\n", error);
339		goto exit;
340	}
341
342	// Set read-only attribute if one of the immutable bits is set.
343	// Always set the "needs archive" attribute on newly created files.
344	ndirent.de_Attributes = ATTR_ARCHIVE;
345	if (VATTR_IS_ACTIVE(vap, va_flags))
346	{
347		if ((vap->va_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) != 0)
348			ndirent.de_Attributes |= ATTR_READONLY;
349		VATTR_SET_SUPPORTED(vap, va_flags);
350	}
351
352
353	/*
354	 * If the file name starts with ".", make it invisible on Windows.
355	 */
356	if (cnp->cn_nameptr[0] == '.')
357		ndirent.de_Attributes |= ATTR_HIDDEN;
358
359	ndirent.de_StartCluster = 0;
360	ndirent.de_FileSize = 0;
361	ndirent.de_dev = pdep->de_dev;
362	ndirent.de_devvp = pdep->de_devvp;
363	ndirent.de_pmp = pdep->de_pmp;
364	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
365	getnanotime(&ts);
366	DETIMES(&ndirent, &ts, &ts, &ts);
367	error = msdosfs_createde(&ndirent, pdep, &dep, cnp, offset, long_count, context);
368	if (error)
369	{
370		/*
371		 * ENOSPC is a common and expected failure.  Anything else is
372		 * unexpected, and I want a chance to debug it.
373		 */
374		if (DEBUG && error != ENOSPC) panic("msdosfs_vnop_create: msdosfs_createde returned %d\n", error);
375		goto exit;
376	}
377	*ap->a_vpp = DETOV(dep);
378	cache_purge_negatives(dvp);
379
380exit:
381	msdosfs_meta_flush(pdep->de_pmp, FALSE);
382	lck_mtx_unlock(pdep->de_lock);
383    KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_CREATE|DBG_FUNC_END, error, dep, offset, long_count, 0);
384	return error;
385}
386
387int msdosfs_vnop_mknod(struct vnop_mknod_args *ap)
388/* {
389		vnode_t a_dvp;
390		vnode_t *a_vpp;
391		struct componentname *a_cnp;
392		struct vattr *a_vap;
393		vfs_context_t a_context;
394	} */
395{
396#pragma unused (ap)
397	/* We don't support special files */
398	return EINVAL;
399}
400
401int msdosfs_vnop_open(struct vnop_open_args *ap)
402/* {
403		vnode_t a_vp;
404		int a_mode;
405		vfs_context_t a_context;
406	} */
407{
408#pragma unused (ap)
409	return 0;
410}
411
412int msdosfs_vnop_close(struct vnop_close_args *ap)
413/* {
414		vnode_t a_vp;
415		int a_fflag;
416		vfs_context_t a_context;
417	} */
418{
419	vnode_t vp = ap->a_vp;
420	struct denode *dep = VTODE(vp);
421
422	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_CLOSE|DBG_FUNC_START, dep, 0, 0, 0, 0);
423	lck_mtx_lock(dep->de_lock);
424
425	cluster_push(vp, IO_CLOSE);
426	msdosfs_deupdat(dep, 0, ap->a_context);
427	msdosfs_meta_flush(dep->de_pmp, FALSE);
428
429	lck_mtx_unlock(dep->de_lock);
430	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_CLOSE|DBG_FUNC_END, 0, 0, 0, 0, 0);
431
432	return 0;
433}
434
435int msdosfs_vnop_getattr(struct vnop_getattr_args *ap)
436/* {
437		vnode_t a_vp;
438		struct vnode_attr *a_vap;
439		vfs_context_t a_context;
440	} */
441{
442	vnode_t vp = ap->a_vp;
443	struct denode *dep = VTODE(vp);
444	struct msdosfsmount *pmp = dep->de_pmp;
445	struct vnode_attr *vap = ap->a_vap;
446
447	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_GETATTR|DBG_FUNC_START, dep, vap->va_active, 0, 0, 0);
448	lck_mtx_lock(dep->de_lock);
449
450	VATTR_RETURN(vap, va_rdev, 0);
451	VATTR_RETURN(vap, va_nlink, 1);
452	VATTR_RETURN(vap, va_total_size, dep->de_FileSize);
453	/* va_total_alloc is wrong for symlinks */
454	VATTR_RETURN(vap, va_total_alloc, ((off_t)dep->de_FileSize + pmp->pm_crbomask) & ~((off_t)pmp->pm_crbomask));
455	VATTR_RETURN(vap, va_data_size, dep->de_FileSize);
456	VATTR_RETURN(vap, va_data_alloc, vap->va_total_alloc);
457	VATTR_RETURN(vap, va_iosize, pmp->pm_iosize);
458	VATTR_RETURN(vap, va_uid, 99);
459	VATTR_RETURN(vap, va_gid, 99);
460	VATTR_RETURN(vap, va_mode, ALLPERMS & pmp->pm_mask);
461	if (VATTR_IS_ACTIVE(vap, va_flags)) {
462		vap->va_flags = 0;
463		/* MSDOS does not set ATTR_ARCHIVE or ATTR_READONLY bits for directories. */
464		if ((dep->de_Attributes & (ATTR_ARCHIVE | ATTR_DIRECTORY)) == 0)	// DOS: flag set means "needs to be archived"
465			vap->va_flags |= SF_ARCHIVED;				// BSD: flag set means "has been archived"
466		if ((dep->de_Attributes & (ATTR_READONLY | ATTR_DIRECTORY)) == ATTR_READONLY)
467			vap->va_flags |= UF_IMMUTABLE;				// DOS read-only becomes BSD user immutable
468		if (dep->de_Attributes & ATTR_HIDDEN)
469			vap->va_flags |= UF_HIDDEN;
470		VATTR_SET_SUPPORTED(vap, va_flags);
471	}
472
473	/* FAT doesn't support extended security data */
474
475	if (vap->va_active & (VNODE_ATTR_va_create_time |
476		VNODE_ATTR_va_access_time | VNODE_ATTR_va_modify_time |
477		VNODE_ATTR_va_change_time))
478	{
479		struct timespec ts;
480		getnanotime(&ts);
481		DETIMES(dep, &ts, &ts, &ts);
482
483		msdosfs_dos2unixtime(dep->de_CDate, dep->de_CTime, 0, &vap->va_create_time);
484		msdosfs_dos2unixtime(dep->de_ADate, 0, 0, &vap->va_access_time);
485		msdosfs_dos2unixtime(dep->de_MDate, dep->de_MTime, 0, &vap->va_modify_time);
486		vap->va_change_time = vap->va_modify_time;
487		/* FAT doesn't have a backup date/time */
488
489		vap->va_supported |= VNODE_ATTR_va_create_time |
490			VNODE_ATTR_va_access_time |
491			VNODE_ATTR_va_modify_time |
492			VNODE_ATTR_va_change_time;
493	}
494
495	if (VATTR_IS_ACTIVE(vap, va_fileid))
496		VATTR_RETURN(vap, va_fileid, msdosfs_defileid(dep));
497	/* FAT has no va_linkid, and no easy access to va_parentid */
498
499	VATTR_RETURN(vap, va_fsid, dep->de_dev);
500	VATTR_RETURN(vap, va_filerev, dep->de_modrev);
501	VATTR_RETURN(vap, va_gen, 0);
502
503	lck_mtx_unlock(dep->de_lock);
504	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_GETATTR|DBG_FUNC_END, 0, vap->va_supported, 0, 0, 0);
505
506	return 0;
507}
508
509int msdosfs_vnop_setattr(struct vnop_setattr_args *ap)
510	/* {
511		vnode_t a_vp;
512		struct vnode_attr *a_vap;
513		vfs_context_t a_context;
514	} */
515{
516	struct denode *dep = VTODE(ap->a_vp);
517	struct vnode_attr *vap = ap->a_vap;
518	int error = 0;
519
520	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_SETATTR|DBG_FUNC_START, dep, vap->va_active, vap->va_data_size, vap->va_flags, 0);
521	lck_mtx_lock(dep->de_lock);
522
523	int isDir = dep->de_Attributes & ATTR_DIRECTORY;
524
525	if (VATTR_IS_ACTIVE(vap, va_data_size)) {
526		if (isDir)
527		{
528			error = EPERM;	/* Cannot change size of a directory or symlink! */
529			goto exit;
530		}
531		if (dep->de_FileSize != vap->va_data_size) {
532			/* msdosfs_detrunc internally updates dep->de_FileSize and calls ubc_setsize. */
533			if (vap->va_data_size > DOS_FILESIZE_MAX)
534				error = EFBIG;
535			else
536				error = msdosfs_detrunc(dep, (uint32_t)vap->va_data_size, vap->va_vaflags, ap->a_context);
537			if (error)
538				goto exit;
539		}
540		VATTR_SET_SUPPORTED(vap, va_data_size);
541	}
542
543	/* FAT does not support setting uid, gid or mode */
544
545	if (VATTR_IS_ACTIVE(vap, va_flags)) {
546		/*
547		 * Here we are strict, stricter than ufs in not allowing
548		 * users to attempt to set SF_SETTABLE bits or anyone to
549		 * set unsupported bits.  However, we ignore attempts to
550		 * set ATTR_ARCHIVE for directories `cp -pr' from a more
551		 * sensible file system attempts it a lot.
552		 */
553
554		if (vap->va_flags & ~(SF_ARCHIVED | SF_IMMUTABLE | UF_IMMUTABLE | UF_HIDDEN))
555		{
556			error = EINVAL;
557			goto exit;
558		}
559
560		uint8_t originalAttributes = dep->de_Attributes;
561
562		if (vap->va_flags & SF_ARCHIVED)
563			dep->de_Attributes &= ~ATTR_ARCHIVE;
564		else if (!isDir)
565			dep->de_Attributes |= ATTR_ARCHIVE;
566
567		/* For files, copy the immutable flag to read-only attribute. */
568		/* Ignore immutable bit for directories. */
569		if (!isDir)
570		{
571			if (vap->va_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
572				dep->de_Attributes |= ATTR_READONLY;
573			else
574				dep->de_Attributes &= ~ATTR_READONLY;
575		}
576
577        if (vap->va_flags & UF_HIDDEN)
578        	dep->de_Attributes |= ATTR_HIDDEN;
579        else
580        	dep->de_Attributes &= ~ATTR_HIDDEN;
581
582		if (dep->de_Attributes != originalAttributes)
583			dep->de_flag |= DE_MODIFIED | DE_ATTR_MOD;
584
585		VATTR_SET_SUPPORTED(vap, va_flags);
586	}
587
588	/*
589	 * Update times.  Since we don't explicitly store a change time, we
590	 * don't let you set it here.  (An alternative behavior would be to
591	 * set the denode's mod time to the greater of va_modify_time and
592	 * va_change_time.)
593	 */
594	if (VATTR_IS_ACTIVE(vap, va_create_time) |
595		VATTR_IS_ACTIVE(vap, va_access_time) |
596		VATTR_IS_ACTIVE(vap, va_modify_time))
597	{
598		if (VATTR_IS_ACTIVE(vap, va_create_time)) {
599			msdosfs_unix2dostime(&vap->va_create_time, &dep->de_CDate, &dep->de_CTime, NULL);
600			VATTR_SET_SUPPORTED(vap, va_create_time);
601		}
602		if (VATTR_IS_ACTIVE(vap, va_access_time)) {
603			msdosfs_unix2dostime(&vap->va_access_time, &dep->de_ADate, NULL, NULL);
604			VATTR_SET_SUPPORTED(vap, va_access_time);
605		}
606		if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
607			msdosfs_unix2dostime(&vap->va_modify_time, &dep->de_MDate, &dep->de_MTime, NULL);
608			VATTR_SET_SUPPORTED(vap, va_modify_time);
609		}
610		dep->de_Attributes |= ATTR_ARCHIVE;
611		dep->de_flag |= DE_MODIFIED;
612	}
613
614	error = msdosfs_deupdat(dep, 1, ap->a_context);
615	msdosfs_meta_flush(dep->de_pmp, FALSE);
616
617exit:
618	lck_mtx_unlock(dep->de_lock);
619	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_SETATTR|DBG_FUNC_END, error, vap->va_supported, 0, 0, 0);
620	return error;
621}
622
623static char MSDOSFS_XATTR_VOLUME_ID_NAME[] = "com.apple.filesystems.msdosfs.volume_id";
624
625int msdosfs_vnop_getxattr(struct vnop_getxattr_args *ap)
626/* {
627 struct vnodeop_desc *a_desc;
628 vnode_t a_vp;
629 const char * a_name;
630 uio_t a_uio;
631 size_t *a_size;
632 int a_options;
633 vfs_context_t a_context;
634 } */
635{
636	if (vnode_isvroot(ap->a_vp) &&
637		!bcmp(ap->a_name, MSDOSFS_XATTR_VOLUME_ID_NAME, sizeof(MSDOSFS_XATTR_VOLUME_ID_NAME)))
638	{
639		struct denode *dep = VTODE(ap->a_vp);
640		struct msdosfsmount *pmp = dep->de_pmp;
641		uio_t uio = ap->a_uio;
642
643		/* Make sure the volume actually has a serial number. */
644		if (!(pmp->pm_flags & MSDOSFS_HAS_EXT_BOOT))
645			return ENOATTR;
646
647		/* Return the volume serial number */
648		if (uio == NULL)
649		{
650			*ap->a_size = sizeof(pmp->pm_volume_serial_num);
651			return 0;
652		}
653		else if ((user_size_t)uio_resid(uio) < sizeof(pmp->pm_volume_serial_num))
654		{
655			return ERANGE;
656		}
657		else
658		{
659			return uiomove((char *)pmp->pm_volume_serial_num, sizeof(pmp->pm_volume_serial_num), ap->a_uio);
660		}
661	}
662
663	/* Let VFS handle all other extended attributes. */
664	return ENOTSUP;
665}
666
667
668int msdosfs_vnop_setxattr(struct vnop_setxattr_args *ap)
669/* {
670 struct vnodeop_desc *a_desc;
671 vnode_t a_vp;
672 const char * a_name;
673 uio_t a_uio;
674 int a_options;
675 vfs_context_t a_context;
676 } */
677{
678	if (vnode_isvroot(ap->a_vp) &&
679		!bcmp(ap->a_name, MSDOSFS_XATTR_VOLUME_ID_NAME, sizeof(MSDOSFS_XATTR_VOLUME_ID_NAME)))
680	{
681		return EPERM;
682	}
683
684	/* Let VFS handle all other extended attributes. */
685	return ENOTSUP;
686}
687
688
689int msdosfs_vnop_removexattr(struct vnop_removexattr_args *ap)
690/* {
691 struct vnodeop_desc *a_desc;
692 vnode_t a_vp;
693 const char * a_name;
694 int a_options;
695 vfs_context_t a_context;
696 } */
697{
698	if (vnode_isvroot(ap->a_vp) &&
699		!bcmp(ap->a_name, MSDOSFS_XATTR_VOLUME_ID_NAME, sizeof(MSDOSFS_XATTR_VOLUME_ID_NAME)))
700	{
701		return EPERM;
702	}
703
704	/* Let VFS handle all other extended attributes. */
705	return ENOTSUP;
706}
707
708
709int msdosfs_vnop_listxattr(struct vnop_listxattr_args *ap)
710/* {
711 struct vnodeop_desc *a_desc;
712 vnode_t a_vp;
713 uio_t a_uio;
714 size_t *a_size;
715 int a_options;
716 vfs_context_t a_context;
717 } */
718{
719	/* Return our xattrs, then return ENOTSUP to let VFS add the rest from AppleDouble files. */
720	if (vnode_isvroot(ap->a_vp))
721	{
722		struct denode *dep = VTODE(ap->a_vp);
723		struct msdosfsmount *pmp = dep->de_pmp;
724
725		if (pmp->pm_flags & MSDOSFS_HAS_EXT_BOOT)
726		{
727			/*
728			 * The volume has a serial number, so return its name.
729			 */
730
731			uio_t uio = ap->a_uio;
732
733			if (uio == NULL)
734			{
735				/* Just update the size */
736				*ap->a_size += sizeof(MSDOSFS_XATTR_VOLUME_ID_NAME);
737			}
738			else if (uio_resid(uio) < (user_ssize_t)sizeof(MSDOSFS_XATTR_VOLUME_ID_NAME))
739			{
740				return ERANGE;
741			}
742			else
743			{
744				int error = uiomove(MSDOSFS_XATTR_VOLUME_ID_NAME, sizeof(MSDOSFS_XATTR_VOLUME_ID_NAME), uio);
745				if (error)
746					return ERANGE;
747			}
748		}
749	}
750
751	return ENOTSUP;
752}
753
754
755int msdosfs_vnop_read(struct vnop_read_args *ap)
756/* {
757		vnode_t a_vp;
758		struct uio *a_uio;
759		int a_ioflag;
760		vfs_context_t a_context;
761	} */
762{
763	int error = 0;
764	user_ssize_t orig_resid;
765	vnode_t vp = ap->a_vp;
766	struct uio *uio = ap->a_uio;
767	vfs_context_t context = ap->a_context;
768	struct denode *dep = VTODE(vp);
769	struct msdosfsmount *pmp = dep->de_pmp;
770
771	if (uio_offset(uio) < 0)
772		return EINVAL;
773
774	if (uio_offset(uio) > DOS_FILESIZE_MAX)
775		return 0;
776
777	/* If they didn't ask for any data, then we are done. */
778	orig_resid = uio_resid(uio);
779	if (orig_resid <= 0)
780		return 0;
781
782	lck_mtx_lock(dep->de_lock);
783
784	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_READ|DBG_FUNC_START, dep, uio_offset(uio), uio_resid(uio), dep->de_FileSize, 0);
785
786	if (vnode_isreg(vp)) {
787		error = cluster_read(vp, uio, (off_t)dep->de_FileSize, ap->a_ioflag);
788		if (error == 0 && (vfs_flags(pmp->pm_mountp) & (MNT_RDONLY | MNT_NOATIME)) == 0)
789			dep->de_flag |= DE_ACCESS;
790	}
791	else
792	{
793		uint32_t blsize;
794		u_int n;
795		uint32_t diff;
796		uint32_t on;
797		daddr64_t lbn;
798		buf_t bp;
799
800		/* The following code is only used for reading directories */
801
802		do {
803			if (uio_offset(uio) >= dep->de_FileSize)
804				break;
805			lbn = de_cluster(pmp, uio_offset(uio));
806			/*
807			 * If we are operating on a directory file then be sure to
808			 * do i/o with the vnode for the filesystem instead of the
809			 * vnode for the directory.
810			 */
811			/* convert cluster # to block # */
812			error = msdosfs_pcbmap(dep, (uint32_t)lbn, 1, &lbn, NULL, &blsize);
813			if (error == E2BIG) {
814				error = EINVAL;
815				break;
816			} else if (error)
817				break;
818			error = (int)buf_meta_bread(pmp->pm_devvp, lbn, blsize, vfs_context_ucred(context), &bp);
819			if (error) {
820				buf_brelse(bp);
821				break;
822			}
823			if (ISSET(ap->a_ioflag, IO_NOCACHE) && buf_fromcache(bp) == 0)
824				buf_markaged(bp);
825			on = uio_offset(uio) & pmp->pm_crbomask;
826			diff = pmp->pm_bpcluster - on;
827			n = diff > (uint32_t)uio_resid(uio) ? (uint32_t)uio_resid(uio) : diff;
828			diff = (uint32_t)(dep->de_FileSize - uio_offset(uio));
829			if (diff < n)
830				n = diff;
831			diff = blsize - buf_resid(bp);
832			if (diff < n)
833				n = diff;
834			error = uiomove((char *)buf_dataptr(bp) + on, (int) n, uio);
835			buf_brelse(bp);
836		} while (error == 0 && uio_resid(uio) > 0 && n != 0);
837	}
838
839	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_READ|DBG_FUNC_END, error, uio_offset(uio), uio_resid(uio), dep->de_FileSize, 0);
840	lck_mtx_unlock(dep->de_lock);
841
842	return error;
843}
844
845/*
846 * Write data to a file.
847 */
848int msdosfs_vnop_write(struct vnop_write_args *ap)
849/* {
850		vnode_t a_vp;
851		struct uio *a_uio;
852		int a_ioflag;
853		vfs_context_t a_context;
854	} */
855{
856	int error = 0;
857	vnode_t vp = ap->a_vp;
858	struct uio *uio = ap->a_uio;
859	int ioflag = ap->a_ioflag;
860	vfs_context_t context = ap->a_context;
861	struct denode *dep = VTODE(vp);
862	struct msdosfsmount *pmp = dep->de_pmp;
863	off_t zero_off;
864	uint32_t original_size;
865    uint32_t original_clusters;
866	uint32_t filesize;
867	int   lflag;
868	user_ssize_t original_resid;
869	user_ssize_t partial_resid;
870	off_t original_offset;
871	off_t offset;
872
873	switch (vnode_vtype(vp)) {
874	case VREG:
875		break;
876	case VDIR:
877		return EISDIR;
878	default:
879		panic("msdosfs_vnop_write: bad file type");
880		return EINVAL;
881	}
882
883	lck_mtx_lock(dep->de_lock);
884	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_WRITE|DBG_FUNC_START, dep, uio_offset(uio), uio_resid(uio), dep->de_FileSize, 0);
885
886	/*
887	 * Remember some values in case the write fails.
888	 */
889	partial_resid = 0;
890	original_resid = uio_resid(uio);
891	original_size = filesize = dep->de_FileSize;
892    original_clusters = de_clcount(pmp, original_size);
893	original_offset = uio_offset(uio);
894	offset = original_offset;
895
896	if (ioflag & IO_APPEND) {
897		uio_setoffset(uio, dep->de_FileSize);
898		offset = dep->de_FileSize;
899	}
900
901	if (offset < 0)
902	{
903		error = EFBIG;
904		goto exit;
905	}
906
907	if (original_resid == 0)
908	{
909		goto exit;
910	}
911
912	if (offset + original_resid > DOS_FILESIZE_MAX)
913	{
914		error = EFBIG;
915		goto exit;
916	}
917
918	/*
919	 * If the end of the write will be beyond the current end of file,
920	 * then grow the file now to accommodate the bytes we want to write.
921	 */
922    if (offset + original_resid > original_size) {
923		uint32_t clusters_needed, clusters_got;
924		filesize = (uint32_t)(offset + original_resid);
925        clusters_needed = de_clcount(pmp, filesize) - original_clusters;
926		if (clusters_needed)
927			error = msdosfs_extendfile(dep, clusters_needed, &clusters_got);
928
929		if (error == ENOSPC)
930		{
931			/*
932			 * We got fewer clusters than we wanted.  Set the file size
933			 * to the end of the last cluster we now have.
934			 */
935			filesize = de_cn2off(pmp, original_clusters + clusters_got);
936			if (filesize > offset)
937			{
938				/*
939				 * We allocated enough to be able to do a partial write.
940				 * Limit the I/O amount to not exceed the new end of file.
941				 * Keep track of the number of bytes beyond the new end
942				 * of file that we're unable to write, so we can add them
943				 * back in later.
944				 */
945				uio_setresid(uio, filesize-offset);
946				partial_resid = offset + original_resid - filesize;
947				error = 0;
948			}
949		}
950		if (error)
951			goto errexit;
952    }
953
954	lflag = ioflag;
955
956	/*
957	 * If the offset we are starting the write at is beyond the end of
958	 * the file, then they've done a seek.  Unix filesystems allow
959	 * files with holes in them.  DOS doesn't, so we must fill the hole
960	 * with zeroed blocks.
961	 */
962    if (offset > original_size) {
963		zero_off = original_size;
964		lflag   |= IO_HEADZEROFILL;
965	} else
966		zero_off = 0;
967
968	/* Write the data, and any zero filling */
969	error = cluster_write(vp, uio, (off_t)original_size, (off_t)filesize,
970				(off_t)zero_off, (off_t)0, lflag);
971
972	if (uio_offset(uio) > dep->de_FileSize) {
973		dep->de_FileSize = (uint32_t)uio_offset(uio);
974		ubc_setsize(vp, (off_t)dep->de_FileSize);
975	}
976
977	if (partial_resid)
978		uio_setresid(uio, uio_resid(uio) + partial_resid);
979
980	if (original_resid > uio_resid(uio))
981		dep->de_flag |= DE_UPDATE;
982
983	/*
984	 * If the write failed and they want us to, truncate the file back
985	 * to the size it was before the write was attempted.
986	 */
987errexit:
988	if (error) {
989		if (ioflag & IO_UNIT) {
990			/* msdosfs_detrunc internally updates dep->de_FileSize and calls ubc_setsize. */
991			msdosfs_detrunc(dep, original_size, ioflag, context);
992			uio_setoffset(uio, original_offset);
993			uio_setresid(uio, original_resid);
994		} else {
995			/* msdosfs_detrunc internally updates dep->de_FileSize and calls ubc_setsize. */
996			msdosfs_detrunc(dep, dep->de_FileSize, ioflag, context);
997		}
998	} else if (ioflag & IO_SYNC)
999		error = msdosfs_deupdat(dep, 1, context);
1000	msdosfs_meta_flush(pmp, (ioflag & IO_SYNC));
1001
1002exit:
1003	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_WRITE|DBG_FUNC_END, error, uio_offset(uio), uio_resid(uio), dep->de_FileSize, 0);
1004	lck_mtx_unlock(dep->de_lock);
1005	return error;
1006}
1007
1008/*
1009 * Read one or more VM pages from a file on disk.
1010 *
1011 * This routine assumes that the denode's de_lock has already been acquired
1012 * (such as inside of msdosfs_vnop_read).
1013 *
1014 * [It wouldn't make sense to call this routine if the file's size or
1015 * location on disk could be changing.  If it could, then the page(s)
1016 * passed in could be invalid before we could reference them.]
1017 */
1018int msdosfs_vnop_pagein(struct vnop_pagein_args *ap)
1019/* {
1020		vnode_t a_vp;
1021		upl_t a_pl;
1022		vm_offset_t a_pl_offset;
1023		off_t a_f_offset;
1024		size_t a_size;
1025		int a_flags;
1026		vfs_context_t a_context;
1027	} */
1028{
1029	vnode_t vp = ap->a_vp;
1030	struct denode *dep = VTODE(vp);
1031	int error;
1032
1033	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_PAGEIN|DBG_FUNC_START, dep, ap->a_f_offset, ap->a_size, dep->de_FileSize, 0);
1034	error = cluster_pagein(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
1035				(int)ap->a_size, (off_t)dep->de_FileSize,
1036				ap->a_flags);
1037	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_PAGEIN|DBG_FUNC_END, error, 0, 0, 0, 0);
1038
1039	return error;
1040}
1041
1042/*
1043 * Write one or more VM pages to a file on disk.
1044 *
1045 * This routine assumes that the denode's de_lock has already been acquired
1046 * (such as inside of msdosfs_vnop_write).
1047 *
1048 * [It wouldn't make sense to call this routine if the file's size or
1049 * location on disk could be changing.  If it could, then the page(s)
1050 * passed in could be invalid before we could reference them.]
1051 */
1052int msdosfs_vnop_pageout(struct vnop_pageout_args *ap)
1053/* {
1054		vnode_t a_vp;
1055		upl_t a_pl;
1056		vm_offset_t a_pl_offset;
1057		off_t a_f_offset;
1058		size_t a_size;
1059		int a_flags;
1060		vfs_context_t a_context;
1061	} */
1062{
1063	vnode_t vp = ap->a_vp;
1064	struct denode *dep = VTODE(vp);
1065	int error;
1066
1067	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_PAGEOUT|DBG_FUNC_START, dep, ap->a_f_offset, ap->a_size, dep->de_FileSize, 0);
1068	error = cluster_pageout(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
1069				(int)ap->a_size, (off_t)dep->de_FileSize,
1070				ap->a_flags);
1071	if (!error)
1072		dep->de_flag |= DE_UPDATE;
1073	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_PAGEOUT|DBG_FUNC_END, error, 0, 0, 0, 0);
1074
1075	return error;
1076}
1077
1078/*
1079 * Assumes the denode's de_lock is already acquired.
1080 */
1081int msdosfs_fsync_internal(vnode_t vp, int sync, int do_dirs, vfs_context_t context)
1082{
1083	int error;
1084	struct denode *dep = VTODE(vp);
1085
1086	/*
1087	 * First of all, write out any clusters.
1088	 */
1089	cluster_push(vp, sync ? IO_SYNC : 0);
1090
1091	/*
1092	 * Flush all dirty buffers associated with a vnode.
1093	 */
1094	buf_flushdirtyblks(vp, sync, 0, "msdosfs_fsync_internal");
1095
1096	if (do_dirs && (dep->de_Attributes & ATTR_DIRECTORY))
1097		(void) msdosfs_dir_flush(dep, sync);
1098
1099	error = msdosfs_deupdat(dep, sync, context);
1100
1101	return error;
1102}
1103
1104/*
1105 * Flush the blocks of a file to disk.
1106 *
1107 * This function is worthless for vnodes that represent directories. Maybe we
1108 * could just do a sync if they try an fsync on a directory file.
1109 *
1110 * NOTE: This gets called for every vnode, with a_waitfor=MNT_WAIT, during
1111 * an unmount (via vflush and vclean).  Possible optimization: keep track
1112 * of whether the file has dirty content, dirty FAT blocks, or dirty
1113 * directory blocks; skip the msdosfs_fsync_internal and msdosfs_meta_flush
1114 * if nothing is dirty.
1115 */
1116int msdosfs_vnop_fsync(struct vnop_fsync_args *ap)
1117/* {
1118		vnode_t a_vp;
1119		int a_waitfor;
1120		vfs_context_t a_context;
1121	} */
1122{
1123	int error;
1124	vnode_t vp = ap->a_vp;
1125	struct denode *dep = VTODE(vp);
1126
1127	/*
1128	 * Skip everything if there was no denode.  This can happen
1129	 * If the vnode was temporarily created in msdosfs_check_link.
1130	 */
1131	if (dep == NULL)
1132		return 0;
1133
1134	if (dep->de_pmp->pm_flags & MSDOSFSMNT_RONLY) {
1135		/* For read-only mounts, there's nothing to do */
1136		return 0;
1137	}
1138	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_FSYNC|DBG_FUNC_START, dep, 0, 0, 0, 0);
1139
1140	lck_mtx_lock(dep->de_lock);
1141
1142	/* sync dirty buffers associated with this vnode */
1143	error = msdosfs_fsync_internal(vp, (ap->a_waitfor == MNT_WAIT), TRUE, ap->a_context);
1144
1145	/*
1146	 * Flush primary copy of FAT and all directory blocks delayed or asynchronously.
1147	 *
1148	 * Ideally, we would do this synchronously if MNT_WAIT was given.  But that
1149	 * leads to poor performance because every unlink_rmdir causes the vnode to
1150	 * be recycled, and VFS does a VNOP_FSYNC(..., MNT_WAIT, ...), causing us
1151	 * to flush ALL dirty metadata synchronously.  Ouch.
1152	 *
1153	 * Note that HFS with journal does not write the dirty metadata in this
1154	 * case, either.  And there is always fcntl(F_FULLFSYNC) if the user really
1155	 * wants to be sure the data has made it to the media.
1156	 */
1157	msdosfs_meta_flush(dep->de_pmp, FALSE);
1158
1159	lck_mtx_unlock(dep->de_lock);
1160
1161	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_FSYNC|DBG_FUNC_END, error, 0, 0, 0, 0);
1162
1163	return error;
1164}
1165
1166/*
1167 * Remove (unlink) a file.
1168 */
1169int msdosfs_vnop_remove(struct vnop_remove_args *ap)
1170/* {
1171		vnode_t a_dvp;
1172		vnode_t a_vp;
1173		struct componentname *a_cnp;
1174		int a_flags;
1175		vfs_context_t a_context;
1176	} */
1177{
1178    vnode_t vp = ap->a_vp;
1179    vnode_t dvp = ap->a_dvp;
1180    struct denode *dep = VTODE(vp);
1181    struct denode *ddep = VTODE(dvp);
1182    int error;
1183	uint32_t cluster, offset;
1184
1185	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_REMOVE|DBG_FUNC_START, ddep, dep, 0, 0, 0);
1186
1187	msdosfs_lock_two(ddep, dep);
1188
1189	/*
1190	 * Make sure the parent directory hasn't been deleted.
1191	 */
1192	if (ddep->de_refcnt <= 0)
1193	{
1194		cache_purge(dvp);
1195		error = ENOENT;
1196		goto exit;
1197	}
1198
1199	/*
1200	 * Make sure the child denode hasn't been deleted.
1201	 */
1202	if (dep->de_refcnt <= 0)
1203	{
1204		cache_purge(vp);
1205		error = ENOENT;
1206		goto exit;
1207	}
1208
1209	/*
1210	 * Make sure the child still has the same name.
1211	 */
1212	error = msdosfs_lookup_name(ddep, ap->a_cnp, &cluster, &offset, NULL, NULL, NULL, NULL, ap->a_context);
1213	if (error || cluster != dep->de_dirclust || offset != dep->de_diroffset)
1214	{
1215		cache_purge(vp);
1216		error = ENOENT;
1217		goto exit;
1218	}
1219
1220    /* Make sure the file isn't read-only */
1221    /* Note that this is an additional imposition over the
1222       normal deletability rules... */
1223    if (dep->de_Attributes & ATTR_READONLY)
1224    {
1225        error = EPERM;
1226        goto exit;
1227    }
1228
1229	/* Don't allow deletes of busy files (option used by Carbon) */
1230	if ((ap->a_flags & VNODE_REMOVE_NODELETEBUSY) && vnode_isinuse(vp, 0))
1231	{
1232		error = EBUSY;
1233		goto exit;
1234	}
1235
1236	cache_purge(vp);
1237	dep->de_refcnt--;
1238	if (DEBUG && dep->de_refcnt < 0)
1239		panic("msdosfs_vnop_remove: de_refcnt went negative");
1240    error = msdosfs_removede(ddep, dep->de_diroffset, ap->a_context);
1241	if (DEBUG && error) panic("msdosfs_vnop_remove: msdosfs_removede returned %d\n", error);
1242	msdosfs_meta_flush(ddep->de_pmp, FALSE);
1243
1244exit:
1245	lck_mtx_unlock(ddep->de_lock);
1246	lck_mtx_unlock(dep->de_lock);
1247	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_REMOVE|DBG_FUNC_END, error, 0, 0, 0, 0);
1248	return error;
1249}
1250
1251/*
1252 * Renames on files require moving the denode to a new hash queue since the
1253 * location of the directory entry is used to compute the hash.
1254 *
1255 * What follows is the basic algorithm:
1256 *
1257 * if (file move) {
1258 *	if (dest file exists) {
1259 *		remove dest file
1260 *	}
1261 *	if (dest and src in same directory) {
1262 *		rewrite name in existing directory slot
1263 *	} else {
1264 *		write new entry in dest directory
1265 *		update offset and dirclust in denode
1266 *		move denode to new hash chain
1267 *		clear old directory entry
1268 *	}
1269 * } else {
1270 *	directory move
1271 *	if (dest directory exists) {
1272 *		if (dest is not empty) {
1273 *			return ENOTEMPTY
1274 *		}
1275 *		remove dest directory
1276 *	}
1277 *	if (dest and src in same directory) {
1278 *		rewrite name in existing entry
1279 *	} else {
1280 *		be sure dest is not a child of src directory
1281 *		write entry in dest directory
1282 *		update "." and ".." in moved directory
1283 *		clear old directory entry for moved directory
1284 *	}
1285 * }
1286 *
1287 * Locking:
1288 *	On entry, VFS has locked all of the vnodes in an
1289 *	arbitrary, but consistent, order.  For example,
1290 *	it may lock the parent with the smallest vnode pointer,
1291 *	then its child, then the parent with the larger vnode
1292 *	pointer, then its child.
1293 *
1294 *	However, VFS doesn't acquire the locks until after the
1295 *	lookups on both source and destination have completed.
1296 *	It is possible that the source or destination have been
1297 *	deleted or renamed between the lookup and locking the vnode.
1298 *	So we must be paranoid and make sure things haven't changed
1299 *	since VFS locked the vnodes.
1300 *
1301 *	Traditionally, one of the hardest parts about rename's
1302 *	locking is when the source is a directory, and we
1303 *	have to verify that the destination parent isn't a
1304 *	descendant of the source.  We need to walk up the
1305 *	directory hierarchy from the destination parent up to
1306 *	the root.  If any of those directories is the source,
1307 *	the operation is invalid.  But walking up the hierarchy
1308 *	violates the top-down lock order; to avoid deadlock, we
1309 *	must not have two nodes locked at the same time.
1310 *
1311 *	But there is a solution: a mutex on rename operations that
1312 *	reshape the hierarchy (i.e. the source and destination parents
1313 *	are different).  During the walk up the hierarchy, we must
1314 *	prevent any change to the hierarchy that could cause the
1315 *	destination parent to become or stop being an ancestor of
1316 *	the source.  Any operation on a file can't affect the
1317 *	ancestry of directories.  Directory create operations can't
1318 *	affect the ancestry of pre-existing directories.
1319 *	Directory delete operations outside the ancestry path
1320 *	don't matter, and deletes within the ancestry path will
1321 *	fail as long as the directories remain locked (the delete
1322 *	will fail because the directory is locked, or not empty, or
1323 *	both).  The only operation that can affect the ancestry is
1324 *	other rename operations on the same volume (and even then,
1325 *	only if the source parent is not the destination parent).
1326 *
1327 *	It is sufficient if the rename mutex is taken only
1328 *	when the source parent is not the destination parent.
1329 */
1330int msdosfs_vnop_rename(struct vnop_rename_args *ap)
1331/* {
1332		vnode_t a_fdvp;
1333		vnode_t a_fvp;
1334		struct componentname *a_fcnp;
1335		vnode_t a_tdvp;
1336		vnode_t a_tvp;
1337		struct componentname *a_tcnp;
1338		vfs_context_t a_context;
1339	} */
1340{
1341	vnode_t tdvp = ap->a_tdvp;
1342	vnode_t fvp = ap->a_fvp;
1343	vnode_t fdvp = ap->a_fdvp;
1344	vnode_t tvp = ap->a_tvp;
1345	struct componentname *tcnp = ap->a_tcnp;
1346	vfs_context_t context = ap->a_context;
1347	u_char toname[SHORT_NAME_LEN], oldname[SHORT_NAME_LEN];
1348	uint32_t to_diroffset;
1349	uint32_t to_long_count;
1350    int needs_generation;
1351	u_int32_t from_offset;
1352	u_int8_t new_deLowerCase;	/* deLowerCase corresponding to toname */
1353	int doingdirectory = 0, newparent = 0;
1354    int change_case;
1355	int error;
1356	uint32_t cn;
1357	daddr64_t bn = 0;
1358	struct denode *fddep;	/* from file's parent directory	 */
1359	struct denode *fdep;	/* from file or directory	 */
1360	struct denode *tddep;	/* to file's parent directory	 */
1361	struct denode *tdep;	/* to file or directory		 */
1362	struct msdosfsmount *pmp;
1363	struct buf *bp;
1364	uint32_t cluster, offset;
1365
1366	fddep = VTODE(fdvp);
1367	fdep = VTODE(fvp);
1368	tddep = VTODE(tdvp);
1369	tdep = tvp ? VTODE(tvp) : NULL;
1370
1371	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_RENAME|DBG_FUNC_START, fddep, fdep, tddep, tdep, 0);
1372
1373	msdosfs_lock_four(fddep, fdep, tddep, tdep);
1374
1375	pmp = fddep->de_pmp;
1376
1377	/*
1378	 * VNOP_RENAME is different from other VNOPs to non-thread-safe file
1379	 * systems.  Because two paths are involved, VFS does not lock the
1380	 * parent vnodes before the VNOP_LOOKUPs.  It locks all four vnodes
1381	 * after all of the lookups are done (and all vnodes referenced).
1382	 *
1383	 * This means that the source and destination objects may no longer
1384	 * exist in the namespace, and that the children may no longer be
1385	 * children of the given parents (the child may have been renamed
1386	 * by another thread).  And the children may not have the names you
1387	 * find in the component name.
1388	 *
1389	 * I think the best we can do is to try to verify that the vnodes
1390	 * still have the indicated relationship.  Plus, we need to check that
1391	 * the desintation name does not exist in the desintation directory.
1392	 * (A wrinkle here is that a case variant of the desintation name may
1393	 * exist, and it may be the source!
1394	 */
1395
1396	/*
1397	 * Make sure the from parent directory hasn't been deleted.
1398	 */
1399	if (fddep->de_refcnt <= 0)
1400	{
1401		cache_purge(fdvp);
1402		error = ENOENT;
1403		goto exit;
1404	}
1405
1406	/*
1407	 * Make sure the from child denode hasn't been deleted.
1408	 */
1409	if (fdep->de_refcnt <= 0)
1410	{
1411		cache_purge(fvp);
1412		error = ENOENT;
1413		goto exit;
1414	}
1415
1416	/*
1417	 * Make sure the from child still has the same name.
1418	 */
1419	error = msdosfs_lookup_name(fddep, ap->a_fcnp, &cluster, &offset, NULL, NULL, NULL, NULL, context);
1420	if (error || cluster != fdep->de_dirclust || offset != fdep->de_diroffset)
1421	{
1422		cache_purge(fvp);
1423		error = ENOENT;
1424		goto exit;
1425	}
1426
1427	/*
1428	 * Make sure the to parent directory hasn't been deleted.
1429	 */
1430	if (tddep->de_refcnt <= 0)
1431	{
1432		cache_purge(tdvp);
1433		error = ENOENT;
1434		goto exit;
1435	}
1436
1437	/*
1438	 * If there was a to child, make sure it hasn't been deleted.
1439	 */
1440	if (tdep && tdep->de_refcnt <= 0)
1441	{
1442		cache_purge(tvp);
1443		error = ENOENT;
1444		goto exit;
1445	}
1446
1447	/*
1448	 * Look for the destination name in the destination directory.  If it exists,
1449	 * it had better be tdep; otherwise, tdep had better be NULL.
1450	 */
1451	error = msdosfs_lookup_name(tddep, tcnp, &cluster, &offset, NULL, NULL, NULL, NULL, context);
1452	if (tdep)
1453	{
1454		/* We think the destination exists... */
1455		if (error || cluster != tdep->de_dirclust || offset != tdep->de_diroffset)
1456		{
1457			error = ERESTART;
1458			goto exit;
1459		}
1460	}
1461	else
1462	{
1463		/* We think the destination does not exist... */
1464		if (error != ENOENT)
1465		{
1466			error = EEXIST;
1467			goto exit;
1468		}
1469	}
1470
1471	/*
1472	 * If source and dest are the same, then it is a rename
1473	 * that may be changing the upper/lower case of the name.
1474	 */
1475    change_case = 0;
1476	if (tvp == fvp) {
1477        /* Pretend the destination doesn't exist. */
1478        tvp = NULL;
1479
1480        /* Remember we're changing case, so we can skip msdosfs_uniqdosname() */
1481        change_case = 1;
1482	}
1483
1484	/*
1485	 * If there is a destination, and it's a file, make sure it isn't
1486	 * read-only.
1487	 */
1488	if (tdep && (tdep->de_Attributes & (ATTR_READONLY | ATTR_DIRECTORY)) == ATTR_READONLY)
1489	{
1490		error = EPERM;
1491		goto exit;
1492	}
1493
1494	/* If the source is a file, make sure it isn't read-only. */
1495	if ((fdep->de_Attributes & (ATTR_READONLY | ATTR_DIRECTORY)) == ATTR_READONLY)
1496	{
1497		error = EPERM;
1498		goto exit;
1499	}
1500
1501	/*
1502	 * Figure out where to put the new directory entry.
1503	 */
1504	error = msdosfs_findslots(tddep, tcnp, toname, &needs_generation, &new_deLowerCase, &to_diroffset, &to_long_count, context);
1505	if (error)
1506		goto exit;
1507
1508	/*
1509	 * If this is the root directory and there is no space left we
1510	 * can't do anything.  This is because the root directory can not
1511	 * change size.  (FAT12 and FAT16 only)
1512	 *
1513	 * On FAT32, we can grow the root directory, and de_StartCluster
1514	 * will be the actual cluster number of the root directory.
1515	 */
1516	if (tddep->de_StartCluster == MSDOSFSROOT && to_diroffset >= tddep->de_FileSize)
1517	{
1518        printf("msdosfs_vnop_rename: Cannot grow the root directory on FAT12 or FAT16; returning ENOSPC.\n");
1519		error = ENOSPC;
1520		goto exit;
1521	}
1522
1523	if (fdep->de_Attributes & ATTR_DIRECTORY)
1524		doingdirectory = 1;
1525
1526	/*
1527	 * If ".." must be changed (ie the directory gets a new
1528	 * parent) then the source directory must not be in the
1529	 * directory heirarchy above the target, as this would
1530	 * orphan everything below the source directory. Also
1531	 * the user must have write permission in the source so
1532	 * as to be able to change "..".
1533	 */
1534	if (fddep->de_StartCluster != tddep->de_StartCluster)
1535		newparent = 1;
1536	if (doingdirectory && newparent) {
1537		lck_mtx_lock(pmp->pm_rename_lock);
1538		error = msdosfs_doscheckpath(fdep, tddep, context);
1539		if (error) goto exit;
1540	}
1541
1542	/* Remember the offset of fdep within fddep. */
1543	from_offset = fdep->de_diroffset;
1544
1545	if (tvp != NULL && tdep != NULL) {
1546		uint32_t dest_offset;	/* Of the pre-existing entry being removed */
1547
1548		/*
1549		 * Target must be empty if a directory and have no links
1550		 * to it. Also, ensure source and target are compatible
1551		 * (both directories, or both not directories).
1552		 */
1553		if (tdep->de_Attributes & ATTR_DIRECTORY) {
1554			if (!msdosfs_dosdirempty(tdep, context)) {
1555				error = ENOTEMPTY;
1556				goto exit;
1557			}
1558			if (!doingdirectory) {
1559				error = ENOTDIR;
1560				goto exit;
1561			}
1562		} else {
1563			if (doingdirectory) {
1564				error = EISDIR;
1565				goto exit;
1566			}
1567		}
1568		dest_offset = tdep->de_diroffset;
1569		cache_purge(tvp);		/* Purge tvp before we delete it on disk */
1570		tdep->de_refcnt--;
1571		if (DEBUG && tdep->de_refcnt < 0)
1572			panic("msdosfs_vnop_rename: de_refcnt went negative");
1573		error = msdosfs_removede(tddep, dest_offset, context);
1574		if (error)
1575		{
1576			if (DEBUG) panic("msdosfs_vnop_rename: msdosfs_removede (destination) returned %d\n", error);
1577			goto flush_exit;
1578		}
1579	}
1580
1581	/*
1582	 * Figure out the new short name (toname).  If we're just changing
1583	 * case, then the short name stays the same.  Otherwise, we have
1584	 * to convert the long name to a unique short name.
1585	 */
1586    if (change_case)
1587	{
1588		bcopy(fdep->de_Name, toname, SHORT_NAME_LEN);
1589	}
1590	else
1591	{
1592        error = msdosfs_uniqdosname(tddep, toname, needs_generation ? to_diroffset - to_long_count * sizeof(struct dosdirentry) : 1, context);
1593        /*� What if we get an error and we already deleted the target? */
1594        if (error)
1595		{
1596			if (DEBUG) panic("msdosfs_vnop_rename: msdosfs_uniqdosname returned %d\n", error);
1597			goto flush_exit;
1598		}
1599    }
1600
1601	/*
1602	 * First write a new entry in the destination
1603	 * directory and mark the entry in the source directory
1604	 * as deleted.  Then move the denode to the correct hash
1605	 * chain for its new location in the filesystem.  And, if
1606	 * we moved a directory, then update its .. entry to point
1607	 * to the new parent directory.  Set the name in the denode
1608	 * to the new short name.
1609	 *
1610	 * The name in the denode is updated for files.  For
1611	 * directories, the denode points at the "." entry in
1612	 * the directory, so temporarily change the name in
1613	 * the denode, and restore it; otherwise the "." entry
1614	 * may be overwritten.
1615	 */
1616	cache_purge(fvp);
1617	bcopy(fdep->de_Name, oldname, SHORT_NAME_LEN);
1618	bcopy(toname, fdep->de_Name, SHORT_NAME_LEN);	/* update denode */
1619	fdep->de_LowerCase = new_deLowerCase;
1620
1621	/*
1622	 * If the new name starts with ".", make it invisible on Windows.
1623	 * Otherwise, make it visible.
1624	 */
1625	if (tcnp->cn_nameptr[0] == '.')
1626		fdep->de_Attributes |= ATTR_HIDDEN;
1627	else
1628		fdep->de_Attributes &= ~ATTR_HIDDEN;
1629
1630	error = msdosfs_createde(fdep, tddep, NULL, tcnp, to_diroffset, to_long_count, context);
1631	if (error)
1632	{
1633		if (DEBUG) panic("msdosfs_vnop_rename: msdosfs_createde returned %d\n", error);
1634		bcopy(oldname, fdep->de_Name, SHORT_NAME_LEN);
1635		/*� What if we already deleted the target? */
1636		/* And shouldn't we also restore fdep->de_LowerCase? */
1637		goto flush_exit;
1638	}
1639	else
1640	{
1641		cache_purge_negatives(tdvp);
1642	}
1643
1644	fdep->de_parent = tddep;
1645
1646	error = msdosfs_removede(fddep, from_offset, context);
1647	if (error) {
1648		if (DEBUG) panic("msdosfs_vnop_rename: msdosfs_removede (source) returned %d\n", error);
1649		goto flush_exit;
1650	}
1651
1652	/*
1653	 * Fix fdep's de_dirclust and de_diroffset to reflect
1654	 * its new location in the destination directory.
1655	 */
1656	error = msdosfs_pcbmap(tddep, de_cluster(pmp, to_diroffset), 1,
1657				NULL, &fdep->de_dirclust, NULL);
1658	if (error) {
1659		if (DEBUG) panic("msdosfs_vnop_rename: msdosfs_pcbmap returned %d\n", error);
1660		goto flush_exit;
1661	}
1662	fdep->de_diroffset = to_diroffset;
1663
1664	/*
1665	 * fdep's identity (name and parent) have changed, so we must msdosfs_hash_reinsert
1666	 * it in our denode hash.
1667	 */
1668	msdosfs_hash_reinsert(fdep);
1669
1670	/*
1671	 * If we moved a directory to a new parent directory, then we must
1672	 * fixup the ".." entry in the moved directory.
1673	 */
1674	if (doingdirectory && newparent) {
1675		struct dosdirentry *dotdotp;
1676
1677		/* Read in the first cluster of the directory and point to ".." entry */
1678		cn = fdep->de_StartCluster;
1679		bn = cntobn(pmp, cn);
1680		error = (int)buf_meta_bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, vfs_context_ucred(context), &bp);
1681		if (error) {
1682			if (DEBUG) panic("msdosfs_vnop_rename: buf_meta_bread returned %d\n", error);
1683			buf_brelse(bp);
1684			goto flush_exit;
1685		}
1686		dotdotp = (struct dosdirentry *)buf_dataptr(bp) + 1;
1687
1688		/* Update the starting cluster of ".." */
1689		putuint16(dotdotp->deStartCluster, tddep->de_StartCluster);
1690		if (FAT32(pmp))
1691			putuint16(dotdotp->deHighClust, tddep->de_StartCluster >> 16);
1692
1693		error = (int)buf_bdwrite(bp);
1694		if (error) {
1695			if (DEBUG) panic("msdosfs_vnop_rename: buf_bdwrite returned %d\n", error);
1696			goto flush_exit;
1697		}
1698	}
1699
1700flush_exit:
1701	msdosfs_meta_flush(pmp, FALSE);
1702exit:
1703
1704	if (doingdirectory && newparent)
1705		lck_mtx_unlock(pmp->pm_rename_lock);
1706	msdosfs_unlock_four(fddep, fdep, tddep, tdep);
1707
1708	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_RENAME|DBG_FUNC_END, error, 0, 0, 0, 0);
1709
1710	return error;
1711
1712}
1713
1714static struct {
1715	struct dosdirentry dot;
1716	struct dosdirentry dotdot;
1717} dosdirtemplate = {
1718	{	".       ", "   ",			/* the . entry */
1719		ATTR_DIRECTORY,				/* file attribute */
1720		0,	 				/* reserved */
1721		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1722		{ 0, 0 },				/* access date */
1723		{ 0, 0 },				/* high bits of start cluster */
1724		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1725		{ 0, 0 },				/* startcluster */
1726		{ 0, 0, 0, 0 } 				/* filesize */
1727	},
1728	{	"..      ", "   ",			/* the .. entry */
1729		ATTR_DIRECTORY,				/* file attribute */
1730		0,	 				/* reserved */
1731		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1732		{ 0, 0 },				/* access date */
1733		{ 0, 0 },				/* high bits of start cluster */
1734		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1735		{ 0, 0 },				/* startcluster */
1736		{ 0, 0, 0, 0 }				/* filesize */
1737	}
1738};
1739
1740int msdosfs_vnop_mkdir(struct vnop_mkdir_args *ap)
1741/* {
1742		vnode_t a_dvp;
1743		vnode_t *a_vpp;
1744		struct componentname *a_cnp;
1745		struct vattr *a_vap;
1746		vfs_context_t a_context;
1747	} */
1748{
1749	vnode_t dvp = ap->a_dvp;
1750	struct denode *pdep = VTODE(dvp);
1751	struct componentname *cnp = ap->a_cnp;
1752	vfs_context_t context = ap->a_context;
1753	struct denode *dep;
1754	struct dosdirentry *denp;
1755	struct msdosfsmount *pmp = pdep->de_pmp;
1756	struct buf *bp;
1757	uint32_t newcluster, pcl;
1758	daddr64_t bn;
1759	int error;
1760	struct denode ndirent;
1761	struct timespec ts;
1762	char *bdata;
1763	uint32_t offset;
1764	uint32_t long_count;
1765	int needs_generation;
1766
1767	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_MKDIR|DBG_FUNC_START, pdep, 0, 0, 0, 0);
1768
1769	lck_mtx_lock(pdep->de_lock);
1770
1771	/*
1772	 * Make sure the parent directory hasn't been deleted.
1773	 */
1774	if (pdep->de_refcnt <= 0)
1775	{
1776		cache_purge(dvp);
1777		error = ENOENT;
1778		goto exit;
1779	}
1780
1781	/*
1782	 * Make sure the name does not exist in the parent directory.  (It didn't
1783	 * exist during VNOP_LOOKUP, but another thread may have created the name
1784	 * before we got the lock on the parent.)
1785	 */
1786	error = msdosfs_lookup_name(pdep, cnp, NULL, NULL, NULL, NULL, NULL, NULL, context);
1787	if (error != ENOENT)
1788	{
1789		error = EEXIST;
1790		goto exit;
1791	}
1792
1793	/*
1794	 * Find space in the directory to place the new name.
1795	 */
1796	bzero(&ndirent, sizeof(ndirent));
1797	error = msdosfs_findslots(pdep, cnp, ndirent.de_Name, &needs_generation, &ndirent.de_LowerCase, &offset, &long_count, context);
1798	if (error)
1799		goto exit;
1800
1801	/*
1802	 * If this is the root directory and there is no space left we
1803	 * can't do anything.  This is because the root directory can not
1804	 * change size.  (FAT12 and FAT16 only)
1805	 *
1806	 * On FAT32, we can grow the root directory, and de_StartCluster
1807	 * will be the actual cluster number of the root directory.
1808	 */
1809	if (pdep->de_StartCluster == MSDOSFSROOT && offset >= pdep->de_FileSize)
1810	{
1811        printf("msdosfs_vnop_mkdir: Cannot grow the root directory on FAT12 or FAT16; returning ENOSPC.\n");
1812		error = ENOSPC;
1813		goto exit;
1814	}
1815
1816	/*
1817	 * Allocate a cluster to hold the about to be created directory.
1818	 */
1819	error = msdosfs_clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
1820	if (error)
1821		goto exit;
1822
1823	ndirent.de_pmp = pmp;
1824	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
1825	getnanotime(&ts);
1826	DETIMES(&ndirent, &ts, &ts, &ts);
1827
1828	/*
1829	 * Now fill the cluster with the "." and ".." entries. And write
1830	 * the cluster to disk.  This way it is there for the parent
1831	 * directory to be pointing at if there were a crash.
1832	 */
1833	bn = cntobn(pmp, newcluster);
1834	bp = buf_getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, BLK_META);
1835	bdata = (char *)buf_dataptr(bp);
1836
1837	bzero(bdata, pmp->pm_bpcluster);
1838	bcopy(&dosdirtemplate, bdata, sizeof dosdirtemplate);
1839	denp = (struct dosdirentry *)bdata;
1840	putuint16(denp[0].deStartCluster, newcluster);
1841	putuint16(denp[0].deCDate, ndirent.de_CDate);
1842	putuint16(denp[0].deCTime, ndirent.de_CTime);
1843	denp[0].deCHundredth = ndirent.de_CHun;
1844	putuint16(denp[0].deADate, ndirent.de_ADate);
1845	putuint16(denp[0].deMDate, ndirent.de_MDate);
1846	putuint16(denp[0].deMTime, ndirent.de_MTime);
1847	pcl = pdep->de_StartCluster;
1848	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1849		pcl = 0;
1850	putuint16(denp[1].deStartCluster, pcl);
1851	putuint16(denp[1].deCDate, ndirent.de_CDate);
1852	putuint16(denp[1].deCTime, ndirent.de_CTime);
1853	denp[1].deCHundredth = ndirent.de_CHun;
1854	putuint16(denp[1].deADate, ndirent.de_ADate);
1855	putuint16(denp[1].deMDate, ndirent.de_MDate);
1856	putuint16(denp[1].deMTime, ndirent.de_MTime);
1857	if (FAT32(pmp)) {
1858		putuint16(denp[0].deHighClust, newcluster >> 16);
1859		putuint16(denp[1].deHighClust, pdep->de_StartCluster >> 16);
1860	}
1861
1862	error = (int)buf_bdwrite(bp);
1863	if (error)
1864		goto exit;
1865
1866	/*
1867	 * Now build up a directory entry pointing to the newly allocated
1868	 * cluster.  This will be written to an empty slot in the parent
1869	 * directory.
1870	 */
1871	error = msdosfs_uniqdosname(pdep, ndirent.de_Name, needs_generation ? offset - long_count * sizeof(struct dosdirentry) : 1, context);
1872	if (error)
1873		goto exit;
1874
1875	ndirent.de_Attributes = ATTR_DIRECTORY;
1876	ndirent.de_StartCluster = newcluster;
1877	ndirent.de_FileSize = 0;
1878	ndirent.de_dev = pdep->de_dev;
1879	ndirent.de_devvp = pdep->de_devvp;
1880
1881	/*
1882	 * If the file name starts with ".", make it invisible on Windows.
1883	 */
1884	if (cnp->cn_nameptr[0] == '.')
1885		ndirent.de_Attributes |= ATTR_HIDDEN;
1886
1887	error = msdosfs_createde(&ndirent, pdep, &dep, cnp, offset, long_count, context);
1888	if (error)
1889	{
1890		if (DEBUG)
1891			panic("msodsfs_mkdir: msdosfs_createde failed\n");
1892		msdosfs_freeclusterchain(pmp, newcluster);
1893	}
1894	else
1895	{
1896		*ap->a_vpp = DETOV(dep);
1897		cache_purge_negatives(dvp);
1898	}
1899
1900exit:
1901	msdosfs_meta_flush(pmp, FALSE);
1902	lck_mtx_unlock(pdep->de_lock);
1903	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_MKDIR|DBG_FUNC_END, error, 0, 0, 0, 0);
1904	return error;
1905}
1906
1907/*
1908 * Remove a directory.
1909 *
1910 * On entry, vp has been suspended, so there are no pending
1911 * create or lookup operations happening using it as the
1912 * parent directory.
1913 *
1914 * VFS has already checked that dvp != vp.
1915 *
1916 */
1917int msdosfs_vnop_rmdir(struct vnop_rmdir_args *ap)
1918/* {
1919		vnode_t a_dvp;
1920		vnode_t a_vp;
1921		struct componentname *a_cnp;
1922		vfs_context_t a_context;
1923	} */
1924{
1925	vnode_t vp = ap->a_vp;
1926	vnode_t dvp = ap->a_dvp;
1927	vfs_context_t context = ap->a_context;
1928	struct denode *ip, *dp;
1929	int error;
1930	uint32_t cluster, offset;
1931
1932	ip = VTODE(vp);
1933	dp = VTODE(dvp);
1934
1935	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_RMDIR|DBG_FUNC_START, dp, ip, 0, 0, 0);
1936	msdosfs_lock_two(dp, ip);
1937
1938	/*
1939	 * Make sure the parent directory hasn't been deleted.
1940	 */
1941	if (dp->de_refcnt <= 0)
1942	{
1943		cache_purge(dvp);
1944		error = ENOENT;
1945		goto exit;
1946	}
1947
1948	/*
1949	 * Make sure the child denode hasn't been deleted.
1950	 */
1951	if (ip->de_refcnt <= 0)
1952	{
1953		cache_purge(vp);
1954		error = ENOENT;
1955		goto exit;
1956	}
1957
1958	/*
1959	 * Make sure the child still has the same name.
1960	 */
1961	error = msdosfs_lookup_name(dp, ap->a_cnp, &cluster, &offset, NULL, NULL, NULL, NULL, context);
1962	if (error || cluster != ip->de_dirclust || offset != ip->de_diroffset)
1963	{
1964		cache_purge(vp);
1965		error = ENOENT;
1966		goto exit;
1967	}
1968
1969	/*
1970	 * No rmdir "." please.
1971	 *
1972	 * VFS already checks this in rmdir(), so do we
1973	 * need to check again?  (It would only be useful if
1974	 * some other entity called our VNOP directly.)
1975	 */
1976	if (dp == ip) {
1977		error = EINVAL;
1978		goto exit;
1979	}
1980
1981	/*
1982	 * Verify the directory is empty (and valid).
1983	 * (Rmdir ".." won't be valid since
1984	 *  ".." will contain a reference to
1985	 *  the current directory and thus be
1986	 *  non-empty.)
1987	 */
1988	if (!msdosfs_dosdirempty(ip, context)) {
1989		error = ENOTEMPTY;
1990		goto exit;
1991	}
1992
1993	/*
1994	 * Delete the entry from the directory.  For dos filesystems this
1995	 * gets rid of the directory entry on disk.  The in memory copy
1996	 * still exists but the de_refcnt is <= 0.  This prevents it from
1997	 * being found by msdosfs_deget().  When the vput() on dep is done we give
1998	 * up access and eventually msdosfs_vnop_reclaim() will be called which
1999	 * will remove it from the denode cache.
2000	 */
2001	ip->de_refcnt--;
2002	if (DEBUG && ip->de_refcnt < 0)
2003		panic("msdosfs_vnop_rmdir: de_refcnt went negative");
2004	error = msdosfs_removede(dp, ip->de_diroffset, context);
2005	if (error)
2006	{
2007		if (DEBUG) panic("msdosfs_vnop_rmdir: msdosfs_removede returned %d\n", error);
2008		goto flush_exit;
2009	}
2010
2011	/*
2012	 * Invalidate the directory's contents.  If directory I/O went through
2013	 * the directory's vnode, this wouldn't be needed; the invalidation
2014	 * done in msdosfs_detrunc would be sufficient.
2015	 */
2016	error = msdosfs_dir_invalidate(ip);
2017	if (error)
2018	{
2019		if (DEBUG) panic("msdosfs_vnop_rmdir: msdosfs_dir_invalidate returned %d\n", error);
2020		goto flush_exit;
2021	}
2022
2023	/*
2024	 * Truncate the directory that is being deleted.
2025	 * msdosfs_detrunc internally updates dep->de_FileSize and calls ubc_setsize.
2026	 */
2027	error = msdosfs_detrunc(ip, 0, 0, context);
2028	if (DEBUG && error) panic("msdosfs_vnop_rmdir: msdosfs_detrunc returned %d\n", error);
2029	cache_purge(vp);
2030
2031flush_exit:
2032	msdosfs_meta_flush(dp->de_pmp, FALSE);
2033exit:
2034	lck_mtx_unlock(dp->de_lock);
2035	lck_mtx_unlock(ip->de_lock);
2036	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_RMDIR|DBG_FUNC_END, error, 0, 0, 0, 0);
2037	return error;
2038}
2039
2040
2041/*
2042 * Set the d_namlen and d_reclen fields in a directory buffer.
2043 *
2044 * The d_reclen field must be rounded up to a multiple of the length of the d_ino
2045 * field so that all entries will have their d_ino field on natural alignment.
2046 */
2047ssize_t msdosfs_dirbuf_size(union msdosfs_dirbuf *buf, size_t name_length, int flags)
2048{
2049	if (flags & VNODE_READDIR_EXTENDED)
2050	{
2051		buf->direntry.d_namlen = name_length;
2052		buf->direntry.d_reclen = (offsetof(struct direntry, d_name) + name_length + 8) & ~7;
2053		return buf->direntry.d_reclen;
2054	}
2055	else
2056	{
2057		buf->dirent.d_namlen = name_length;
2058		buf->dirent.d_reclen = (offsetof(struct dirent, d_name) + name_length + 4) & ~3;
2059		return buf->dirent.d_reclen;
2060	}
2061}
2062
2063int msdosfs_vnop_readdir(struct vnop_readdir_args *ap)
2064/* {
2065		vnode_t a_vp;
2066		struct uio *a_uio;
2067		int a_flags;
2068		int *a_eofflag;
2069		int *a_numdirent;
2070		vfs_context_t a_context;
2071	} */
2072{
2073	int error = 0;
2074	vnode_t vp = ap->a_vp;
2075	struct uio *uio = ap->a_uio;
2076	struct denode *dep = VTODE(vp);
2077	struct msdosfsmount *pmp = dep->de_pmp;
2078	vfs_context_t context = ap->a_context;
2079	uint32_t blsize;
2080	int32_t entry_offset;
2081	uint32_t cn;
2082	uint32_t fileno;
2083	int32_t bias = 0;
2084	daddr64_t bn, dir_cluster;
2085	struct buf *bp;
2086	struct dosdirentry *dentp;
2087	off_t offset;			/* Current offset within directory */
2088	off_t long_name_offset;	/* Offset to start of long name */
2089	int chksum = -1;
2090	u_int16_t ucfn[WIN_MAXLEN];
2091	u_int16_t unichars = 0;
2092	size_t outbytes;
2093	char *bdata;
2094	union msdosfs_dirbuf buf;
2095	char *buf_name;
2096	size_t max_name;
2097	ssize_t buf_reclen;
2098	uint8_t buf_type;
2099	int eofflag = 0;
2100	int numdirent = 0;
2101
2102	if (ap->a_numdirent)
2103		*ap->a_numdirent = 0;
2104
2105	/* Assume we won't hit end of directory */
2106	if (ap->a_eofflag)
2107		*ap->a_eofflag = 0;
2108
2109	if (ap->a_flags & VNODE_READDIR_REQSEEKOFF)
2110		return EINVAL;
2111
2112	/* Make a pointer to the start of the name field in the buffer. */
2113	if (ap->a_flags & VNODE_READDIR_EXTENDED)
2114	{
2115		buf_name = &buf.direntry.d_name[0];
2116		max_name = sizeof(buf.direntry.d_name);
2117		buf.direntry.d_seekoff = 0;
2118	}
2119	else
2120	{
2121		buf_name = &buf.dirent.d_name[0];
2122		max_name = sizeof(buf.dirent.d_name);
2123	}
2124
2125	/*
2126	 * msdosfs_vnop_readdir() won't operate properly on regular files since
2127	 * it does i/o only with the device vnode, and hence can
2128	 * retrieve the wrong block from the buffer cache for a plain file.
2129	 * So, fail attempts to readdir() on a plain file.
2130	 */
2131	if (!vnode_isdir(vp))
2132		return ENOTDIR;
2133
2134	/*
2135	 * If the file offset is not a multiple of the size of a
2136	 * directory entry, then we fail the read.  The remaining
2137	 * space in the buffer will be checked before each uiomove.
2138	 */
2139	long_name_offset = offset = uio_offset(uio);
2140	if (offset & (sizeof(struct dosdirentry) - 1))
2141		return EINVAL;
2142
2143	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_READDIR|DBG_FUNC_START, dep, uio_offset(uio), uio_resid(uio), ap->a_flags, 0);
2144	lck_mtx_lock(dep->de_lock);
2145
2146	/*
2147	 * If they are reading from the root directory, then we simulate
2148	 * the . and .. entries since these don't exist in the root
2149	 * directory.  We also set the offset bias to make up for having to
2150	 * simulate these entries. By this I mean that at file offset 64 we
2151	 * read the first entry in the root directory that lives on disk.
2152	 */
2153	if (dep->de_StartCluster == MSDOSFSROOT
2154	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
2155		bias = 2 * sizeof(struct dosdirentry);
2156		while (offset < bias) {
2157			if (ap->a_flags & VNODE_READDIR_EXTENDED) {
2158				buf.direntry.d_fileno = msdosfs_defileid(dep);
2159				buf.direntry.d_type = DT_DIR;
2160			} else {
2161				buf.dirent.d_fileno = msdosfs_defileid(dep);
2162				buf.dirent.d_type = DT_DIR;
2163			}
2164			if (offset == 0) {
2165				strlcpy(buf_name, ".", max_name);
2166				outbytes = 1;
2167			} else {
2168				strlcpy(buf_name, "..", max_name);
2169				outbytes = 2;
2170			}
2171			buf_reclen = msdosfs_dirbuf_size(&buf, outbytes, ap->a_flags);
2172			if (uio_resid(uio) < buf_reclen)
2173				goto out;
2174			error = uiomove((caddr_t) &buf, (int)buf_reclen, uio);
2175			if (error)
2176				goto out;
2177			++numdirent;
2178			offset += sizeof(struct dosdirentry);
2179		}
2180	}
2181
2182	while (uio_resid(uio) > 0) {
2183		dir_cluster = de_cluster(pmp, offset - bias);
2184		entry_offset = (offset - bias) & pmp->pm_crbomask;
2185		if (dep->de_FileSize <= (offset - bias)) {
2186			eofflag = 1;	/* Hit end of directory */
2187			break;
2188		}
2189		error = msdosfs_pcbmap(dep, (uint32_t)dir_cluster, 1, &bn, &cn, &blsize);
2190		if (error)
2191			break;
2192		error = (int)buf_meta_bread(pmp->pm_devvp, bn, blsize, vfs_context_ucred(context), &bp);
2193		if (error) {
2194			buf_brelse(bp);
2195			goto exit;
2196		}
2197		bdata = (char *)buf_dataptr(bp);
2198
2199		/*
2200		 * Correct the block size to be just the amount of data actually
2201		 * returned in the buffer.  That is, the amount we asked to read
2202		 * minus the "resid" (amount remaining to read).
2203		 *
2204		 * Normally, buf_resid() should return 0; that is, it should have
2205		 * read as much as we asked for.  But we've seen some badly behaved
2206		 * devices which either misreport the number of sectors (giving us
2207		 * a value that is too large), or are unable to access sectors past
2208		 * a given offset.  If we end up trying to read past that offset,
2209		 * the devices tend to return success but zero bytes of data,
2210		 * and buf_resid() will end up being the number of bytes we
2211		 * expected to get, but didn't actually get.
2212		 */
2213		blsize -= buf_resid(bp);
2214
2215		/*
2216		 * Convert from dos directory entries to fs-independent
2217		 * directory entries.
2218		 */
2219		for (dentp = (struct dosdirentry *)(bdata + entry_offset);
2220		     (char *)dentp < bdata + blsize;
2221		     dentp++, offset += sizeof(struct dosdirentry)) {
2222			/*
2223			 * If this is an unused entry, we can stop.
2224			 */
2225			if (dentp->deName[0] == SLOT_EMPTY) {
2226				buf_brelse(bp);
2227				if (ap->a_eofflag)
2228					*ap->a_eofflag = 1;	/* Hit end of directory */
2229				goto out;
2230			}
2231			/*
2232			 * Skip deleted entries.
2233			 */
2234			if (dentp->deName[0] == SLOT_DELETED) {
2235				chksum = -1;
2236				continue;
2237			}
2238
2239			/*
2240			 * Handle Win95 long directory entries
2241			 */
2242			if ((dentp->deAttributes & ATTR_WIN95_MASK) == ATTR_WIN95) {
2243				if (dentp->deName[0] & WIN_LAST)
2244					long_name_offset = offset;
2245				chksum = msdosfs_getunicodefn((struct winentry *)dentp,
2246						ucfn, &unichars, chksum);
2247				continue;
2248			}
2249
2250			/*
2251			 * Skip volume labels
2252			 */
2253			if (dentp->deAttributes & ATTR_VOLUME) {
2254				chksum = -1;
2255				continue;
2256			}
2257
2258			/*
2259			 * This computation of d_fileno must match
2260			 * the computation in msdosfs_defileid().
2261             */
2262            fileno = getuint16(dentp->deStartCluster);
2263            if (FAT32(pmp))
2264                fileno |= getuint16(dentp->deHighClust) << 16;
2265			if (dentp->deAttributes & ATTR_DIRECTORY) {
2266            	if (fileno == MSDOSFSROOT) {
2267                    /* if this is the root directory */
2268                    if (FAT32(pmp))
2269                        fileno = pmp->pm_rootdirblk;
2270                    else
2271                        fileno = FILENO_ROOT;
2272                }
2273				buf_type = DT_DIR;
2274			} else {
2275                if (fileno == 0)
2276                    fileno = FILENO_EMPTY;	/* constant for empty files */
2277				if (getuint32(dentp->deFileSize) == sizeof(struct symlink))
2278					buf_type = DT_UNKNOWN;	/* Might be a symlink */
2279				else
2280					buf_type = DT_REG;
2281			}
2282			if (chksum != msdosfs_winChksum(dentp->deName)) {
2283				chksum = -1;
2284				unichars = msdosfs_dos2unicodefn(dentp->deName, ucfn,
2285				    dentp->deLowerCase);
2286			}
2287
2288			/* translate the name in ucfn into UTF-8 */
2289			(void) utf8_encodestr(ucfn, unichars * 2, (u_int8_t*)buf_name,
2290						&outbytes, max_name, 0,
2291						UTF_DECOMPOSED|UTF_SFM_CONVERSIONS);
2292
2293			/* Fill in the other buffer fields. */
2294			if (ap->a_flags & VNODE_READDIR_EXTENDED)
2295			{
2296				buf.direntry.d_ino = fileno;
2297				buf.direntry.d_type = buf_type;
2298			}
2299			else
2300			{
2301				buf.dirent.d_ino = fileno;
2302				buf.dirent.d_type = buf_type;
2303			}
2304			buf_reclen = msdosfs_dirbuf_size(&buf, outbytes, ap->a_flags);
2305
2306			if (uio_resid(uio) < buf_reclen) {
2307				buf_brelse(bp);
2308				goto out;
2309			}
2310			error = uiomove((caddr_t) &buf, (int)buf_reclen, uio);
2311			if (error) {
2312				buf_brelse(bp);
2313				goto out;
2314			}
2315			chksum = -1;
2316			++numdirent;
2317		}
2318		buf_brelse(bp);
2319	}
2320
2321out:
2322	/* Update the current position within the directory */
2323	if (chksum != -1)
2324		offset = long_name_offset;
2325	uio_setoffset(uio, offset);
2326exit:
2327	lck_mtx_unlock(dep->de_lock);
2328	if (ap->a_eofflag)
2329		*ap->a_eofflag = eofflag;
2330	if (ap->a_numdirent)
2331		*ap->a_numdirent = numdirent;
2332	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_READDIR|DBG_FUNC_END, error, uio_offset(uio), numdirent, eofflag, 0);
2333	return error;
2334}
2335
2336/* blktooff converts a logical block number to a file offset */
2337int msdosfs_vnop_blktooff(struct vnop_blktooff_args *ap)
2338/* {
2339		vnode_t a_vp;
2340		daddr64_t a_lblkno;
2341		off_t *a_offset;
2342	} */
2343{
2344	if (ap->a_vp == NULL)
2345		return EINVAL;
2346
2347	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_BLKTOOFF|DBG_FUNC_START, ap->a_lblkno, 0, 0, 0, 0);
2348	*ap->a_offset = ap->a_lblkno * PAGE_SIZE_64;
2349	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_BLKTOOFF|DBG_FUNC_END, 0, *ap->a_offset, 0, 0, 0);
2350
2351	return 0;
2352}
2353
2354/* offtoblk converts a file offset to a logical block number */
2355int msdosfs_vnop_offtoblk(struct vnop_offtoblk_args *ap)
2356/* {
2357		vnode_t a_vp;
2358		off_t a_offset;
2359		daddr64_t *a_lblkno;
2360	} */
2361{
2362	if (ap->a_vp == NULL)
2363		return EINVAL;
2364
2365	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_OFFTOBLK|DBG_FUNC_START, ap->a_offset, 0, 0, 0, 0);
2366	*ap->a_lblkno = ap->a_offset / PAGE_SIZE_64;
2367	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_OFFTOBLK|DBG_FUNC_END, 0, *ap->a_lblkno, 0, 0, 0);
2368
2369	return 0;
2370}
2371
2372
2373/*
2374 * Map a logical file range (offset and length) to an on-disk extent
2375 * (block number and number of contiguous blocks).
2376 *
2377 * This routine assumes that the denode's de_lock has already been acquired
2378 * (such as inside of msdosfs_vnop_read or msdosfs_vnop_write, or by the caller of
2379 * buf_meta_bread).
2380 *
2381 * [It wouldn't make sense to call this routine if the file's size or
2382 * location on disk could be changing.  If it could, then any output
2383 * from this routine could be obsolete before the caller could use it.]
2384 */
2385int msdosfs_vnop_blockmap(struct vnop_blockmap_args *ap)
2386/* {
2387		vnode_t a_vp;
2388		off_t a_foffset;
2389		size_t a_size;
2390		daddr64_t *a_bpn;
2391		size_t *a_run;
2392		void *a_poff;
2393		int a_flags;
2394		vfs_context_t a_context;
2395	} */
2396{
2397	int error;
2398	vnode_t vp = ap->a_vp;
2399	struct denode *dep = VTODE(vp);
2400    struct msdosfsmount *pmp = dep->de_pmp;
2401	uint32_t runsize;
2402    uint32_t		cn;
2403    uint32_t		numclusters;
2404    daddr64_t	bn;
2405
2406	if (ap->a_bpn == NULL)
2407		return 0;
2408
2409	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_BLOCKMAP|DBG_FUNC_START, dep, ap->a_foffset, ap->a_size, 0, 0);
2410    if (ap->a_size == 0)
2411        panic("msdosfs_vnop_blockmap: a_size == 0");
2412
2413    /* Find the cluster that contains the given file offset */
2414    cn = de_cluster(pmp, ap->a_foffset);
2415
2416    /* Determine number of clusters occupied by the given range */
2417    numclusters = de_cluster(pmp, ap->a_foffset + ap->a_size - 1) - cn + 1;
2418
2419    /* Find the physical (device) block where that cluster starts */
2420    error = msdosfs_pcbmap(dep, cn, numclusters, &bn, NULL, &runsize);
2421
2422    /* Add the offset in physical (device) blocks from the start of the cluster */
2423    bn += (((uint32_t)ap->a_foffset - de_cn2off(pmp, cn)) >> pmp->pm_bnshift);
2424    runsize -= ((uint32_t)ap->a_foffset - (de_cn2off(pmp, cn)));
2425
2426    *ap->a_bpn = bn;
2427	if (error == 0 && ap->a_run) {
2428		if (runsize > ap->a_size)
2429			* ap->a_run = ap->a_size;
2430		else
2431			* ap->a_run = runsize;
2432	}
2433	if (ap->a_poff)
2434		*(int *)ap->a_poff = 0;
2435
2436	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_BLOCKMAP|DBG_FUNC_END, error, bn, runsize, 0, 0);
2437	return error;
2438}
2439
2440/*
2441 * prepare and issue the I/O
2442 * buf_strategy knows how to deal
2443 * with requests that require
2444 * fragmented I/Os
2445 */
2446int msdosfs_vnop_strategy(struct vnop_strategy_args *ap)
2447/* {
2448		struct buf *a_bp;
2449	} */
2450{
2451	buf_t	bp = ap->a_bp;
2452	vnode_t	vp = buf_vnode(bp);
2453	struct denode *dep = VTODE(vp);
2454	int error;
2455
2456	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_STRATEGY|DBG_FUNC_START, dep, buf_lblkno(bp), buf_resid(bp), buf_flags(bp), 0);
2457	error = buf_strategy(dep->de_devvp, ap);
2458	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_STRATEGY|DBG_FUNC_END, error, buf_error(bp), buf_resid(bp), buf_flags(bp), 0);
2459	return error;
2460}
2461
2462int msdosfs_vnop_pathconf(struct vnop_pathconf_args *ap)
2463/* {
2464		vnode_t a_vp;
2465		int a_name;
2466		register_t *a_retval;
2467		vfs_context_t a_context;
2468	} */
2469{
2470	int error = 0;
2471
2472	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_PATHCONF|DBG_FUNC_START, ap->a_name, 0, 0, 0, 0);
2473	switch (ap->a_name) {
2474	case _PC_LINK_MAX:
2475		*ap->a_retval = 1;
2476		break;
2477	case _PC_NAME_MAX:
2478		*ap->a_retval = WIN_MAXLEN;
2479		break;
2480	case _PC_PATH_MAX:
2481		*ap->a_retval = PATH_MAX;
2482		break;
2483	case _PC_CHOWN_RESTRICTED:
2484		*ap->a_retval = 1;
2485		break;
2486	case _PC_NO_TRUNC:
2487		*ap->a_retval = 0;
2488		break;
2489	case _PC_CASE_SENSITIVE:
2490		*ap->a_retval = 0;
2491		break;
2492	case _PC_CASE_PRESERVING:
2493		*ap->a_retval = 1;
2494		break;
2495	case _PC_FILESIZEBITS:
2496		*ap->a_retval = 32;
2497		break;
2498	default:
2499		error = EINVAL;
2500		break;
2501	}
2502	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_PATHCONF|DBG_FUNC_END, error, *ap->a_retval, 0, 0, 0);
2503	return error;
2504}
2505
2506
2507/*
2508 * Given a chunk of memory, compute the md5 digest as a string of 32
2509 * hex digits followed by a NUL character.
2510 */
2511void msdosfs_md5_digest(void *text, size_t length, char digest[33])
2512{
2513	int i;
2514	MD5_CTX context;
2515	unsigned char digest_raw[16];
2516
2517	MD5Init(&context);
2518	MD5Update(&context, text, (unsigned)length);
2519	MD5Final(digest_raw, &context);
2520
2521	for (i=0; i<16; ++i)
2522	{
2523		/*
2524		 * The "3" below is for the two hex digits plus trailing '\0'.
2525		 * Note that the trailing '\0' from byte N is overwritten
2526		 * by the first character of byte N+1, and that digest[] has
2527		 * a length of 33 == 2 * 16 + 1 in order to have room for a
2528		 * trailing '\0' after the last byte's characters.
2529		 */
2530		(void) snprintf(digest, 3, "%02x", digest_raw[i]);
2531		digest += 2;
2532	}
2533}
2534
2535
2536/*
2537 * Determine whether the given denode refers to a symlink or an ordinary
2538 * file.  This is called during vnode creation, so the de_vnode field
2539 * has not been set up.  Returns the vnode type to create (either
2540 * VREG or VLNK).
2541 *
2542 * In order to tell whether the file is an ordinary file or a symlink,
2543 * we need to read from it.  The easiest way to do this is to create a
2544 * temporary vnode of type VREG, so we can use the buffer cache.  We will
2545 * terminate the vnode before returning.  msdosfs_deget() will create the real
2546 * vnode to be returned.
2547 *
2548 * Assumes that the denode's de_lock is already acquired, or that the denode is
2549 * otherwise protected (not part of the denode hash, not in the name cache, not
2550 * in the volume's name space).
2551 */
2552enum vtype msdosfs_check_link(struct denode *dep, vfs_context_t context)
2553{
2554	int error;
2555	int i;
2556	unsigned length;
2557	char c;
2558	enum vtype result;
2559	struct msdosfsmount *pmp;
2560	vnode_t vp = NULL;
2561    buf_t bp = NULL;
2562	struct symlink *link;
2563	char digest[33];
2564	struct vnode_fsparam vfsp;
2565
2566	if (dep->de_FileSize != sizeof(struct symlink))
2567	{
2568		result = VREG;
2569		goto exit;
2570	}
2571
2572	/*
2573	 * The file is the magic symlink size.  We need to read it in so we
2574	 * can validate the header and update the size to reflect the
2575	 * length of the symlink.
2576	 *
2577	 * We create a temporary vnode to read in the contents of the file
2578	 * (since it may be fragmented, and this lets us take advantage of the
2579	 * blockmap and strategy routines).
2580	 *
2581	 * The temporary vnode's type is set to VNON instead of VREG so that
2582	 * vnode_iterate won't return it.  This prevents a race with
2583	 * msdosfs_vfs_sync that might try to fsync this vnode as its v_data pointer
2584	 * gets cleared by vnode_clearfsnode below.  (The race can lead to a
2585	 * NULL denode pointer in msdosfs_deupdat(), which causes a panic.)
2586	 */
2587	result = VREG;		/* Assume it's not a symlink, until we verify it. */
2588	pmp = dep->de_pmp;
2589
2590	vfsp.vnfs_mp = pmp->pm_mountp;
2591	vfsp.vnfs_vtype = VNON;
2592	vfsp.vnfs_str = "msdosfs";
2593	vfsp.vnfs_dvp = NULL;
2594	vfsp.vnfs_fsnode = dep;
2595	vfsp.vnfs_cnp = NULL;
2596	vfsp.vnfs_vops = msdosfs_vnodeop_p;
2597	vfsp.vnfs_rdev = 0;
2598	vfsp.vnfs_filesize = dep->de_FileSize;
2599	vfsp.vnfs_flags = VNFS_NOCACHE;
2600	vfsp.vnfs_markroot = 0;
2601	vfsp.vnfs_marksystem = 0;
2602
2603	error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &dep->de_vnode);
2604	vp = dep->de_vnode;
2605	if (error) goto exit;
2606
2607    error = buf_meta_bread(vp, 0, roundup(sizeof(*link),pmp->pm_bpcluster),
2608        vfs_context_ucred(context), &bp);
2609    if (error) goto exit;
2610    link = (struct symlink *) buf_dataptr(bp);
2611
2612	/* Verify the magic */
2613	if (strncmp(link->magic, symlink_magic, 5) != 0)
2614		goto exit;
2615
2616	/* Parse and sanity check the length field */
2617	length = 0;
2618	for (i=0; i<4; ++i)
2619	{
2620		c = link->length[i];
2621		if (c < '0' || c > '9')
2622			goto exit;		/* Length is non-decimal */
2623		length = 10 * length + c - '0';
2624	}
2625	if (length > SYMLINK_LINK_MAX)
2626		goto exit;			/* Length is too big */
2627
2628	/* Verify the MD5 digest */
2629	msdosfs_md5_digest(link->link, length, digest);
2630	if (strncmp(digest, link->md5, 32) != 0)
2631		goto exit;
2632
2633	/* It passed all the checks; must be a symlink */
2634	result = VLNK;
2635	dep->de_FileSize = length;
2636	dep->de_flag |= DE_SYMLINK;
2637
2638exit:
2639    if (bp)
2640    {
2641        /*
2642         * We're going to be getting rid of the vnode, so we might as well
2643         * mark the buffer invalid so that it will get reused right away.
2644         */
2645        buf_markinvalid(bp);
2646        buf_brelse(bp);
2647    }
2648
2649	if (vp)
2650	{
2651		(void) vnode_clearfsnode(vp);	/* So we won't free dep */
2652		(void) vnode_recycle(vp);		/* get rid of the vnode now */
2653		(void) vnode_put(vp);			/* to balance vnode_create */
2654	}
2655
2656	return result;
2657}
2658
2659
2660/*
2661 * Create a symbolic link.
2662 *
2663 * The idea is to write the symlink file content to disk and
2664 * create a new directory entry pointing at the symlink content.
2665 * Then msdosfs_createde will automatically create the correct type of
2666 * vnode.
2667 */
2668int msdosfs_vnop_symlink(struct vnop_symlink_args *ap)
2669/* {
2670	vnode_t a_dvp;
2671	vnode_t *a_vpp;
2672	struct componentname *a_cnp;
2673	struct vnode_attr *a_vap;
2674	char *a_target;
2675	vfs_context_t a_context;
2676	} */
2677{
2678	int error;
2679	vnode_t dvp = ap->a_dvp;
2680	struct denode *dep = VTODE(dvp);
2681    struct msdosfsmount *pmp = dep->de_pmp;
2682    struct componentname *cnp = ap->a_cnp;
2683	struct vnode_attr *vap = ap->a_vap;
2684	char *target = ap->a_target;
2685	vfs_context_t context = ap->a_context;
2686	size_t length;		/* length of target path */
2687	struct symlink *link = NULL;
2688	uint32_t cn = 0;			/* first cluster of symlink */
2689	uint32_t clusters, got;	/* count of clusters needed, actually allocated */
2690	buf_t bp = NULL;
2691	struct denode ndirent;
2692	struct denode *new_dep;
2693	struct timespec ts;
2694	uint32_t offset;
2695	uint32_t long_count;
2696	int needs_generation;
2697
2698	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_SYMLINK|DBG_FUNC_START, dep, 0, 0, 0, 0);
2699	lck_mtx_lock(dep->de_lock);
2700
2701	/*
2702	 * Make sure the parent directory hasn't been deleted.
2703	 */
2704	if (dep->de_refcnt <= 0)
2705	{
2706		cache_purge(dvp);
2707		error = ENOENT;
2708		goto exit;
2709	}
2710
2711	/*
2712	 * Make sure the name does not exist in the parent directory.  (It didn't
2713	 * exist during VNOP_LOOKUP, but another thread may have created the name
2714	 * before we got the lock on the parent.)
2715	 */
2716	error = msdosfs_lookup_name(dep, cnp, NULL, NULL, NULL, NULL, NULL, NULL, context);
2717	if (error != ENOENT)
2718	{
2719		error = EEXIST;
2720		goto exit;
2721	}
2722
2723	/*
2724	 * Find space in the directory to place the new name.
2725	 */
2726	bzero(&ndirent, sizeof(ndirent));
2727	error = msdosfs_findslots(dep, cnp, ndirent.de_Name, &needs_generation, &ndirent.de_LowerCase, &offset, &long_count, context);
2728	if (error)
2729		goto exit;
2730
2731	/*
2732	 * If this is the root directory and there is no space left we
2733	 * can't do anything.  This is because the root directory can not
2734	 * change size.  (FAT12 and FAT16 only)
2735	 *
2736	 * On FAT32, we can grow the root directory, and de_StartCluster
2737	 * will be the actual cluster number of the root directory.
2738	 */
2739	if (dep->de_StartCluster == MSDOSFSROOT && offset >= dep->de_FileSize)
2740	{
2741        printf("msdosfs_vnop_symlink: Cannot grow the root directory on FAT12 or FAT16; returning ENOSPC.\n");
2742		error = ENOSPC;
2743		goto exit;
2744	}
2745
2746	length = strlen(target);
2747	if (length > SYMLINK_LINK_MAX)
2748	{
2749		error = ENAMETOOLONG;
2750		goto exit;
2751	}
2752
2753	/*
2754	 * Allocate (contiguous) space to store the symlink.  In an ideal world,
2755	 * we should support creating a non-contiguous symlink file, but that
2756	 * would be much more complicated (creating a temporary denode and vnode
2757	 * just so that we can allocate and write to the link, then removing that
2758	 * vnode and creating a real one with the correct vtype).
2759	 */
2760	clusters = de_clcount(pmp, sizeof(*link));
2761	error = msdosfs_clusteralloc(pmp, 0, clusters, CLUST_EOFE, &cn, &got);
2762	if (error) goto exit;
2763	if (got < clusters)
2764	{
2765		error = ENOSPC;
2766		goto exit;
2767	}
2768
2769	/* Get a buffer to hold the symlink */
2770	bp = buf_getblk(pmp->pm_devvp, cntobn(pmp, cn),
2771		roundup(sizeof(*link),pmp->pm_bpcluster),
2772		0, 0, BLK_META);
2773	buf_clear(bp);
2774	link = (struct symlink *) buf_dataptr(bp);
2775
2776	/*
2777	 * Fill in each of the symlink fields.  We have to do this in the same
2778	 * order as the fields appear in the structure because some of the
2779	 * operations clobber one byte past the end of their field (with a
2780	 * NUL character that is a string terminator).
2781	 */
2782	bcopy(symlink_magic, link->magic, sizeof(symlink_magic));
2783	/* 6 = 4 bytes of digits + newline + '\0' */
2784	snprintf(link->length, 6, "%04u\n", (unsigned)length);
2785	msdosfs_md5_digest(target, length, link->md5);
2786	link->newline2 = '\n';
2787	bcopy(target, link->link, length);
2788
2789	/* Pad with newline if there is room */
2790	if (length < SYMLINK_LINK_MAX)
2791		link->link[length++] = '\n';
2792
2793	/* Pad with spaces if there is room */
2794	if (length < SYMLINK_LINK_MAX)
2795		memset(&link->link[length], ' ', SYMLINK_LINK_MAX-length);
2796
2797	/* Write out the symlink */
2798	error = buf_bwrite(bp);
2799	bp = NULL;
2800	buf_invalblkno(pmp->pm_devvp, cntobn(pmp, cn), BUF_WAIT);
2801	if (error)
2802	{
2803		if (DEBUG) panic("msdosfs_vnop_symlink: buf_bwrite returned %d\n", error);
2804		goto exit;
2805	}
2806
2807	/* Start setting up new directory entry */
2808	error = msdosfs_uniqdosname(dep, ndirent.de_Name, needs_generation ? offset - long_count * sizeof(struct dosdirentry) : 1, context);
2809	if (error)
2810	{
2811		if (DEBUG) panic("msdosfs_vnop_symlink: msdosfs_uniqdosname returned %d\n", error);
2812		goto exit;
2813	}
2814
2815	/*
2816	 * Set read-only attribute if one of the immutable bits is set.
2817	 * Always set the "needs archive" attribute on newly created files.
2818	 */
2819	ndirent.de_Attributes = ATTR_ARCHIVE;
2820	if (VATTR_IS_ACTIVE(vap, va_flags))
2821	{
2822		if ((vap->va_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) != 0)
2823			ndirent.de_Attributes |= ATTR_READONLY;
2824		VATTR_SET_SUPPORTED(vap, va_flags);
2825	}
2826
2827	ndirent.de_StartCluster = cn;
2828	ndirent.de_FileSize = sizeof(*link);
2829	ndirent.de_dev = dep->de_dev;
2830	ndirent.de_devvp = dep->de_devvp;
2831	ndirent.de_pmp = dep->de_pmp;
2832	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
2833	getnanotime(&ts);
2834	DETIMES(&ndirent, &ts, &ts, &ts);
2835
2836	/* Create a new directory entry pointing at the newly allocated clusters */
2837	error = msdosfs_createde(&ndirent, dep, &new_dep, cnp, offset, long_count, context);
2838	if (error)
2839	{
2840		if (DEBUG) panic("msdosfs_vnop_symlink: msdosfs_createde returned %d\n", error);
2841		goto exit;
2842	}
2843	*ap->a_vpp = DETOV(new_dep);
2844	cache_purge_negatives(dvp);
2845
2846exit:
2847	if (bp)
2848	{
2849		/*
2850		 * After the symlink is created, we should access the contents via the
2851		 * symlink's vnode, not via the device.  Marking it invalid here
2852		 * prevents double caching (and the inconsistencies which can result).
2853		 */
2854		buf_markinvalid(bp);
2855		buf_brelse(bp);
2856	}
2857	if (error != 0 && cn != 0)
2858		(void) msdosfs_freeclusterchain(pmp, cn);
2859
2860	msdosfs_meta_flush(pmp, FALSE);
2861	lck_mtx_unlock(dep->de_lock);
2862	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_SYMLINK|DBG_FUNC_END, error, 0, 0, 0, 0);
2863	return error;
2864}
2865
2866
2867int msdosfs_vnop_readlink(struct vnop_readlink_args *ap)
2868/* {
2869	vnode_t a_vp;
2870	struct uio *a_uio;
2871	vfs_context_t a_context;
2872	} */
2873{
2874	int error;
2875	vnode_t vp = ap->a_vp;
2876	struct denode *dep = VTODE(vp);
2877    struct msdosfsmount *pmp = dep->de_pmp;
2878    buf_t bp = NULL;
2879	struct symlink *link;
2880
2881	if (vnode_vtype(vp) != VLNK)
2882		return EINVAL;
2883
2884	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_READLINK|DBG_FUNC_START, dep, 0, 0, 0, 0);
2885	lck_mtx_lock(dep->de_lock);
2886
2887	if (dep->de_refcnt <= 0)
2888	{
2889		cache_purge(vp);
2890		error = EAGAIN;
2891		goto exit;
2892	}
2893
2894	if (dep->de_StartCluster == 0)
2895		panic("msdosfs_vnop_readlink: de_StartCluster == 0!\n");
2896
2897	/*
2898	 * If the vnode was created of type VLNK, then we assume the symlink
2899	 * file has been checked for consistency.  So we skip it here.
2900	 */
2901    error = buf_meta_bread(vp, 0, roundup(sizeof(*link),pmp->pm_bpcluster),
2902        vfs_context_ucred(ap->a_context), &bp);
2903    if (error) goto exit;
2904    link = (struct symlink *) buf_dataptr(bp);
2905
2906	/*
2907	 * Return the link.  Assumes de_FileSize was set to the length
2908	 * of the symlink.
2909	 */
2910	error = uiomove(link->link, dep->de_FileSize, ap->a_uio);
2911
2912exit:
2913    if (bp)
2914        buf_brelse(bp);
2915
2916	lck_mtx_unlock(dep->de_lock);
2917	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_READLINK|DBG_FUNC_END, error, 0, 0, 0, 0);
2918
2919	return error;
2920}
2921
2922
2923int msdosfs_vnop_ioctl(struct vnop_ioctl_args *ap)
2924/* {
2925	vnode_t a_vp;
2926	uint32_t a_command;
2927	caddr_t a_data;
2928	int a_fflag;
2929	vfs_context_t a_context;
2930	} */
2931{
2932	int error;
2933	vnode_t vp = ap->a_vp;
2934
2935	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_IOCTL|DBG_FUNC_START, VTODE(vp), ap->a_command, 0, 0, 0);
2936	switch(ap->a_command) {
2937		case F_FULLFSYNC:
2938		{
2939			struct vnop_fsync_args fsync_args;
2940
2941			bzero(&fsync_args, sizeof(fsync_args));
2942			fsync_args.a_vp = ap->a_vp;
2943			fsync_args.a_waitfor = MNT_WAIT;
2944			fsync_args.a_context = ap->a_context;
2945
2946			error = msdosfs_vnop_fsync(&fsync_args);
2947			if (error) {
2948				goto exit;
2949			}
2950
2951			/* Call device ioctl to flush media track cache */
2952			error = VNOP_IOCTL(VTODE(vp)->de_pmp->pm_devvp, DKIOCSYNCHRONIZECACHE,
2953							   NULL, FWRITE, ap->a_context);
2954			if (error) {
2955				goto exit;
2956			}
2957
2958			break;
2959		} /* F_FULLFSYNC */
2960
2961		default:
2962		{
2963			error = ENOTTY;
2964			goto exit;
2965		}
2966	}/* switch(ap->a_command) */
2967
2968exit:
2969	KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_IOCTL|DBG_FUNC_START, error, 0, 0, 0, 0);
2970	return error;
2971}
2972
2973
2974
2975/* Global vfs data structures for msdosfs */
2976
2977typedef int     vnop_t(void *);
2978
2979int (**msdosfs_vnodeop_p)(void *);
2980static struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
2981	{ &vnop_default_desc,		(vnop_t *) vn_default_error },
2982	{ &vnop_lookup_desc,		(vnop_t *) msdosfs_vnop_lookup },
2983	{ &vnop_create_desc,		(vnop_t *) msdosfs_vnop_create },
2984	{ &vnop_mknod_desc,			(vnop_t *) msdosfs_vnop_mknod },
2985	{ &vnop_open_desc,			(vnop_t *) msdosfs_vnop_open },
2986	{ &vnop_close_desc,			(vnop_t *) msdosfs_vnop_close },
2987	{ &vnop_getattr_desc,		(vnop_t *) msdosfs_vnop_getattr },
2988	{ &vnop_setattr_desc,		(vnop_t *) msdosfs_vnop_setattr },
2989	{ &vnop_getxattr_desc,		(vnop_t *) msdosfs_vnop_getxattr },
2990	{ &vnop_setxattr_desc,		(vnop_t *) msdosfs_vnop_setxattr },
2991	{ &vnop_removexattr_desc,	(vnop_t *) msdosfs_vnop_removexattr },
2992	{ &vnop_listxattr_desc,		(vnop_t *) msdosfs_vnop_listxattr },
2993	{ &vnop_read_desc,			(vnop_t *) msdosfs_vnop_read },
2994	{ &vnop_write_desc,			(vnop_t *) msdosfs_vnop_write },
2995	{ &vnop_fsync_desc,			(vnop_t *) msdosfs_vnop_fsync },
2996	{ &vnop_remove_desc,		(vnop_t *) msdosfs_vnop_remove },
2997	{ &vnop_rename_desc,		(vnop_t *) msdosfs_vnop_rename },
2998	{ &vnop_mkdir_desc,			(vnop_t *) msdosfs_vnop_mkdir },
2999	{ &vnop_rmdir_desc,			(vnop_t *) msdosfs_vnop_rmdir },
3000	{ &vnop_readdir_desc,		(vnop_t *) msdosfs_vnop_readdir },
3001	{ &vnop_inactive_desc,		(vnop_t *) msdosfs_vnop_inactive },
3002	{ &vnop_reclaim_desc,		(vnop_t *) msdosfs_vnop_reclaim },
3003	{ &vnop_pathconf_desc,		(vnop_t *) msdosfs_vnop_pathconf },
3004	{ &vnop_pagein_desc,		(vnop_t *) msdosfs_vnop_pagein },
3005	{ &vnop_pageout_desc,		(vnop_t *) msdosfs_vnop_pageout },
3006	{ &vnop_blktooff_desc,		(vnop_t *) msdosfs_vnop_blktooff },
3007	{ &vnop_offtoblk_desc,		(vnop_t *) msdosfs_vnop_offtoblk },
3008  	{ &vnop_blockmap_desc,		(vnop_t *) msdosfs_vnop_blockmap },
3009	{ &vnop_strategy_desc,		(vnop_t *) msdosfs_vnop_strategy },
3010	{ &vnop_symlink_desc,		(vnop_t *) msdosfs_vnop_symlink },
3011	{ &vnop_readlink_desc,		(vnop_t *) msdosfs_vnop_readlink },
3012	{ &vnop_ioctl_desc, 		(vnop_t *) msdosfs_vnop_ioctl},
3013	{ &vnop_bwrite_desc,		(vnop_t *) vn_bwrite },
3014	{ NULL, NULL }
3015};
3016
3017extern int (**msdosfs_fat_vnodeop_p)(void *);
3018extern struct vnodeopv_entry_desc msdosfs_fat_vnodeop_entries[];
3019
3020struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
3021	{ &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
3022