diff options
author | Jeremy Allison <jra@samba.org> | 1998-10-23 00:58:28 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 1998-10-23 00:58:28 +0000 |
commit | 5d6ed11ef3c860c95ae7b3a855b0ddb123bd9737 (patch) | |
tree | 13a6cba2591bcb12dfc838055d5cb80971574778 /source3 | |
parent | 95056761c8137aa0d0d4f04e862efa7fa94e064c (diff) | |
download | samba-5d6ed11ef3c860c95ae7b3a855b0ddb123bd9737.tar.gz samba-5d6ed11ef3c860c95ae7b3a855b0ddb123bd9737.tar.bz2 samba-5d6ed11ef3c860c95ae7b3a855b0ddb123bd9737.zip |
include/smb.h: Added #defines for lots of things - makes our code a *lot* easier to read.
lib/util.c: Fixed Luke's set_first_token() function - should return void.
smbd/close.c: Move delete_on_close into file_fd_struct structure.
smbd/ipc.c: Changed local_machine back to fstring.
smbd/nttrans.c: Use defines for mapping share modes.
smbd/open.c: Move delete_on_close into file_fd_struct structure, added code for ALLOW_SHARE_DELETE.
smbd/reply.c: Use defines for mapping share modes.
smbd/trans2.c: Move delete_on_close into file_fd_struct structure.
Jeremy.
(This used to be commit 8e1ce307bd6a9056b4a95fe6f52ff42dc6e03a08)
Diffstat (limited to 'source3')
-rw-r--r-- | source3/include/proto.h | 2 | ||||
-rw-r--r-- | source3/include/smb.h | 47 | ||||
-rw-r--r-- | source3/lib/util.c | 2 | ||||
-rw-r--r-- | source3/smbd/close.c | 3 | ||||
-rw-r--r-- | source3/smbd/ipc.c | 2 | ||||
-rw-r--r-- | source3/smbd/nttrans.c | 42 | ||||
-rw-r--r-- | source3/smbd/open.c | 71 | ||||
-rw-r--r-- | source3/smbd/reply.c | 38 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 11 |
9 files changed, 146 insertions, 72 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h index 6ec6f7e617..9b707adeef 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -231,7 +231,7 @@ BOOL user_in_list(char *user,char *list); char *tmpdir(void); BOOL is_a_socket(int fd); -BOOL set_first_token(char *ptr); +void set_first_token(char *ptr); BOOL next_token(char **ptr,char *buff,char *sep, int bufsize); char **toktocliplist(int *ctok, char *sep); void set_socket_options(int fd, char *options); diff --git a/source3/include/smb.h b/source3/include/smb.h index f755b81b92..d71d1cd1eb 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -186,6 +186,47 @@ implemented */ #define DENY_NONE 4 #define DENY_FCB 7 +/* open modes */ +#define DOS_OPEN_RDONLY 0 +#define DOS_OPEN_WRONLY 1 +#define DOS_OPEN_RDWR 2 +#define DOS_OPEN_FCB 0xF + +/* define shifts and masks for share and open modes. */ +#define OPEN_MODE_MASK 0xF +#define SHARE_MODE_SHIFT 4 +#define SHARE_MODE_MASK 0x7 +#define GET_OPEN_MODE(x) ((x) & OPEN_MODE_MASK) +#define SET_OPEN_MODE(x) ((x) & OPEN_MODE_MASK) +#define GET_DENY_MODE(x) (((x)>>SHARE_MODE_SHIFT) & SHARE_MODE_MASK) +#define SET_DENY_MODE(x) ((x)<<SHARE_MODE_SHIFT) + +/* allow delete on open file mode (used by NT SMB's). */ +#define ALLOW_SHARE_DELETE (1<<15) +#define GET_ALLOW_SHARE_DELETE(x) (((x) & ALLOW_SHARE_DELETE) ? 1 : 0) +#define SET_ALLOW_SHARE_DELETE(x) ((x) ? ALLOW_SHARE_DELETE : 0) + +/* Sync on open file (not sure if used anymore... ?) */ +#define FILE_SYNC_OPENMODE (1<<14) +#define GET_FILE_SYNC_OPENMODE(x) (((x) & FILE_SYNC_OPENMODE) ? 1 : 0) + +/* open disposition values */ +#define FILE_EXISTS_FAIL 0 +#define FILE_EXISTS_OPEN 1 +#define FILE_EXISTS_TRUNCATE 2 + +/* mask for open disposition. */ +#define FILE_OPEN_MASK 0x3 + +#define GET_FILE_OPEN_DISPOSITION(x) ((x) & FILE_OPEN_MASK) +#define SET_FILE_OPEN_DISPOSITION(x) ((x) & FILE_OPEN_MASK) + +/* The above can be OR'ed with... */ +#define FILE_CREATE_IF_NOT_EXIST 0x10 +#define FILE_FAIL_IF_NOT_EXIST 0 + +#define GET_FILE_CREATE_DISPOSITION(x) ((x) & (FILE_CREATE_IF_NOT_EXIST|FILE_FAIL_IF_NOT_EXIST)) + /* share types */ #define STYPE_DISKTREE 0 /* Disk drive */ #define STYPE_PRINTQ 1 /* Spooler queue */ @@ -433,6 +474,7 @@ typedef struct file_fd_struct int fd_readonly; int fd_writeonly; int real_open_flags; + BOOL delete_on_close; } file_fd_struct; /* @@ -517,17 +559,16 @@ typedef struct files_struct SMB_OFF_T mmap_size; write_bmpx_struct *wbmpx_ptr; struct timeval open_time; + int share_mode; BOOL open; BOOL can_lock; BOOL can_read; BOOL can_write; - BOOL share_mode; BOOL print_file; BOOL modified; BOOL granted_oplock; BOOL sent_oplock_break; BOOL is_directory; - BOOL delete_on_close; char *fsp_name; } files_struct; @@ -788,7 +829,7 @@ struct bitmap { #define FLAG_HIDE 2 /* options that should be hidden in SWAT */ #define FLAG_PRINT 4 /* printing options */ #define FLAG_GLOBAL 8 /* local options that should be globally settable in SWAT */ -#define FLAG_DEPRECATED 16 /* options that should no longer be used */ +#define FLAG_DEPRECATED 0x10 /* options that should no longer be used */ #ifndef LOCKING_VERSION #define LOCKING_VERSION 4 diff --git a/source3/lib/util.c b/source3/lib/util.c index d4f939e081..ced6786194 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -125,7 +125,7 @@ BOOL is_a_socket(int fd) static char *last_ptr=NULL; -BOOL set_first_token(char *ptr) +void set_first_token(char *ptr) { last_ptr = ptr; } diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 981c0d77bb..2dba691a1c 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -103,6 +103,7 @@ void close_file(files_struct *fsp, BOOL normal_close) SMB_INO_T inode = fsp->fd_ptr->inode; int token; BOOL last_reference = False; + BOOL delete_on_close = fsp->fd_ptr->delete_on_close; connection_struct *conn = fsp->conn; remove_pending_lock_requests_by_fid(fsp); @@ -140,7 +141,7 @@ void close_file(files_struct *fsp, BOOL normal_close) * reference to a file. */ - if (normal_close && last_reference && fsp->delete_on_close) { + if (normal_close && last_reference && delete_on_close) { if(dos_unlink(fsp->fsp_name) != 0) DEBUG(0,("close_file: file %s. Delete on close was set and unlink failed \ with error %s\n", fsp->fsp_name, strerror(errno) )); diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index d9ef53e70f..112a79b5ca 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -37,7 +37,7 @@ extern int DEBUGLEVEL; extern int max_send; -extern pstring local_machine; +extern fstring local_machine; extern fstring global_myworkgroup; #define NERR_Success 0 diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 62ca9fe1c8..8b4049cd96 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -317,24 +317,24 @@ static int map_create_disposition( uint32 create_disposition) switch( create_disposition ) { case FILE_CREATE: /* create if not exist, fail if exist */ - ret = 0x10; + ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL); break; case FILE_SUPERSEDE: case FILE_OVERWRITE_IF: /* create if not exist, trunc if exist */ - ret = 0x12; + ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE); break; case FILE_OPEN: /* fail if not exist, open if exists */ - ret = 0x1; + ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN); break; case FILE_OPEN_IF: /* create if not exist, open if exists */ - ret = 0x11; + ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_OPEN); break; case FILE_OVERWRITE: /* fail if not exist, truncate if exists */ - ret = 0x2; + ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE); break; default: DEBUG(0,("map_create_disposition: Incorrect value for create_disposition = %d\n", @@ -358,13 +358,13 @@ static int map_share_mode( char *fname, uint32 desired_access, uint32 share_acce switch( desired_access & (FILE_READ_DATA|FILE_WRITE_DATA) ) { case FILE_READ_DATA: - smb_open_mode = 0; + smb_open_mode = DOS_OPEN_RDONLY; break; case FILE_WRITE_DATA: - smb_open_mode = 1; + smb_open_mode = DOS_OPEN_WRONLY; break; case FILE_READ_DATA|FILE_WRITE_DATA: - smb_open_mode = 2; + smb_open_mode = DOS_OPEN_RDWR; break; } @@ -386,7 +386,7 @@ static int map_share_mode( char *fname, uint32 desired_access, uint32 share_acce if(desired_access & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS| FILE_EXECUTE|FILE_READ_ATTRIBUTES| FILE_WRITE_ATTRIBUTES|READ_CONTROL_ACCESS)) - smb_open_mode = 0; + smb_open_mode = DOS_OPEN_RDONLY; else { DEBUG(0,("map_share_mode: Incorrect value %lx for desired_access to file %s\n", (unsigned long)desired_access, fname)); @@ -394,27 +394,37 @@ static int map_share_mode( char *fname, uint32 desired_access, uint32 share_acce } } - /* Add in the requested share mode - ignore FILE_SHARE_DELETE for now. */ + /* + * Set the special bit that means allow share delete. + * This is held outside the normal share mode bits at 1<<15. + * JRA. + */ + + if(share_access & FILE_SHARE_DELETE) + smb_open_mode |= ALLOW_SHARE_DELETE; + + /* Add in the requested share mode. */ switch( share_access & (FILE_SHARE_READ|FILE_SHARE_WRITE)) { case FILE_SHARE_READ: - smb_open_mode |= (DENY_WRITE<<4); + smb_open_mode |= SET_DENY_MODE(DENY_WRITE); break; case FILE_SHARE_WRITE: - smb_open_mode |= (DENY_READ<<4); + smb_open_mode |= SET_DENY_MODE(DENY_READ); break; case (FILE_SHARE_READ|FILE_SHARE_WRITE): - smb_open_mode |= (DENY_NONE<<4); + smb_open_mode |= SET_DENY_MODE(DENY_NONE); break; case FILE_SHARE_NONE: - smb_open_mode |= (DENY_ALL<<4); + smb_open_mode |= SET_DENY_MODE(DENY_ALL); break; } /* - * Handle a O_SYNC request. + * Handle an O_SYNC request. */ + if(file_attributes & FILE_FLAG_WRITE_THROUGH) - smb_open_mode |= (1<<14); + smb_open_mode |= FILE_SYNC_OPENMODE; DEBUG(10,("map_share_mode: Mapped desired access %lx, share access %lx, file attributes %lx \ to open_mode %x\n", (unsigned long)desired_access, (unsigned long)share_access, diff --git a/source3/smbd/open.c b/source3/smbd/open.c index a6e2953263..b6b2ef5bb8 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -505,7 +505,6 @@ static void open_file(files_struct *fsp,connection_struct *conn, fsp->granted_oplock = False; fsp->sent_oplock_break = False; fsp->is_directory = False; - fsp->delete_on_close = False; fsp->conn = conn; /* * Note that the file name here is the *untranslated* name @@ -657,8 +656,8 @@ static int check_share_mode( share_mode_entry *share, int deny_mode, char *fname, BOOL fcbopen, int *flags) { - int old_open_mode = share->share_mode &0xF; - int old_deny_mode = (share->share_mode >>4)&7; + int old_open_mode = GET_OPEN_MODE(share->share_mode); + int old_deny_mode = GET_DENY_MODE(share->share_mode); if (old_deny_mode > 4 || old_open_mode > 2) { @@ -702,7 +701,8 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int { int flags=0; int flags2=0; - int deny_mode = (share_mode>>4)&7; + int deny_mode = GET_DENY_MODE(share_mode); + BOOL allow_share_delete = GET_ALLOW_SHARE_DELETE(share_mode); SMB_STRUCT_STAT sbuf; BOOL file_existed = file_exist(fname,&sbuf); BOOL share_locked = False; @@ -733,7 +733,7 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int return; } - if ((ofun & 0x3) == 0 && file_existed) + if ((GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL) && file_existed) { DEBUG(5,("open_file_shared: create new requested for file %s and file already exists.\n", fname )); @@ -741,24 +741,25 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int return; } - if (ofun & 0x10) + if (GET_FILE_CREATE_DISPOSITION(ofun) == FILE_CREATE_IF_NOT_EXIST) flags2 |= O_CREAT; - if ((ofun & 0x3) == 2) + + if (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_TRUNCATE) flags2 |= O_TRUNC; /* note that we ignore the append flag as append does not mean the same thing under dos and unix */ - switch (share_mode&0xF) + switch (GET_OPEN_MODE(share_mode)) { - case 1: + case DOS_OPEN_WRONLY: flags = O_WRONLY; break; - case 0xF: + case DOS_OPEN_FCB: fcbopen = True; flags = O_RDWR; break; - case 2: + case DOS_OPEN_RDWR: flags = O_RDWR; break; default: @@ -767,7 +768,7 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int } #if defined(O_SYNC) - if (share_mode&(1<<14)) { + if (GET_FILE_SYNC_OPENMODE(share_mode)) { flags2 |= O_SYNC; } #endif /* O_SYNC */ @@ -792,7 +793,8 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int return; } - if (deny_mode == DENY_FCB) deny_mode = DENY_DOS; + if (deny_mode == DENY_FCB) + deny_mode = DENY_DOS; if (lp_share_modes(SNUM(conn))) { @@ -908,17 +910,19 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou switch (flags) { case O_RDONLY: - open_mode = 0; + open_mode = DOS_OPEN_RDONLY; break; case O_RDWR: - open_mode = 2; + open_mode = DOS_OPEN_RDWR; break; case O_WRONLY: - open_mode = 1; + open_mode = DOS_OPEN_WRONLY; break; } - fsp->share_mode = (deny_mode<<4) | open_mode; + fsp->share_mode = SET_DENY_MODE(deny_mode) | + SET_OPEN_MODE(open_mode) | + SET_ALLOW_SHARE_DELETE(allow_share_delete); if (Access) (*Access) = open_mode; @@ -971,11 +975,10 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou unlock_share_entry( conn, dev, inode, token); } - - /**************************************************************************** Open a directory from an NT SMB call. ****************************************************************************/ + int open_directory(files_struct *fsp,connection_struct *conn, char *fname, int smb_ofun, mode_t unixmode, int *action) { @@ -1057,6 +1060,7 @@ int open_directory(files_struct *fsp,connection_struct *conn, check if the share mode on a file allows it to be deleted or unlinked return True if sharing doesn't prevent the operation ********************************************************************/ + BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op) { int i; @@ -1156,9 +1160,20 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou } } - /* someone else has a share lock on it, check to see - if we can too */ - if ((share_entry->share_mode != DENY_DOS) || (share_entry->pid != pid)) + /* + * If this is a delete request and ALLOW_SHARE_DELETE is set then allow + * this to proceed. This takes precedence over share modes. + */ + + if(!rename_op && GET_ALLOW_SHARE_DELETE(share_entry->share_mode)) + continue; + + /* + * Someone else has a share lock on it, check to see + * if we can too. + */ + + if ((GET_DENY_MODE(share_entry->share_mode) != DENY_DOS) || (share_entry->pid != pid)) goto free_and_exit; } /* end for */ @@ -1173,8 +1188,14 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou /* XXXX exactly what share mode combinations should be allowed for deleting/renaming? */ - /* If we got here then either there were no share modes or - all share modes were DENY_DOS and the pid == getpid() */ + /* + * If we got here then either there were no share modes or + * all share modes were DENY_DOS and the pid == getpid() or + * delete access was requested and all share modes had the + * ALLOW_SHARE_DELETE bit set (takes precedence over other + * share modes). + */ + ret = True; free_and_exit: @@ -1184,5 +1205,3 @@ free_and_exit: free((char *)old_shares); return(ret); } - - diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 8b96ff17fd..bc19f1a931 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1239,6 +1239,7 @@ int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size /**************************************************************************** reply to an open ****************************************************************************/ + int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; @@ -1276,8 +1277,8 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, unixmode = unix_mode(conn,aARCH); - open_file_shared(fsp,conn,fname,share_mode,3,unixmode, - oplock_request,&rmode,NULL); + open_file_shared(fsp,conn,fname,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + unixmode, oplock_request,&rmode,NULL); if (!fsp->open) { @@ -1529,8 +1530,8 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, } /* Open file in dos compatibility share mode. */ - open_file_shared(fsp,conn,fname,(DENY_FCB<<4)|0xF, ofun, unixmode, - oplock_request, NULL, NULL); + open_file_shared(fsp,conn,fname,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + ofun, unixmode, oplock_request, NULL, NULL); if (!fsp->open) { @@ -1601,8 +1602,8 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, /* Open file in dos compatibility share mode. */ /* We should fail if file exists. */ - open_file_shared(fsp,conn,fname2,(DENY_FCB<<4)|0xF, 0x10, unixmode, - oplock_request, NULL, NULL); + open_file_shared(fsp,conn,fname2,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL), unixmode, oplock_request, NULL, NULL); if (!fsp->open) { @@ -1702,8 +1703,10 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size if (!has_wild) { pstrcat(directory,"/"); pstrcat(directory,mask); - if (can_delete(directory,conn,dirtype) && !dos_unlink(directory)) count++; - if (!count) exists = file_exist(directory,NULL); + if (can_delete(directory,conn,dirtype) && !dos_unlink(directory)) + count++; + if (!count) + exists = file_exist(directory,NULL); } else { void *dirptr = NULL; char *dname; @@ -2675,9 +2678,8 @@ int reply_printopen(connection_struct *conn, } /* Open for exclusive use, write only. */ - open_file_shared(fsp,conn,fname2, - (DENY_ALL<<4)|1, 0x12, unix_mode(conn,0), - 0, NULL, NULL); + open_file_shared(fsp,conn,fname2, SET_DENY_MODE(DENY_ALL)|SET_OPEN_MODE(DOS_OPEN_WRONLY), + (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE), unix_mode(conn,0), 0, NULL, NULL); if (!fsp->open) { file_free(fsp); @@ -3323,6 +3325,7 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, in /******************************************************************* copy a file as part of a reply_copy ******************************************************************/ + static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, int count,BOOL target_is_directory) { @@ -3343,12 +3346,15 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, pstrcat(dest,p); } - if (!file_exist(src,&st)) return(False); + if (!file_exist(src,&st)) + return(False); fsp1 = file_new(); - if (!fsp1) return(False); - open_file_shared(fsp1,conn,src,(DENY_NONE<<4), - 1,0,0,&Access,&action); + if (!fsp1) + return(False); + + open_file_shared(fsp1,conn,src,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),0,0,&Access,&action); if (!fsp1->open) { file_free(fsp1); @@ -3363,7 +3369,7 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, close_file(fsp1,False); return(False); } - open_file_shared(fsp2,conn,dest,(DENY_NONE<<4)|1, + open_file_shared(fsp2,conn,dest,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY), ofun,st.st_mode,0,&Access,&action); if (!fsp2->open) { diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 5b057410ca..f8d90cd871 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -1658,14 +1658,11 @@ static int call_trans2setfilepathinfo(connection_struct *conn, if(fsp->is_directory) return(ERROR(ERRDOS,ERRnoaccess)); /* - * TODO - check here is this means set - * this flag bit on all open files that - * reference this particular dev/inode pair. - * If so we'll need to search the open - * file entries here and set this flag on - * all of them that match. JRA. + * Set the delete on close flag in the reference + * counted struct. Delete when the last reference + * goes away. */ - fsp->delete_on_close = CVAL(pdata,0); + fsp->fd_ptr->delete_on_close = CVAL(pdata,0); } else return(ERROR(ERRDOS,ERRunknownlevel)); break; |