Subversion Repositories shark

Rev

Rev 2 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Project: HARTIK (HA-rd R-eal TI-me K-ernel)
 *
 * Coordinators: Giorgio Buttazzo <giorgio@sssup.it>
 *               Gerardo Lamastra <gerardo@sssup.it>
 *
 * Authors     : Massimiliano Giorgi <massy@hartik.sssup.it>
 * (see authors.txt for full list of hartik's authors)
 *
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
 *
 * http://www.sssup.it
 * http://retis.sssup.it
 * http://hartik.sssup.it
 */


/*
 * Copyright (C) 1999 Massimiliano Giorgi
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */


/*
 * CVS :        $Id: dentry.c,v 1.1.1.1 2002-03-29 14:12:50 pj Exp $
 *
 * File:        $File$
 * Revision:    $Revision: 1.1.1.1 $
 * Last update: $Date: 2002-03-29 14:12:50 $
 */


#include <fs/util.h>
#include <fs/types.h>
#include <fs/assert.h>
#include "fs.h"
#include "super.h"
#include "superop.h"
#include "dentry.h"
#include "dentryop.h"
#include "inode.h"
#include "inodeop.h"
#include "fsconst.h"
#include "mutex.h"

#include "debug.h"

/*
 * DEBUG
 */


/* if defined:
 * a short message is printed on console when a task go in/out of
 * functions dentry_find() & dentry_unlock()
 * (after  and before the lock/unlock of the mutex!)
 */

#define DEBUGFINDUNLOCK KERN_DEBUG
#undef DEBUGFINDUNLOCK

#ifdef DEBUGFINDUNLOCK
#define printdd(fmt,pid) kern_printf(fmt,pid)
#else
#define printdd(fmt,pid)
#endif

/* if you modify this you must modify MAXINODE */
#define MAXDENTRY 128

static __fs_mutex_t mutex;
static __fs_fastmutex_t mutexreq;
static struct dentry *freelist;
//static struct dentry *leaflist;
static struct dentry direntry[MAXDENTRY];

static struct dentry *root_direntry=NULL;

#ifdef FSMUTEXCHECK
void debug_dump_dentry_mutex(void)
{
  //printk(KERN_DEBUG "dentry mutex: %i",(int)mutex);
  //printk(KERN_DEBUG "dentry mutex req: %i",(int)mutexreq);
}

//static __fs_mutex_t regmutex;
//static __fs_mutex_t regmutexreq;

int debug_check_dentry_mutex(void)
{
  /*
  if (mutex!=regmutex) {
    printk(KERN_DEBUG "dentry mutex CHANGED! (prev: %i)",regmutex);
    return 0;
  }
  if (mutexreq!=regmutexreq) {
    printk(KERN_DEBUG "dentry mutexreq CHANGED! (prev: %i)",regmutexreq);
    return 0;
  }
  */

  return -1;
}

void debug_save_dentry_mutex(void)
{
  //regmutex=mutex;
  //regmutexreq=mutexreq;
}
#endif

int dentry_init(void)
{
  int i;
 
  __fs_mutex_init(&mutex);
  __fs_fastmutex_init(&mutexreq);

#ifdef FSMUTEXCHECK
  debug_save_dentry_mutex();
#endif
 
  freelist=direntry;
  for (i=0;i<MAXDENTRY;i++) {
    memset(direntry+i,0,sizeof(struct dentry));
    magic_set(direntry[i].magic,DENTRY_MAGIC);
    direntry[i].d_next=direntry+i+1;
    direntry[i].d_lock=-1;
  }
  direntry[MAXDENTRY-1].d_next=NULL;
  return 0;
}

static __inline__ struct dentry *__get_dentry(void)
{
  struct dentry *den;
  if (freelist==NULL) return NULL;    
  den=freelist;
  freelist=den->d_next;
 
  _assert(den->d_lock==-1);
  magic_assert(den->magic,DENTRY_MAGIC,"get_dentry: dentry MAGIC failed!");
 
  den->d_lock=0;
  return den;
}

/* get a dentry from the free queue */
struct dentry *get_dentry(void)
{
  struct dentry *den;
  __fs_fastmutex_lock(&mutexreq);
  den=__get_dentry();
  __fs_fastmutex_unlock(&mutexreq);
  return den;
}

static __inline__ void __free_dentry(struct dentry *den)
{
  _assert(den>=direntry&&den<direntry+MAXDENTRY);
  _assert(den->d_lock==0);
  magic_assert(den->magic,DENTRY_MAGIC,"free_dentry: dentry MAGIC failed!");
 
  den->d_lock=-1;
  den->d_next=freelist;
  freelist=den;
}

/* insert dentry into the free queue */
void free_dentry(struct dentry *den)
{
  __fs_fastmutex_lock(&mutexreq);
  __free_dentry(den);
  __fs_fastmutex_unlock(&mutexreq);
}

static void __insert_dentry(struct dentry *den, struct dentry *parent)
{
  _assert(den>=direntry&&den<direntry+MAXDENTRY);
  magic_assert(den->magic,DENTRY_MAGIC,"insert_dentry: dentry MAGIC failed!");
 
  den->d_acc=gettimek();
  den->d_parent=parent;
  den->d_child=NULL;
  den->d_prev=NULL;
  den->d_next=parent->d_child;
  if (parent->d_child!=NULL)
    parent->d_child->d_prev=den;
  parent->d_child=den;
}

/* insert a dentry into the tree */
void insert_dentry(struct dentry *den, struct dentry *parent)
{
  __fs_mutex_lock(&mutex);
  __insert_dentry(den,parent);
  __fs_mutex_unlock(&mutex);
}

static void __remove_dentry_only(struct dentry *den)
{
  _assert(den>=direntry&&den<direntry+MAXDENTRY);
  magic_assert(den->magic,DENTRY_MAGIC,"remove_dentry: dentry MAGIC failed!");
  _assert(den->d_lock==0);
 
  if (den->d_prev!=NULL)
    den->d_prev->d_next=den->d_next;
  else
    den->d_parent->d_child=den->d_next;
  if (den->d_next!=NULL)
    den->d_next->d_prev=den->d_prev;
}

static __inline__ void __remove_dentry(struct dentry *den)
{
  __remove_dentry_only(den);
  unlock_inode(den->d_inode);  
}

/* remove a dentry from the tree */
void remove_dentry(struct dentry *den)
{
  __fs_mutex_lock(&mutex);
  __remove_dentry(den);
  __fs_mutex_unlock(&mutex);
}

/* find the oldest dentry into the tree */
/*
 * (potrebbe essere fatto in maniera non ricorsiva usando un
 * puntatore alle foglie e scandendo questa lista invece di visitare
 * tutto l'albero!)
 *
 * si potrebbe usare un task NRT periodico per mantenere liberi (se
 * possibile l'X% delle dentry libere.
 */


static struct dentry *__recurse(struct dentry *den, struct dentry *act)
{
  while (den!=NULL) {    
    if (den->d_child!=NULL) {
      act=__recurse(den->d_child,act);
    } else {
      if (den->d_lock==0) {
        if (act==NULL)
          act=den;
        else {
          if (den->d_acc<act->d_acc)
            act=den;
        }
      }      
    }
    den=den->d_next;
  }
  return act;
}

struct dentry *__purge_dentry(void)
{
  struct dentry *act;
  act=__recurse(root_direntry,NULL);
  if (act!=NULL) __remove_dentry(act);
  return act;
}

/* find & remove the oldest dentry from the tree */
struct dentry *purge_dentry(void)
{
  struct dentry *act;
  __fs_mutex_lock(&mutex);
  act=__purge_dentry();  
  __fs_mutex_unlock(&mutex);
  return act;
}

struct dentry *__catch_dentry(void)
{
  struct dentry *den;
  den=__get_dentry();
  if (den==NULL) den=__purge_dentry();
  return den;
}

/* get a dentry by searching into the free space or purging an old one */
/*
static struct dentry *catch_dentry(void)
{
  struct dentry *den;
  __fs_mutex_lock(&mutex);
  den=__catch_dentry();  
  __fs_mutex_unlock(&mutex);
  return den;
}
*/


/*---------------------*/

int set_root_dentry(struct super_block *sb)
{
  struct dentry *den;

  __fs_mutex_lock(&mutex);
  if (sb==NULL) {
    _assert(root_direntry!=NULL);
    root_direntry->d_lock--;
    _assert(root_direntry->d_lock==1);
    root_direntry=NULL;
    __fs_mutex_unlock(&mutex);
    return 0;
  }
  _assert(root_direntry==NULL);
  __fs_mutex_unlock(&mutex);
 
  den=get_dentry();
  if (den==NULL) return -1;

  den->d_next=NULL;
  den->d_prev=NULL;
  den->d_parent=NULL;
  den->d_child=NULL;
 
  den->d_acc=0;
  den->d_name.nameptr=ROOTDIRNAME;

  den->d_op=sb->sb_dop;
  den->d_sb=sb;
  den->d_inode=sb->sb_root;
 
  den->d_lock=1;
 
  sb->sb_droot=den;
  root_direntry=den;

  return 0;
}

struct dentry *get_root_dentry(void)
{
  _assert(root_direntry!=NULL);
  return root_direntry;
}

#ifdef DEBUG_FIND_DUMP
static void dump_dentry(struct dentry *ptr)
{
  printk7(KERN_DEBUG "dentry dump:");
  printk7(KERN_DEBUG "  name : %s",QSTRNAME(&ptr->d_name));
  printk7(KERN_DEBUG "  lock : %i",ptr->d_lock);
  printk7(KERN_DEBUG "  inode: %i",ptr->d_inode->i_st.st_ino);
}
#else
#define dump_dentry(x)
#endif

/*--------------*/

/*
 * flags for createflags:
 * DENTRY_NOCREATE   -> can't create a new inode
 * DENTRY_CANCREATE  -> an inode (only the last) can be created
 * DENTRY_MUSTCREATE -> the last inode MUST be created (it must no exist)
 *
 * return flags for createflags:
 * DENTRY_CREATED -> the last inode has been created
 * DENTRY_EXIST   -> (error) the last inode already exist
 *
 * return NULL on error (not found or DENTRY_EXIST)
 *
 * NB:
 * if createflag is NULL a DENTRY_NOCREATE is assumed
 * 'act' can be NULL if pathname is an absolute path
 */


struct dentry *find_dentry_from_ext(struct dentry *act,
                                    char *pathname,
                                    int *createflag)
{
  struct dentry *ptr;
  struct dentry *den;
  struct inode *ino;
  struct qstr str;
  char *sp;
  char *s;
  int  found,created;

  /* too much 'goto' :-( and 'if' */

  sp=pathname;
  if (*pathname==DIRDELIMCHAR) {
    act=root_direntry;
    sp++;
  }
  if (act==NULL) return NULL;
 
  printdd("°f<%i>",exec_shadow);
  __fs_mutex_lock(&mutex);
  printdd("±f<%i>",exec_shadow);
 
  if (*sp=='\0') goto END0;

  created=0;
  found=1;
  while (found) {
    s=strchr(sp,DIRDELIMCHAR);
    if (s!=NULL) *s='\0';
   
    printk7("dentry find: searching for %s...",sp);

    /* for handle special directory entry "." and ".." */
    if (*sp=='.') {
      if (*sp=='\0') {
        /* found "." special directory */
        printk7("dentry find: special directory entry '.'");
        found=1;
        if (s==NULL) goto END0;
        *s=DIRDELIMCHAR;
        sp=s+1;
        s=strchr(sp,DIRDELIMCHAR);
        continue;      
      }
      if (*sp=='.') {
        if (*sp=='\0') {
          /* found ".." special directory */
          printk7("dentry find: special directory entry '..'");
          found=1;
          if (act->d_parent!=NULL) act=act->d_parent;
          if (s==NULL) goto END0;
          *s=DIRDELIMCHAR;
          sp=s+1;
          s=strchr(sp,DIRDELIMCHAR);
          continue;    
        }      
      }
    }
   
    ptr=act->d_child;
    found=0;
    str.nameptr=sp;
    while (ptr!=NULL) {
      if (ptr->d_op->d_compare(ptr,&ptr->d_name,&str)==0) {

        printk7("dentry find: found!");
       
        found=1;
        act=ptr;
        if (s==NULL) goto END0;
        *s=DIRDELIMCHAR;
        sp=s+1;
        s=strchr(sp,DIRDELIMCHAR);
        break;
      }
      ptr=ptr->d_next;
    }        
  }

  printk7("dentry find: NOT found!");
 
  for (;;) {
    den=__catch_dentry();
    if (den==NULL) {
      if (s!=NULL) *s=DIRDELIMCHAR;
      printk7("dentry find: can't have new struct dentry!");
      act=NULL;
      goto END1;
    }

    den->d_acc=0;
    den->d_name.nameptr=NULL;
    strcpy(den->d_name.name,sp);
    den->d_lock=0;
    den->d_op=act->d_op;
    den->d_sb=act->d_sb;

    printk7("dentry find: looking up for %s....",sp);
    dump_dentry(act);
   
    ino=act->d_inode->i_op->lookup(act->d_inode,den);

    printk7("dentry find: end looking");
   
    if (ino==NULL) {

      printk7("dentry find: NOT found!");
      if (s!=NULL) *s=DIRDELIMCHAR;      

      if (createflag==NULL) {
        __free_dentry(den);
        act=NULL;
        goto END1;
      }
     
      if ((s==NULL)&&(*createflag&DENTRY_CANCREATE)) {

        printk7("dentry find: check for read-only fs");
        if (!(act->d_sb->sb_mopts.flags&MOUNT_FLAG_RW)) {
          printk7("dentry find: read-only fs.. creating new inode fails");
          __free_dentry(den);
          act=NULL;
          goto END1;
        }
       
        printk7("dentry find: try to create a new inode");
        ino=act->d_inode->i_op->create(act->d_inode,den);

        if (ino==NULL) {  
          printk7("dentry find: inode creation fail");
          __free_dentry(den);
          act=NULL;
          goto END1;
        }
        printk7("dentry find: new inode created");
        created=1;
       
      } else {
        __free_dentry(den);
        act=NULL;
        goto END1;
      }
    }
    den->d_inode=ino;
   
    insert_inode(ino);  
    printk7("dentry find: found!");
   
    __insert_dentry(den,act);
    act=den;
 
    if (s==NULL) goto END2;
   
    sp=s+1;
    *s=DIRDELIMCHAR;    
    s=strchr(sp,DIRDELIMCHAR);
    if (s!=NULL) *s='\0';
  }

  /* for safety */
  _assert(0==-1);

END0:
  /* 'if found' we are here */
  if (createflag!=NULL) {
    *createflag&=~DENTRY_CREATEMASK;
    if (*createflag&DENTRY_MUSTCREATE) {
      _assert(act!=NULL);
      *createflag|=DENTRY_EXIST;
      act=NULL;
    }
  }
  goto END;

END1:
  /* 'if error' we are here */
  _assert(act==NULL);
  if (createflag!=NULL) *createflag&=~DENTRY_CREATEMASK;
  goto END;

END2:
  /* 'if ok' (found or created) we are here */
  _assert(act!=NULL);
  if (createflag!=NULL) {
    if (created) {
      *createflag&=~DENTRY_CREATEMASK;           
      *createflag|=DENTRY_CREATED;
    } else if ((*createflag&DENTRY_MUSTCREATE)==DENTRY_MUSTCREATE) {
      *createflag&=~DENTRY_CREATEMASK;           
      *createflag|=DENTRY_EXIST;
      act=NULL;
    } else
      *createflag&=~DENTRY_CREATEMASK;                   
  }
  goto END;    

END:
  if (act!=NULL) act->d_lock++;

  printdd("²f<%i>",exec_shadow);
  __fs_mutex_unlock(&mutex);
  printdd("Ûf<%i>",exec_shadow);
  return act;
 
}

void unlock_dentry(struct dentry *den)
{
  printdd("°u<%i>",exec_shadow);
  __fs_mutex_lock(&mutex);
  printdd("±u<%i>",exec_shadow);

  _assert(den>=direntry&&den<direntry+MAXDENTRY);
  if (den->d_lock<=0) {
    printk(KERN_DEBUG "unlock_dentry on %s",QSTRNAME(&den->d_name));
  }
  _assert(den->d_lock>0);
 
  den->d_lock--;
  //if (den->d_lock==0) {
  //  _assert(den->d_inode!=NULL);
  //  unlock_inode(den->d_inode);
  //}
 
  printdd("²u<%i>",exec_shadow);
  __fs_mutex_unlock(&mutex);
  printdd("Ûu<%i>",exec_shadow);
}

/*
 *
 */


static void __print_name(struct dentry *act,char *buffer)
{
  if (act->d_parent==NULL) return;
  __print_name(act->d_parent,buffer);
  strcat(buffer,ROOTDIRNAME);
  strcat(buffer,QSTRNAME(&act->d_name));
  return;
}

static char *print_name(struct dentry *act)
{
  static char buffer[1024];
  buffer[0]='\0';
  __print_name(act,buffer);
  return buffer;
}

void getfullname_dentry(struct dentry *act, char *buffer, int size)
{
  _assert(act>=direntry&&act<direntry+MAXDENTRY);
  magic_assert(act->magic,DENTRY_MAGIC,
               "getfullname_dentry: dentry MAGIC failed!");

  buffer[0]='\0';
  __print_name(act,buffer);
  _assert(strlen(buffer)<size);
}

static void visit_tree(struct dentry *act)
{
  struct dentry *ptr;
  if (act==root_direntry) printk(KERN_DEBUG "  %s",ROOTDIRNAME);
  else { char *s=print_name(act); printk(KERN_DEBUG "  %s",s);}
  ptr=act->d_child;
  while (ptr!=NULL) {
    visit_tree(ptr);
    ptr=ptr->d_next;
  }
  return;
}

void dump_dentry_tree(void)
{
  printk(KERN_DEBUG "DTREE dump");
  visit_tree(root_direntry);
}

/*
 *
 */


#ifdef DEBUGPURGECHILD
#define dprint(fmt,args...) printk(fmt,##args)
#else
#define dprint(fmt,args...)
#endif

static __inline__ int __purgeallchilds_mg(struct dentry *de)
{
  int ret;
  struct dentry *ptr,*ptr2,*ptr3;

  dprint(KERN_DEBUG "START mg purgechild");

  {
#ifdef DEBUGPURGECHILD
    char *s=print_name(de);
    dprint(KERN_DEBUG "for %s",s);
#endif
  }
 
  /*
   * Phase 1
   * purge all childs (if possible!)
   */


  ptr=de->d_child;
  ret=0;
 
  /* why these? I do not want to use recursion... (or goto statement)*/
  for (;;) {
    /* if there is a child... continue using the child's chain */
    if (ptr->d_child!=NULL) {
      ptr=ptr->d_child;
      continue;      
    }    
    /* if a child dentry is locked... can't continue! */
    if (ptr->d_lock) {
      #ifdef DEBUGPURGECHILD
      char *s=print_name(ptr);
      dprint(KERN_DEBUG "%s is locked!!!",s);
      #endif
      ret=-1;
      break;
    }
    /* purge this dentry */
    ptr2=ptr->d_next;
    ptr3=ptr->d_parent;

    dprint(KERN_DEBUG "%s destroy",QSTRNAME(&ptr->d_name));
    ret=destroy_inode(ptr->d_inode);
    if (ret) break;
    __remove_dentry_only(ptr);
   
    /* if there is not another child... continue with the parent */
    if (ptr2==NULL) {
      ptr=ptr3;
      if (ptr==de) break;
      continue;
    }
    /* continue with next child */
    ptr=ptr2;
  }

  dprint(KERN_DEBUG "END mg purgechild");  
  return ret;
}

static __inline__ int __purgeallchilds_pj(struct dentry *x)
{
  struct dentry *p,*q;

  dprint(KERN_DEBUG "START pj purgechild");
 
  p = x->d_child;
  q = NULL;
  for(;;) {
    /* scorro i figli */
    while(p) {
      q = p;
      p = p->d_next;
    }

    if (q == x) {
      dprint(KERN_DEBUG "END pj purgechild");  
      return 0; /* finito */
    }

    if (q->d_child) {
      p = q->d_child;
      q = NULL;
    }
    else {
      /* sono su un nodo senza figli */
      /* q punta al nodo */
      if (q->d_prev)
        { /* non e' il primo figlio */
          q = q->d_prev;
          if (q->d_next->d_lock) {
            dprint(KERN_DEBUG "%s is locked",
                   QSTRNAME(&q->d_next->d_name));
            return -1;
          }
          if (destroy_inode(q->d_next->d_inode)) {
            dprint(KERN_DEBUG "error destroing %s",
                   QSTRNAME(&q->d_next->d_name));
            return -1;
          }
          dprint(KERN_DEBUG "removing %s",QSTRNAME(&q->d_next->d_name));
          __remove_dentry_only(q->d_next);
          q->d_next = NULL;
          p = NULL;
        }
      else {
        /* e' il primo figlio */
        q = q->d_parent;
        if (q->d_child->d_lock) {
            dprint(KERN_DEBUG "%s is locked",
                   QSTRNAME(&q->d_child->d_name));
          return -1;
        }
        if (destroy_inode(q->d_child->d_inode)) {
            dprint(KERN_DEBUG "error destroing %s",
                   QSTRNAME(&q->d_child->d_name));
          return -1;
        }
        dprint(KERN_DEBUG "removing %s",QSTRNAME(&q->d_child->d_name));
        __remove_dentry_only(q->d_child);
        q->d_child = NULL;
        p = NULL;
      }
    }
  }
 
}

#define __purgeallchilds(de) __purgeallchilds_mg(de)

int mount_dentry(struct super_block *sb, struct dentry *de)
{
  int ret;

  printk9("START mount_dentry()!");
  printk9("for %s",QSTRNAME(&de->d_name));
 
  _assert(de!=NULL);
  _assert(sb!=NULL);
 
  __fs_mutex_lock(&mutex);

  printk9("locked and ready");

  if (de->d_lock>1) {
    printk9("LOCKED!");    
    /* someone is using this dentry! */
    __fs_mutex_unlock(&mutex);    
    return -1;    
  }

  /*
   * Phase 1
   */


  /* purge all childs (if possible) */
 
  if (de->d_child!=NULL) {
    ret=__purgeallchilds(de);      
    if (ret) {
      printk9("PURGECHILD!");      
      __fs_mutex_unlock(&mutex);
      return ret;
    }
   
  }
 
  /*
   * Phase 2
   */


  ret=destroy_inode(de->d_inode);
  if (ret) {
    printk9("DESTROY INODE!");    
    __fs_mutex_unlock(&mutex);
    return ret;
  }
 
  de->d_op=sb->sb_dop;
  de->d_inode=sb->sb_root;
  de->d_sb=sb;

  sb->sb_droot=de;
 
  __fs_mutex_unlock(&mutex);
  printk9("END mount_dentry()!");
  return 0;
}

int umount_dentry(struct super_block *sb)
{
  struct dentry *de;
  int res;

  printk9("START umount_dentry()");

  __fs_mutex_lock(&mutex);

  _assert(sb!=NULL);
  _assert(sb->sb_droot!=NULL);
  if (sb->sb_droot->d_child!=NULL) {
    res=__purgeallchilds(sb->sb_droot);
    if (res) {      
      printk9("PURGING CHILD!");    
      __fs_mutex_unlock(&mutex);
      return res;
    }
  }

  printk9("umount_dentry: phase 1");
 
  de=sb->sb_droot;
  _assert(de->d_lock>0);
  if (de->d_lock!=1) {
    printk9("LOCK COUNT! (%i)",de->d_lock);
    __fs_mutex_unlock(&mutex);
    return -1;
  }
  de->d_lock--;

  printk9("umount_dentry: phase 2");
 
  __remove_dentry_only(de);
  destroy_inode(de->d_inode);
  __free_dentry(de);
 
  printk9("umount_dentry: phase 3");
   
  sb->sb_droot=NULL;
  sb->sb_root=NULL;
 
  __fs_mutex_unlock(&mutex);
 
  printk9("END umount_dentry()!");
  return 0;
}

int unlink_dentry(struct dentry *d)
{
  struct inode *in;
  int res;

  printkd("unlink_dentry: START");
 
  __fs_mutex_lock(&mutex);
  if (d->d_lock!=1) return EBUSY;

  in=d->d_inode;

  printkd("unlink_dentry: dentry ok");

  __rwlock_wrlock(&in->i_lock);  

  printkd("unlink_dentry: locked inode");
 
  res=in->i_op->unlink(d);

  printkd("unlink_dentry: inode unlinked from dentry");
 
  if (res==0&&in->i_st.st_nlink==0) {
    printkd("unlink_dentry: no more links erasing inode");
    erase_inode(in);    
  }
 
  __rwlock_wrunlock(&in->i_lock);

  printkd("unlink_dentry: unlocked inode");

  if (res==0) {
    d->d_lock--;
    __remove_dentry_only(d);
    printkd("unlink_dentry: dentry unlinked");
  }
   
  __fs_mutex_unlock(&mutex);
 
  printkd("unlink_dentry: END");
  return -res;
}