pc_dir.c revision 54207fd2e1e7ed01d0416da8cf296dbef920fbfc
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2015 Joyent, Inc.
*/
#include <sys/sysmacros.h>
static int pc_dirempty(struct pcnode *);
static int pc_parsename(char *, char *, char *);
int ndirentries, int *errret);
/*
* Tunables
*/
int enable_long_filenames = 1;
/*
* Lookup a name in a directory. Return a pointer to the pc_node
* which represents the entry.
*/
int
char *namep, /* name to lookup */
{
int error;
return (ENOTDIR);
}
/*
* check now for changed disk, before any return(0)
*/
return (error);
/*
* Null component name is synonym for directory being searched.
*/
if (*namep == '\0') {
return (0);
}
/*
* The root directory does not have "." and ".." entries,
* so they are faked here.
*/
return (0);
}
}
if (error == 0) {
}
return (error);
}
/*
* Enter a name in a directory.
*/
int
char *namep, /* name of entry */
{
int error;
int boff;
/*
* Leading spaces are not allowed in DOS.
*/
if (*namep == ' ')
return (EINVAL);
/*
* If name is "." or "..", just look it up.
*/
if (pcpp) {
if (error)
return (error);
}
return (EEXIST);
}
return (EPERM);
}
/*
* Make sure directory has not been removed while fs was unlocked.
*/
return (ENOENT);
}
if (error == 0) {
if (pcpp) {
*pcpp =
}
struct pcdir *direntries;
int ndirentries;
/*
* The entry does not exist. Check write permission in
* directory to see if entry can be created.
*/
return (EPERM);
}
error = 0;
/*
* Make sure there is a slot.
*/
panic("pc_direnter: no slot\n");
if (ndirentries == -1) {
return (EINVAL);
}
if (offset == -1) {
return (ENOSPC);
}
/*
* Make an entry from the supplied attributes.
*/
if (direntries == NULL) {
return (error);
}
offset);
if (error) {
return (error);
}
if (error) {
return (error);
}
/*
* Get a pcnode for the new entry.
*/
/*
* Write out the new entry in the parent directory.
*/
if (!error) {
}
}
return (error);
}
/*
* Template for "." and ".." directory entries.
*/
static struct {
} dirtemplate = {
{
". ",
" ",
},
{
".. ",
" ",
}
};
/*
* Convert an attributes structure into the short filename entry
* and write out the whole entry.
*/
static int
{
int error;
int boff;
int i;
return (EOPNOTSUPP);
gethrestime(&now);
return (error);
ep->pcd_crtime_msec = 0;
/*
* Fields we don't use.
*/
ep->pcd_ntattr = 0;
/*
* Make dot and dotdot entries for a new directory.
*/
switch (cn) {
case PCF_FREECLUSTER:
return (ENOSPC);
case PCF_ERRORCLUSTER:
return (EIO);
}
if (error) {
return (EIO);
}
} else {
}
/* always modified */
if (error)
return (error);
}
if (error)
return (error);
}
*ep = direntries[i];
}
/* always modified */
if (error)
return (error);
}
return (0);
}
/*
* Remove a name from a directory.
*/
int
char *namep,
{
int error;
return (EPERM);
}
if (error)
return (error);
} else {
pcp =
}
if (error) {
return (error);
}
else if (!pc_dirempty(pcp))
} else {
}
} else {
}
if (error == 0) {
/*
* Mark the in core node and on disk entry
* as removed. The slot may then be reused.
* The files clusters will be deallocated
* when the last reference goes away.
*/
if (lfn_offset != -1) {
if (error) {
return (EIO);
}
} else {
}
if (error) {
return (EIO);
}
} else {
}
if (error == 0) {
} else {
}
}
return (error);
}
/*
* Determine whether a directory is empty.
*/
static int
{
int boff;
char c;
int error;
offset = 0;
for (;;) {
/*
* If offset is on a block boundary,
* read in the next directory block.
* Release previous if it exists.
*/
return (error);
}
}
if (PCDL_IS_LFN(ep)) {
&bp);
/*
* EINVAL means the lfn was invalid, so start with
* the next entry. Otherwise, an error occurred _or_
* the lfn is valid, either of which means the
* directory is not empty.
*/
continue;
else {
if (bp)
return (error);
}
}
c = ep->pcd_filename[0];
if (c == PCD_UNUSED)
break;
if ((c != '.') && (c != PCD_ERASED)) {
return (0);
}
return (0);
}
ep++;
}
return (1);
}
/*
* Rename a file.
*/
int
char *snm, /* source file name */
char *tnm, /* target file name */
{
int error;
int filecasechange = 0;
int oldisdir = 0;
/*
* Leading spaces are not allowed in DOS.
*/
if (*tnm == ' ')
return (EINVAL);
/*
* No dot or dotdot.
*/
return (EINVAL);
/*
* Get the source node. We'll jump back to here if trying to
* move on top of an existing file, after deleting that file.
*/
top:
if (error) {
return (error);
}
if (pcp)
/*
* is the rename invalid, i.e. rename("a", "a/a")
*/
if (svp)
return (EINVAL);
}
/*
* Are we just changing the case of an existing name?
*/
&error) == 0)) {
filecasechange = 1;
}
/*
* u8_strcmp detected an illegal character
*/
if (error)
return (EINVAL);
/*
* see if the target exists
*/
if (error == 0 && filecasechange == 0) {
/*
* Target exists. If it's a file, delete it. If it's
* a directory, bail.
*/
int newisdir;
/*
* Error cases (from rename(2)):
* old is dir, new is dir: EEXIST
* old is dir, new is nondir: ENOTDIR
* old is nondir, new is dir: EISDIR
*/
if (!newisdir) {
if (oldisdir) {
} else {
if (error == 0) {
goto top;
}
}
} else if (oldisdir) {
if (error == 0) {
goto top;
}
/* Follow rename(2)'s spec... */
}
} else {
}
}
struct pcdir *direntries;
int ndirentries;
int boff;
int size;
/*
* Rename the source.
*/
/*
* Delete the old name, and create a new name.
*/
if (ndirentries == -1) {
goto done;
}
/*
* first see if we have enough space to create the new
* name before destroying the old one.
*/
if (offset == -1) {
goto done;
}
if (error) {
goto done;
}
else
if (lfn_offset != -1) {
if (error) {
goto done;
}
} else {
}
if (error) {
goto done;
}
/*
* Make an entry from the supplied attributes.
*/
if (direntries == NULL) {
goto done;
}
offset);
if (error) {
goto done;
}
/* advance to short name */
if (error) {
goto done;
}
else
if (error) {
goto done;
}
/* No need to fix ".." if we're renaming within a dir */
goto done;
}
}
goto done;
}
}
if (error == 0) {
}
done:
return (error);
}
/*
* Fix the ".." entry of the child directory so that it points to the
* new parent directory instead of the old one.
*/
static int
{
int error = 0;
/*
* set the new child's ".." directory entry starting cluster to
* point to the new parent's starting cluster
*/
if (error) {
return (error);
}
ep++;
return (error);
}
if (error) {
return (EIO);
}
return (0);
}
/*
* Search a directory for an entry.
* The directory should be locked as this routine
* will sleep on I/O while searching.
*/
static int
char *namep, /* name to lookup */
{
int boff;
int error;
(void *)dp);
return (ENOTDIR);
}
/*
* Verify that the dp is still valid on the disk
*/
if (error)
return (error);
offset = 0;
for (;;) {
/*
* If offset is on a block boundary,
* read in the next directory block.
* Release previous if it exists.
*/
}
}
if (error)
return (error);
}
/*
* note empty slots, in case name is not found
*/
}
/*
* If unused we've hit the end of the directory
*/
break;
ep++;
continue;
}
if (PCDL_IS_LFN(ep)) {
if (lfn_offset != NULL)
*lfn_offset = t;
return (0);
}
continue;
}
return (0);
}
}
return (ENOENT);
}
/*
* Obtain the block at offset "offset" in file pcp.
*/
int
{
int size;
int error;
return (ENOENT);
}
if (error)
return (error);
return (EIO);
}
if (epp) {
*epp =
}
return (0);
}
/*
* Parse user filename into the pc form of "filename.extension".
* If names are too long for the format (and enable_long_filenames is set)
* it returns EINVAL (since either this name was read from the disk (so
* it must fit), _or_ we're trying to match a long file name (so we
* should fail). Tests for characters that are invalid in PCDOS and
* converts to upper case (unless foldcase is 0).
*/
static int
char *namep,
char *fnamep,
char *fextp)
{
int n;
char c;
n = PCFNAMESIZE;
c = *namep++;
if (c == 0)
return (EINVAL);
if (c == '.') {
/*
* check for "." and "..".
*/
*fnamep++ = c;
n--;
if (c = *namep++) {
return (EINVAL);
*fnamep++ = '.';
n--;
}
} else {
/*
* filename up to '.'
*/
do {
if (n-- > 0) {
c = toupper(c);
if (!pc_validchar(c))
return (EINVAL);
*fnamep++ = c;
} else {
/* not short */
return (EINVAL);
}
}
while (n-- > 0) { /* fill with blanks */
*fnamep++ = ' ';
}
/*
* remainder is extension
*/
n = PCFEXTSIZE;
if (c == '.') {
while ((c = *namep++) != '\0' && n--) {
c = toupper(c);
if (!pc_validchar(c))
return (EINVAL);
*fextp++ = c;
}
if (enable_long_filenames && (c != '\0')) {
/* not short */
return (EINVAL);
}
}
while (n-- > 0) { /* fill with blanks */
*fextp++ = ' ';
}
return (0);
}
/*
* Match a long filename entry with 'namep'. Also return failure
* if the long filename isn't valid.
*/
int
{
int error = 0;
if (error) {
return (ENOENT);
} else
return (error);
}
/* match */
return (0);
}
ep++;
/* If u8_strcmp detected an error it's sufficient to rtn ENOENT */
return (ENOENT);
}
/*
* Match a short filename entry with namep.
*/
int
{
char fname[PCFNAMESIZE];
char fext[PCFEXTSIZE];
int error;
ep++;
return (ENOENT);
}
if (error) {
ep++;
return (error);
}
/*
* found the file
*/
if (fname[0] == '.') {
else
} else {
}
return (0);
}
ep++;
return (ENOENT);
}
/*
* Remove a long filename entry starting at lfn_offset. It must be
* a valid entry or we wouldn't have gotten here. Also remove the
* short filename entry.
*/
static int
{
int boff;
int error = 0;
/*
* if we're in here, we know that the lfn is in the proper format
* of <series-of-lfn-entries> followed by <sfn-entry>
*/
for (;;) {
if (error)
return (error);
}
if (error)
return (error);
}
if (!PCDL_IS_LFN(ep)) {
/* done */
break;
}
/* zap it */
lfn_offset += sizeof (struct pcdir);
ep++;
}
/* now we're on the short entry */
if (error)
return (error);
}
return (0);
}
/*
* Find (and allocate) space in the directory denoted by
* 'pcp'. for 'ndirentries' pcdir structures.
* Return the offset at which to start, or -1 for failure.
*/
static offset_t
{
offset_t spaceavail = 0;
int boff;
int error;
while (spaceneeded > spaceavail) {
/*
* If offset is on a block boundary,
* read in the next directory block.
* Release previous if it exists.
*/
}
/* extend directory */
return (-1);
while (spaceneeded > spaceavail) {
if (error)
return (-1);
}
return (spaceoffset);
}
if (error)
return (-1);
}
spaceavail += sizeof (struct pcdir);
ep++;
continue;
}
spaceavail = 0;
ep++;
}
}
return (spaceoffset);
}
/*
* Return how many long filename entries are needed.
* A maximum of PCLFNCHUNKSIZE characters per entry, plus one for a
* short filename.
*/
static int
{
int ret;
if (enable_long_filenames == 0) {
return (1);
}
if (pc_is_short_file_name(namep, 0)) {
return (1);
}
}
/*
* convert to UTF-16 or UNICODE for calculating the entries
* needed. Conversion will consume at the most 512 bytes
*/
if (ret == 0) {
if (u16l % PCLFNCHUNKSIZE != 0)
ret++;
return (ret);
}
}
return (-1);
}
/*
* Allocate and return an array of pcdir structures for the passed-in
* name. ndirentries tells how many are required (including the short
* filename entry). Just allocate and fill them in properly here so they
* can be written out.
*/
static struct pcdir *
{
int i;
int nchars;
int error = 0;
char *nameend;
int ret;
if (ndirentries == 1) {
return (bpcdir);
}
/* Here we need to convert to UTF-16 or UNICODE for writing */
if (ret != 0) {
return (NULL);
}
u16l %= PCLFNCHUNKSIZE;
if (u16l != 0) {
nameend += 2;
} else {
}
/* short file name */
if (error) {
return (NULL);
}
for (i = 0; i < (ndirentries - 1); i++) {
/* long file name */
}
return (bpcdir);
}
static int
{
int rev;
int nchars;
int i, j;
char scratch[8];
int error = 0;
char shortname[20];
int force_tilde = 0;
/*
* generate a unique short file name based on the long input name.
*
* Say, for "This is a very long filename.txt" generate
* "THISIS~1.TXT", or "THISIS~2.TXT" if that's already there.
* Skip invalid short name characters in the long name, plus
* a couple NT skips (space and reverse backslash).
*
* Unfortunately, since this name would be hidden by the normal
* lookup routine, we need to look for it ourselves. But luckily
* we don't need to look at the lfn entries themselves.
*/
/*
* Strip off leading invalid characters.
* We need this because names like '.login' are now ok, but the
* short name needs to be something like LOGIN~1.
*/
if (*namep == ' ')
continue;
continue;
break;
}
dot++;
for (j = 0, i = 0; j < PCFEXTSIZE; i++) {
if (dot[i] == '\0')
break;
/* skip valid, but not generally good characters */
continue;
if (pc_validchar(dot[i]))
}
for (i = j; i < PCFEXTSIZE; i++)
fext[i] = ' ';
dot--;
} else {
for (i = 0; i < PCFEXTSIZE; i++) {
fext[i] = ' ';
}
}
/*
* We know we're a long name, not a short name (or we wouldn't
* be here at all. But if uppercasing ourselves would be a short
* name, then we can possibly avoid the ~N format.
*/
if (!force_tilde)
rev = 0;
else
rev = 1;
for (;;) {
if (rev) {
nchars--; /* ~ */
i = rev;
do {
nchars--;
i /= 10;
} while (i);
if (nchars <= 0) {
return (ENOSPC);
}
}
for (j = 0, i = 0; j < nchars; i++) {
break;
/* skip valid, but not generally good characters */
continue;
if (pc_validchar(namep[i]))
}
if (rev) {
}
fname[i] = ' ';
/* now see if it exists */
if (error == 0) {
/* found it */
rev++;
continue;
}
break;
rev++;
}
return (0);
}
/*
* Returns 1 if the passed-in filename is a short name, 0 if not.
*/
static int
{
int i;
char c;
for (i = 0; i < PCFNAMESIZE; i++, namep++) {
if (*namep == '\0')
return (1);
if (*namep == '.')
break;
if (foldcase)
else
c = *namep;
if (!pc_validchar(c))
return (0);
}
if (*namep == '\0')
return (1);
if (*namep != '.')
return (0);
namep++;
for (i = 0; i < PCFEXTSIZE; i++, namep++) {
if (*namep == '\0')
return (1);
if (foldcase)
else
c = *namep;
if (!pc_validchar(c))
return (0);
}
/* we should be done. If not... */
if (*namep == '\0')
return (1);
return (0);
}
/*
* We call this when we want to see if a short filename already exists
* in the filesystem as part of a long filename. When creating a short
* name (FILENAME.TXT from the user, or when generating one for a long
* filename), we cannot allow one that is part of a long filename.
* pc_findentry will find all the names that are visible (long or short),
* but will not crack any long filename entries.
*/
static int
{
int offset = 0;
int match = 0;
int boff;
int error = 0;
for (;;) {
}
break;
if (error) {
return (1);
}
}
if (PCDL_IS_LFN(ep) ||
ep++;
continue;
}
break;
/*
* in use, and a short file name (either standalone
* or associated with a long name
*/
match = 1;
break;
}
ep++;
}
if (bp) {
}
return (match);
}
{
return (cn);
} else {
}
}
void
{
} else {
}
}