Exemple #1
0
/* Implement io_read as described in <hurd/io.defs>. */
kern_return_t
diskfs_S_io_read (struct protid *cred,
		  char **data,
		  mach_msg_type_number_t *datalen,
		  off_t offset,
		  mach_msg_type_number_t maxread)
{
  struct node *np;
  int err;
  off_t off = offset;
  char *buf;
  int ourbuf = 0;

  if (!cred)
    return EOPNOTSUPP;

  np = cred->po->np;
  if (!(cred->po->openstat & O_READ))
    return EBADF;

  pthread_mutex_lock (&np->lock);

  iohelp_get_conch (&np->conch);

  if (off == -1)
    off = cred->po->filepointer;
  if (off < 0)
    {
      pthread_mutex_unlock (&np->lock);
      return EINVAL;
    }

  if (off > np->dn_stat.st_size)
    maxread = 0;
  else if (off + (off_t) maxread > np->dn_stat.st_size)
    maxread = np->dn_stat.st_size - off;

  if (maxread > *datalen)
    {
      ourbuf = 1;
      buf = mmap (0, maxread, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);     
      *data = buf;
    }
  else
    buf = *data;

  *datalen = maxread;

  if (maxread == 0)
    err = 0;
  else if (S_ISLNK (np->dn_stat.st_mode))
    {
      /* Read from a symlink.  */
      if (! diskfs_read_symlink_hook)
	err = EINVAL;
      else
	{
	  if (off == 0 && maxread == np->dn_stat.st_size)
	    err = (*diskfs_read_symlink_hook)(np, buf);
	  else
	    {
	      char *whole_link = alloca (np->dn_stat.st_size);
	      err = (*diskfs_read_symlink_hook)(np, whole_link);
	      if (! err)
		memcpy (buf, whole_link + off, maxread);
	    }
	}
    }
  else
    err = EINVAL;		/* Use read below.  */

  if (err == EINVAL)
    err = _diskfs_rdwr_internal (np, buf, off, datalen, 0,
				 cred->po->openstat & O_NOATIME);

  if (diskfs_synchronous)
    diskfs_node_update (np, 1);	/* atime! */

  if (offset == -1 && !err)
    cred->po->filepointer += *datalen;

  if (err && ourbuf)
    munmap (buf, maxread);

  pthread_mutex_unlock (&np->lock);
  return err;
}
Exemple #2
0
/* Implement io_write as described in <hurd/io.defs>. */
kern_return_t
diskfs_S_io_write (struct protid *cred,
		   char *data,
		   mach_msg_type_number_t datalen,
		   off_t offset,
		   mach_msg_type_number_t *amt)
{
  struct node *np;
  error_t err;
  off_t off = offset;

  if (!cred)
    return EOPNOTSUPP;

  np = cred->po->np;
  if (!(cred->po->openstat & O_WRITE))
    return EBADF;

  pthread_mutex_lock (&np->lock);

  assert (!S_ISDIR(np->dn_stat.st_mode));

  iohelp_get_conch (&np->conch);

  if (off == -1)
    {
      if (cred->po->openstat & O_APPEND)
	cred->po->filepointer = np->dn_stat.st_size;
      off = cred->po->filepointer;
    }
  if (off < 0)
    {
      err = EINVAL;
      goto out;
    }

  while (off + (off_t) datalen > np->allocsize)
    {
      err = diskfs_grow (np, off + datalen, cred);
      if (diskfs_synchronous)
	diskfs_node_update (np, 1);
      if (err)
	goto out;
      if (np->filemod_reqs)
	diskfs_notice_filechange (np, FILE_CHANGED_EXTEND, 0, off + datalen);
    }

  if (off + (off_t) datalen > np->dn_stat.st_size)
    {
      np->dn_stat.st_size = off + datalen;
      np->dn_set_ctime = 1;
      if (diskfs_synchronous)
	diskfs_node_update (np, 1);
    }

  *amt = datalen;
  err = _diskfs_rdwr_internal (np, data, off, amt, 1, 0);

  if (!err && offset == -1)
    cred->po->filepointer += *amt;

  if (!err
      && ((cred->po->openstat & O_FSYNC) || diskfs_synchronous))
    diskfs_file_update (np, 1);

  if (!err && np->filemod_reqs)
    diskfs_notice_filechange (np, FILE_CHANGED_WRITE, off, off + *amt);
 out:
  pthread_mutex_unlock (&np->lock);
  return err;
}
/* Implement dir_rename as described in <hurd/fs.defs>. */
kern_return_t
diskfs_S_dir_rename (struct protid *fromcred,
		     char *fromname,
		     struct protid *tocred,
		     char *toname,
		     int excl)
{
  struct node *fdp, *tdp, *fnp, *tnp, *tmpnp;
  error_t err;
  struct dirstat *ds = alloca (diskfs_dirstat_size);
  
  if (!fromcred)
    return EOPNOTSUPP;

  /* Verify that tocred really is a port to us. */
  if (! tocred)
    return EXDEV;

  if (!strcmp (fromname, ".") || !strcmp (fromname, "..")
   || !strcmp (toname,   ".") || !strcmp (toname,   ".."))
    return EINVAL;

  if (tocred->po->shadow_root != fromcred->po->shadow_root)
    /* Same translator, but in different shadow trees.  */
    return EXDEV;

  if (diskfs_check_readonly ())
    return EROFS;

  fdp = fromcred->po->np;
  tdp = tocred->po->np;

 try_again:
  /* Acquire the source; hold a reference to it.  This 
     will prevent anyone from deleting it before we create
     the new link. */
  mutex_lock (&fdp->lock);
  err = diskfs_lookup (fdp, fromname, LOOKUP, &fnp, 0, fromcred);
  mutex_unlock (&fdp->lock);
  if (err == EAGAIN)
    err = EINVAL;
  if (err)
    return err;

  if (S_ISDIR (fnp->dn_stat.st_mode))
    {
      mutex_unlock (&fnp->lock);
      if (!mutex_try_lock (&renamedirlock))
	{
	  diskfs_nrele (fnp);
	  mutex_lock (&renamedirlock);
	  goto try_again;
	}
      err = diskfs_rename_dir (fdp, fnp, fromname, tdp, toname, fromcred,
			       tocred);
      if (diskfs_synchronous)
	{
	  mutex_lock (&fdp->lock);
	  diskfs_file_update (fdp, 1);
	  mutex_unlock (&fdp->lock);
	  
	  mutex_lock (&fnp->lock);
	  diskfs_file_update (fnp, 1);
	  mutex_unlock (&fnp->lock);

	  mutex_lock (&tdp->lock);
	  diskfs_file_update (tdp, 1);
	  mutex_unlock (&tdp->lock);
	}
      
      diskfs_nrele (fnp);
      mutex_unlock (&renamedirlock);
      if (!err)
	/* MiG won't do this for us, which it ought to. */
	mach_port_deallocate (mach_task_self (), tocred->pi.port_right);
      return err;
    }

  mutex_unlock (&fnp->lock);

  /* We now hold no locks */

  /* Link the node into the new directory. */
  mutex_lock (&tdp->lock);
  
  err = diskfs_lookup (tdp, toname, RENAME, &tnp, ds, tocred);
  if (err == EAGAIN)
    err = EINVAL;
  else if (!err && excl)
    {
      err = EEXIST;
      diskfs_nput (tnp);
    }
  if (err && err != ENOENT)
    {
      diskfs_drop_dirstat (tdp, ds);
      diskfs_nrele (fnp);
      mutex_unlock (&tdp->lock);
      return err;
    }

  /* rename("foo", "link-to-foo") is guaranteed to return 0 and
     do nothing by Posix. */
  if (tnp == fnp)
    {
      diskfs_drop_dirstat (tdp, ds);
      diskfs_nrele (fnp);
      diskfs_nput (tnp);
      mutex_unlock (&tdp->lock);
      mach_port_deallocate (mach_task_self (), tocred->pi.port_right);
      return 0;
    }

  /* rename("foo", dir) should fail. */
  if (tnp && S_ISDIR (tnp->dn_stat.st_mode))
    {
      diskfs_drop_dirstat (tdp, ds);
      diskfs_nrele (fnp);
      diskfs_nput (tnp);
      mutex_unlock (&tdp->lock);
      return EISDIR;
    }

  mutex_lock (&fnp->lock);

  /* Increment the link count for the upcoming link */
  if (fnp->dn_stat.st_nlink == diskfs_link_max - 1)
    {
      diskfs_drop_dirstat (tdp, ds);
      diskfs_nput (fnp);
      if (tnp)
        diskfs_nput (tnp);
      mutex_unlock (&tdp->lock);
      return EMLINK;
    }
  fnp->dn_stat.st_nlink++;
  fnp->dn_set_ctime = 1;
  diskfs_node_update (fnp, 1);

  if (tnp)
    {
      err = diskfs_dirrewrite (tdp, tnp, fnp, toname, ds);
      if (!err)
	{
	  tnp->dn_stat.st_nlink--;
	  tnp->dn_set_ctime = 1;
	  if (diskfs_synchronous)
	    diskfs_node_update (tnp, 1);
	}
      diskfs_nput (tnp);
    }
  else
    err = diskfs_direnter (tdp, toname, fnp, ds, tocred);

  if (diskfs_synchronous)
    diskfs_node_update (tdp, 1);

  mutex_unlock (&tdp->lock);
  mutex_unlock (&fnp->lock);
  if (err)
    {
      diskfs_nrele (fnp);
      return err;
    }

  /* We now hold no locks */

  /* Now we remove the source.  Unfortunately, we haven't held 
     fdp locked (nor could we), so someone else might have already
     removed it. */
  mutex_lock (&fdp->lock);
  err = diskfs_lookup (fdp, fromname, REMOVE, &tmpnp, ds, fromcred);
  if (err)
    {
      diskfs_drop_dirstat (tdp, ds);
      mutex_unlock (&fdp->lock);
      diskfs_nrele (fnp);
      return err;
    }

  if (tmpnp != fnp)
    {
      /* This is no longer the node being renamed, so just return. */
      diskfs_drop_dirstat (tdp, ds);
      diskfs_nput (tmpnp);
      diskfs_nrele (fnp);
      mutex_unlock (&fdp->lock);
      mach_port_deallocate (mach_task_self (), tocred->pi.port_right);
      return 0;
    }
  
  diskfs_nrele (tmpnp);

  err = diskfs_dirremove (fdp, fnp, fromname, ds);
  if (diskfs_synchronous)
    diskfs_node_update (fdp, 1);

  fnp->dn_stat.st_nlink--;
  fnp->dn_set_ctime = 1;
  
  if (diskfs_synchronous)
    diskfs_node_update (fnp, 1);
  
  diskfs_nput (fnp);
  mutex_unlock (&fdp->lock);
  if (!err)
    mach_port_deallocate (mach_task_self (), tocred->pi.port_right);

  return err;
}
Exemple #4
0
/* Implement the diskfs_lookup callback from the diskfs library.  See
   <hurd/diskfs.h> for the interface specification.  */
error_t
diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type,
		    struct node **npp, struct dirstat *ds, struct protid *cred)
{
  error_t err;
  ino_t inum;
  int namelen;
  int spec_dotdot;
  struct node *np = 0;
  int retry_dotdot = 0;
  vm_prot_t prot =
    (type == LOOKUP) ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_WRITE);
  memory_object_t memobj;
  vm_address_t buf = 0;
  vm_size_t buflen = 0;
  int blockaddr;
  int idx, lastidx;
  int looped;

  if ((type == REMOVE) || (type == RENAME))
    assert (npp);

  if (npp)
    *npp = 0;

  spec_dotdot = type & SPEC_DOTDOT;
  type &= ~SPEC_DOTDOT;

  namelen = strlen (name);

  if (namelen > FAT_NAME_MAX)
    return ENAMETOOLONG;
  
 try_again:
  if (ds)
    {
      ds->type = LOOKUP;
      ds->mapbuf = 0;
      ds->mapextent = 0;
    }
  if (buf)
    {
      munmap ((caddr_t) buf, buflen);
      buf = 0;
    }
  if (ds && (type == CREATE || type == RENAME))
    ds->stat = LOOKING;

  /* Map in the directory contents. */
  memobj = diskfs_get_filemap (dp, prot);

  if (memobj == MACH_PORT_NULL)
    return errno;

  buf = 0;
  /* We allow extra space in case we have to do an EXTEND.  */
  buflen = round_page (dp->dn_stat.st_size + DIRBLKSIZ);
  err = vm_map (mach_task_self (),
                &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
  mach_port_deallocate (mach_task_self (), memobj);

  inum = 0;

  diskfs_set_node_atime (dp);

  /* Start the lookup at DP->dn->dir_idx.  */
  idx = dp->dn->dir_idx;
  if (idx << LOG2_DIRBLKSIZ > dp->dn_stat.st_size)
    idx = 0;                    /* just in case */
  blockaddr = buf + (idx << LOG2_DIRBLKSIZ);
  looped = (idx == 0);
  lastidx = idx;
  if (lastidx == 0)
    lastidx = dp->dn_stat.st_size >> LOG2_DIRBLKSIZ;

  while (!looped || idx < lastidx)
    {
      err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
      if (!err)
        {
          dp->dn->dir_idx = idx;
          break;
        }
      if (err != ENOENT)
        {
          munmap ((caddr_t) buf, buflen);
          return err;
        }

      blockaddr += DIRBLKSIZ;
      idx++;
      if (blockaddr - buf >= dp->dn_stat.st_size && !looped)
        {
          /* We've gotten to the end; start back at the beginning.  */
          looped = 1;
          blockaddr = buf;
          idx = 0;
        }
    }

  diskfs_set_node_atime (dp);
  if (diskfs_synchronous)
    diskfs_node_update (dp, 1);

  /* If err is set here, it's ENOENT, and we don't want to
     think about that as an error yet.  */
  err = 0;

  if (inum && npp)
    {
      if (namelen != 2 || name[0] != '.' || name[1] != '.')
        {
          if (inum == dp->cache_id)
            {
              np = dp;
              diskfs_nref (np);
            }
          else
            {
              err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
              if (err)
                goto out;
            }
        }

      /* We are looking up "..".  */
      /* Check to see if this is the root of the filesystem.  */
      else if (dp == diskfs_root_node)
        {
          err = EAGAIN;
          goto out;
        }

      /* We can't just do diskfs_cached_lookup, because we would then
         deadlock.  So we do this.  Ick.  */
      else if (retry_dotdot)
        {
          /* Check to see that we got the same answer as last time.  */
          if (inum != retry_dotdot)
            {
              /* Drop what we *thought* was .. (but isn't any more) and
                 try *again*.  */
              diskfs_nput (np);
              mutex_unlock (&dp->lock);
              err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
              mutex_lock (&dp->lock);
              if (err)
                goto out;
              retry_dotdot = inum;
              goto try_again;
            }
          /* Otherwise, we got it fine and np is already set properly.  */
        }
      else if (!spec_dotdot)
        {
          /* Lock them in the proper order, and then
             repeat the directory scan to see if this is still
             right.  */
          mutex_unlock (&dp->lock);
          err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
          mutex_lock (&dp->lock);
          if (err)
            goto out;
          retry_dotdot = inum;
          goto try_again;
        }

      /* Here below are the spec dotdot cases.  */
      else if (type == RENAME || type == REMOVE)
        np = ifind (inum);

      else if (type == LOOKUP)
        {
          diskfs_nput (dp);
          err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
          if (err)
            goto out;
        }
      else
        assert (0);
    }

  if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING)
    {
      /* We didn't find any room, so mark ds to extend the dir.  */
      ds->type = CREATE;
      ds->stat = EXTEND;
      ds->idx = dp->dn_stat.st_size >> LOG2_DIRBLKSIZ;
    }
Exemple #5
0
/* Implement dir_unlink as described in <hurd/fs.defs>. */
kern_return_t
diskfs_S_dir_unlink (struct protid *dircred,
		     char *name)
{
  struct node *dnp;
  struct node *np;
  struct dirstat *ds = alloca (diskfs_dirstat_size);
  error_t err;
  mach_port_t control = MACH_PORT_NULL;

  if (!dircred)
    return EOPNOTSUPP;

  dnp = dircred->po->np;
  if (diskfs_check_readonly ())
    return EROFS;

  pthread_mutex_lock (&dnp->lock);

  err = diskfs_lookup (dnp, name, REMOVE, &np, ds, dircred);
  if (err == EAGAIN)
    err = EPERM;	/* 1003.1-1996 5.5.1.4 */
  if (err)
    {
      diskfs_drop_dirstat (dnp, ds);
      pthread_mutex_unlock (&dnp->lock);
      return err;
    }

  /* This isn't the BSD behavior, but it is Posix compliant and saves
     us on several race conditions.*/
  if (S_ISDIR(np->dn_stat.st_mode))
    {
      if (np == dnp)		/* gotta catch '.' */
	diskfs_nrele (np);
      else
	diskfs_nput (np);
      diskfs_drop_dirstat (dnp, ds);
      pthread_mutex_unlock (&dnp->lock);
      return EPERM;		/* 1003.1-1996 5.5.1.4 */
    }

  err = diskfs_dirremove (dnp, np, name, ds);
  if (diskfs_synchronous)
    diskfs_node_update (dnp, 1);
  if (err)
    {
      diskfs_nput (np);
      pthread_mutex_unlock (&dnp->lock);
      return err;
    }

  np->dn_stat.st_nlink--;
  np->dn_set_ctime = 1;
  if (diskfs_synchronous)
    diskfs_node_update (np, 1);

  if (np->dn_stat.st_nlink == 0)
    fshelp_fetch_control (&np->transbox, &control);

  /* This check is necessary because we might get here on an error while
     checking the mode on something which happens to be `.'. */
  if (np == dnp)
    diskfs_nrele (np);
  else
    diskfs_nput (np);
  pthread_mutex_unlock (&dnp->lock);

  if (control)
    {
      fsys_goaway (control, FSYS_GOAWAY_UNLINK);
      mach_port_deallocate (mach_task_self (), control);
    }

  return err;
}