diff options
author | Steve French <sfrench@us.ibm.com> | 2008-11-13 14:45:32 -0500 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2008-11-13 14:45:32 -0500 |
commit | 3b7952109361c684caf0c50474da8662ecc81019 (patch) | |
tree | 402062ed63236ef245d8d65010d8f06520df8453 /fs/cifs/connect.c | |
parent | c527c8a7ffa18400c2c1488f7ab5aff5e83f3c8e (diff) |
[CIFS] Fix cifs reconnection flags
In preparation for Jeff's big umount/mount fixes to remove the possibility of
various races in cifs mount and linked list handling of sessions, sockets and
tree connections, this patch cleans up some repetitive code in cifs_mount,
and addresses a problem with ses->status and tcon->tidStatus in which we
were overloading the "need_reconnect" state with other status in that
field. So the "need_reconnect" flag has been broken out from those
two state fields (need reconnect was not mutually exclusive from some of the
other possible tid and ses states). In addition, a few exit cases in
cifs_mount were cleaned up, and a problem with a tcon flag (for lease support)
was not being set consistently for the 2nd mount of the same share
CC: Jeff Layton <jlayton@redhat.com>
CC: Shirish Pargaonkar <shirishp@us.ibm.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r-- | fs/cifs/connect.c | 262 |
1 files changed, 131 insertions, 131 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c682be8f2984..c1cd1217c990 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -149,7 +149,7 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
149 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); | 149 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); |
150 | if (ses->server) { | 150 | if (ses->server) { |
151 | if (ses->server == server) { | 151 | if (ses->server == server) { |
152 | ses->status = CifsNeedReconnect; | 152 | ses->need_reconnect = true; |
153 | ses->ipc_tid = 0; | 153 | ses->ipc_tid = 0; |
154 | } | 154 | } |
155 | } | 155 | } |
@@ -158,7 +158,7 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
158 | list_for_each(tmp, &GlobalTreeConnectionList) { | 158 | list_for_each(tmp, &GlobalTreeConnectionList) { |
159 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | 159 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); |
160 | if ((tcon->ses) && (tcon->ses->server == server)) | 160 | if ((tcon->ses) && (tcon->ses->server == server)) |
161 | tcon->tidStatus = CifsNeedReconnect; | 161 | tcon->need_reconnect = true; |
162 | } | 162 | } |
163 | read_unlock(&GlobalSMBSeslock); | 163 | read_unlock(&GlobalSMBSeslock); |
164 | /* do not want to be sending data on a socket we are freeing */ | 164 | /* do not want to be sending data on a socket we are freeing */ |
@@ -1891,6 +1891,92 @@ kill_cifsd(struct TCP_Server_Info *server) | |||
1891 | force_sig(SIGKILL, task); | 1891 | force_sig(SIGKILL, task); |
1892 | } | 1892 | } |
1893 | 1893 | ||
1894 | static void setup_cifs_sb(struct smb_vol *pvolume_info, | ||
1895 | struct cifs_sb_info *cifs_sb) | ||
1896 | { | ||
1897 | if (pvolume_info->rsize > CIFSMaxBufSize) { | ||
1898 | cERROR(1, ("rsize %d too large, using MaxBufSize", | ||
1899 | pvolume_info->rsize)); | ||
1900 | cifs_sb->rsize = CIFSMaxBufSize; | ||
1901 | } else if ((pvolume_info->rsize) && | ||
1902 | (pvolume_info->rsize <= CIFSMaxBufSize)) | ||
1903 | cifs_sb->rsize = pvolume_info->rsize; | ||
1904 | else /* default */ | ||
1905 | cifs_sb->rsize = CIFSMaxBufSize; | ||
1906 | |||
1907 | if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { | ||
1908 | cERROR(1, ("wsize %d too large, using 4096 instead", | ||
1909 | pvolume_info->wsize)); | ||
1910 | cifs_sb->wsize = 4096; | ||
1911 | } else if (pvolume_info->wsize) | ||
1912 | cifs_sb->wsize = pvolume_info->wsize; | ||
1913 | else | ||
1914 | cifs_sb->wsize = min_t(const int, | ||
1915 | PAGEVEC_SIZE * PAGE_CACHE_SIZE, | ||
1916 | 127*1024); | ||
1917 | /* old default of CIFSMaxBufSize was too small now | ||
1918 | that SMB Write2 can send multiple pages in kvec. | ||
1919 | RFC1001 does not describe what happens when frame | ||
1920 | bigger than 128K is sent so use that as max in | ||
1921 | conjunction with 52K kvec constraint on arch with 4K | ||
1922 | page size */ | ||
1923 | |||
1924 | if (cifs_sb->rsize < 2048) { | ||
1925 | cifs_sb->rsize = 2048; | ||
1926 | /* Windows ME may prefer this */ | ||
1927 | cFYI(1, ("readsize set to minimum: 2048")); | ||
1928 | } | ||
1929 | /* calculate prepath */ | ||
1930 | cifs_sb->prepath = pvolume_info->prepath; | ||
1931 | if (cifs_sb->prepath) { | ||
1932 | cifs_sb->prepathlen = strlen(cifs_sb->prepath); | ||
1933 | /* we can not convert the / to \ in the path | ||
1934 | separators in the prefixpath yet because we do not | ||
1935 | know (until reset_cifs_unix_caps is called later) | ||
1936 | whether POSIX PATH CAP is available. We normalize | ||
1937 | the / to \ after reset_cifs_unix_caps is called */ | ||
1938 | pvolume_info->prepath = NULL; | ||
1939 | } else | ||
1940 | cifs_sb->prepathlen = 0; | ||
1941 | cifs_sb->mnt_uid = pvolume_info->linux_uid; | ||
1942 | cifs_sb->mnt_gid = pvolume_info->linux_gid; | ||
1943 | cifs_sb->mnt_file_mode = pvolume_info->file_mode; | ||
1944 | cifs_sb->mnt_dir_mode = pvolume_info->dir_mode; | ||
1945 | cFYI(1, ("file mode: 0x%x dir mode: 0x%x", | ||
1946 | cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); | ||
1947 | |||
1948 | if (pvolume_info->noperm) | ||
1949 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; | ||
1950 | if (pvolume_info->setuids) | ||
1951 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; | ||
1952 | if (pvolume_info->server_ino) | ||
1953 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; | ||
1954 | if (pvolume_info->remap) | ||
1955 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; | ||
1956 | if (pvolume_info->no_xattr) | ||
1957 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; | ||
1958 | if (pvolume_info->sfu_emul) | ||
1959 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; | ||
1960 | if (pvolume_info->nobrl) | ||
1961 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; | ||
1962 | if (pvolume_info->cifs_acl) | ||
1963 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; | ||
1964 | if (pvolume_info->override_uid) | ||
1965 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; | ||
1966 | if (pvolume_info->override_gid) | ||
1967 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; | ||
1968 | if (pvolume_info->dynperm) | ||
1969 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; | ||
1970 | if (pvolume_info->direct_io) { | ||
1971 | cFYI(1, ("mounting share using direct i/o")); | ||
1972 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; | ||
1973 | } | ||
1974 | |||
1975 | if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) | ||
1976 | cERROR(1, ("mount option dynperm ignored if cifsacl " | ||
1977 | "mount option supported")); | ||
1978 | } | ||
1979 | |||
1894 | int | 1980 | int |
1895 | cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | 1981 | cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, |
1896 | char *mount_data, const char *devname) | 1982 | char *mount_data, const char *devname) |
@@ -1996,9 +2082,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
1996 | goto out; | 2082 | goto out; |
1997 | } | 2083 | } |
1998 | 2084 | ||
1999 | if (srvTcp) { | 2085 | if (!srvTcp) { /* create socket */ |
2000 | cFYI(1, ("Existing tcp session with server found")); | ||
2001 | } else { /* create socket */ | ||
2002 | if (volume_info.port) | 2086 | if (volume_info.port) |
2003 | sin_server.sin_port = htons(volume_info.port); | 2087 | sin_server.sin_port = htons(volume_info.port); |
2004 | else | 2088 | else |
@@ -2074,7 +2158,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2074 | cFYI(1, ("Existing smb sess found (status=%d)", | 2158 | cFYI(1, ("Existing smb sess found (status=%d)", |
2075 | pSesInfo->status)); | 2159 | pSesInfo->status)); |
2076 | down(&pSesInfo->sesSem); | 2160 | down(&pSesInfo->sesSem); |
2077 | if (pSesInfo->status == CifsNeedReconnect) { | 2161 | if (pSesInfo->need_reconnect) { |
2078 | cFYI(1, ("Session needs reconnect")); | 2162 | cFYI(1, ("Session needs reconnect")); |
2079 | rc = cifs_setup_session(xid, pSesInfo, | 2163 | rc = cifs_setup_session(xid, pSesInfo, |
2080 | cifs_sb->local_nls); | 2164 | cifs_sb->local_nls); |
@@ -2124,146 +2208,59 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2124 | 2208 | ||
2125 | /* search for existing tcon to this server share */ | 2209 | /* search for existing tcon to this server share */ |
2126 | if (!rc) { | 2210 | if (!rc) { |
2127 | if (volume_info.rsize > CIFSMaxBufSize) { | 2211 | setup_cifs_sb(&volume_info, cifs_sb); |
2128 | cERROR(1, ("rsize %d too large, using MaxBufSize", | ||
2129 | volume_info.rsize)); | ||
2130 | cifs_sb->rsize = CIFSMaxBufSize; | ||
2131 | } else if ((volume_info.rsize) && | ||
2132 | (volume_info.rsize <= CIFSMaxBufSize)) | ||
2133 | cifs_sb->rsize = volume_info.rsize; | ||
2134 | else /* default */ | ||
2135 | cifs_sb->rsize = CIFSMaxBufSize; | ||
2136 | |||
2137 | if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { | ||
2138 | cERROR(1, ("wsize %d too large, using 4096 instead", | ||
2139 | volume_info.wsize)); | ||
2140 | cifs_sb->wsize = 4096; | ||
2141 | } else if (volume_info.wsize) | ||
2142 | cifs_sb->wsize = volume_info.wsize; | ||
2143 | else | ||
2144 | cifs_sb->wsize = | ||
2145 | min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE, | ||
2146 | 127*1024); | ||
2147 | /* old default of CIFSMaxBufSize was too small now | ||
2148 | that SMB Write2 can send multiple pages in kvec. | ||
2149 | RFC1001 does not describe what happens when frame | ||
2150 | bigger than 128K is sent so use that as max in | ||
2151 | conjunction with 52K kvec constraint on arch with 4K | ||
2152 | page size */ | ||
2153 | |||
2154 | if (cifs_sb->rsize < 2048) { | ||
2155 | cifs_sb->rsize = 2048; | ||
2156 | /* Windows ME may prefer this */ | ||
2157 | cFYI(1, ("readsize set to minimum: 2048")); | ||
2158 | } | ||
2159 | /* calculate prepath */ | ||
2160 | cifs_sb->prepath = volume_info.prepath; | ||
2161 | if (cifs_sb->prepath) { | ||
2162 | cifs_sb->prepathlen = strlen(cifs_sb->prepath); | ||
2163 | /* we can not convert the / to \ in the path | ||
2164 | separators in the prefixpath yet because we do not | ||
2165 | know (until reset_cifs_unix_caps is called later) | ||
2166 | whether POSIX PATH CAP is available. We normalize | ||
2167 | the / to \ after reset_cifs_unix_caps is called */ | ||
2168 | volume_info.prepath = NULL; | ||
2169 | } else | ||
2170 | cifs_sb->prepathlen = 0; | ||
2171 | cifs_sb->mnt_uid = volume_info.linux_uid; | ||
2172 | cifs_sb->mnt_gid = volume_info.linux_gid; | ||
2173 | cifs_sb->mnt_file_mode = volume_info.file_mode; | ||
2174 | cifs_sb->mnt_dir_mode = volume_info.dir_mode; | ||
2175 | cFYI(1, ("file mode: 0x%x dir mode: 0x%x", | ||
2176 | cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); | ||
2177 | |||
2178 | if (volume_info.noperm) | ||
2179 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; | ||
2180 | if (volume_info.setuids) | ||
2181 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; | ||
2182 | if (volume_info.server_ino) | ||
2183 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; | ||
2184 | if (volume_info.remap) | ||
2185 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; | ||
2186 | if (volume_info.no_xattr) | ||
2187 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; | ||
2188 | if (volume_info.sfu_emul) | ||
2189 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; | ||
2190 | if (volume_info.nobrl) | ||
2191 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; | ||
2192 | if (volume_info.cifs_acl) | ||
2193 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; | ||
2194 | if (volume_info.override_uid) | ||
2195 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; | ||
2196 | if (volume_info.override_gid) | ||
2197 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; | ||
2198 | if (volume_info.dynperm) | ||
2199 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; | ||
2200 | if (volume_info.direct_io) { | ||
2201 | cFYI(1, ("mounting share using direct i/o")); | ||
2202 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; | ||
2203 | } | ||
2204 | |||
2205 | if ((volume_info.cifs_acl) && (volume_info.dynperm)) | ||
2206 | cERROR(1, ("mount option dynperm ignored if cifsacl " | ||
2207 | "mount option supported")); | ||
2208 | 2212 | ||
2209 | tcon = | 2213 | tcon = |
2210 | find_unc(sin_server.sin_addr.s_addr, volume_info.UNC, | 2214 | find_unc(sin_server.sin_addr.s_addr, volume_info.UNC, |
2211 | volume_info.username); | 2215 | volume_info.username); |
2212 | if (tcon) { | 2216 | if (tcon) { |
2213 | cFYI(1, ("Found match on UNC path")); | 2217 | cFYI(1, ("Found match on UNC path")); |
2214 | /* we can have only one retry value for a connection | ||
2215 | to a share so for resources mounted more than once | ||
2216 | to the same server share the last value passed in | ||
2217 | for the retry flag is used */ | ||
2218 | tcon->retry = volume_info.retry; | ||
2219 | tcon->nocase = volume_info.nocase; | ||
2220 | tcon->local_lease = volume_info.local_lease; | ||
2221 | if (tcon->seal != volume_info.seal) | 2218 | if (tcon->seal != volume_info.seal) |
2222 | cERROR(1, ("transport encryption setting " | 2219 | cERROR(1, ("transport encryption setting " |
2223 | "conflicts with existing tid")); | 2220 | "conflicts with existing tid")); |
2224 | } else { | 2221 | } else { |
2225 | tcon = tconInfoAlloc(); | 2222 | tcon = tconInfoAlloc(); |
2226 | if (tcon == NULL) | 2223 | if (tcon == NULL) { |
2227 | rc = -ENOMEM; | 2224 | rc = -ENOMEM; |
2228 | else { | 2225 | goto mount_fail_check; |
2229 | /* check for null share name ie connecting to | 2226 | } |
2230 | * dfs root */ | 2227 | |
2231 | 2228 | /* check for null share name ie connect to dfs root */ | |
2232 | /* BB check if this works for exactly length | 2229 | |
2233 | * three strings */ | 2230 | /* BB check if works for exactly length 3 strings */ |
2234 | if ((strchr(volume_info.UNC + 3, '\\') == NULL) | 2231 | if ((strchr(volume_info.UNC + 3, '\\') == NULL) |
2235 | && (strchr(volume_info.UNC + 3, '/') == | 2232 | && (strchr(volume_info.UNC + 3, '/') == NULL)) { |
2236 | NULL)) { | 2233 | /* rc = connect_to_dfs_path(...) */ |
2237 | /* rc = connect_to_dfs_path(xid, pSesInfo, | 2234 | cFYI(1, ("DFS root not supported")); |
2238 | "", cifs_sb->local_nls, | 2235 | rc = -ENODEV; |
2239 | cifs_sb->mnt_cifs_flags & | 2236 | goto mount_fail_check; |
2240 | CIFS_MOUNT_MAP_SPECIAL_CHR);*/ | 2237 | } else { |
2241 | cFYI(1, ("DFS root not supported")); | 2238 | /* BB Do we need to wrap sesSem around |
2242 | rc = -ENODEV; | 2239 | * this TCon call and Unix SetFS as |
2243 | goto out; | 2240 | * we do on SessSetup and reconnect? */ |
2244 | } else { | 2241 | rc = CIFSTCon(xid, pSesInfo, volume_info.UNC, |
2245 | /* BB Do we need to wrap sesSem around | 2242 | tcon, cifs_sb->local_nls); |
2246 | * this TCon call and Unix SetFS as | 2243 | cFYI(1, ("CIFS Tcon rc = %d", rc)); |
2247 | * we do on SessSetup and reconnect? */ | 2244 | if (volume_info.nodfs) { |
2248 | rc = CIFSTCon(xid, pSesInfo, | 2245 | tcon->Flags &= ~SMB_SHARE_IS_IN_DFS; |
2249 | volume_info.UNC, | 2246 | cFYI(1, ("DFS disabled (%d)", |
2250 | tcon, cifs_sb->local_nls); | 2247 | tcon->Flags)); |
2251 | cFYI(1, ("CIFS Tcon rc = %d", rc)); | ||
2252 | if (volume_info.nodfs) { | ||
2253 | tcon->Flags &= | ||
2254 | ~SMB_SHARE_IS_IN_DFS; | ||
2255 | cFYI(1, ("DFS disabled (%d)", | ||
2256 | tcon->Flags)); | ||
2257 | } | ||
2258 | } | ||
2259 | if (!rc) { | ||
2260 | atomic_inc(&pSesInfo->inUse); | ||
2261 | tcon->retry = volume_info.retry; | ||
2262 | tcon->nocase = volume_info.nocase; | ||
2263 | tcon->seal = volume_info.seal; | ||
2264 | } | 2248 | } |
2265 | } | 2249 | } |
2250 | if (!rc) { | ||
2251 | atomic_inc(&pSesInfo->inUse); | ||
2252 | tcon->seal = volume_info.seal; | ||
2253 | } else | ||
2254 | goto mount_fail_check; | ||
2266 | } | 2255 | } |
2256 | |||
2257 | /* we can have only one retry value for a connection | ||
2258 | to a share so for resources mounted more than once | ||
2259 | to the same server share the last value passed in | ||
2260 | for the retry flag is used */ | ||
2261 | tcon->retry = volume_info.retry; | ||
2262 | tcon->nocase = volume_info.nocase; | ||
2263 | tcon->local_lease = volume_info.local_lease; | ||
2267 | } | 2264 | } |
2268 | if (pSesInfo) { | 2265 | if (pSesInfo) { |
2269 | if (pSesInfo->capabilities & CAP_LARGE_FILES) { | 2266 | if (pSesInfo->capabilities & CAP_LARGE_FILES) { |
@@ -2276,6 +2273,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2276 | sb->s_time_gran = 100; | 2273 | sb->s_time_gran = 100; |
2277 | 2274 | ||
2278 | /* on error free sesinfo and tcon struct if needed */ | 2275 | /* on error free sesinfo and tcon struct if needed */ |
2276 | mount_fail_check: | ||
2279 | if (rc) { | 2277 | if (rc) { |
2280 | /* if session setup failed, use count is zero but | 2278 | /* if session setup failed, use count is zero but |
2281 | we still need to free cifsd thread */ | 2279 | we still need to free cifsd thread */ |
@@ -3518,6 +3516,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, | |||
3518 | /* above now done in SendReceive */ | 3516 | /* above now done in SendReceive */ |
3519 | if ((rc == 0) && (tcon != NULL)) { | 3517 | if ((rc == 0) && (tcon != NULL)) { |
3520 | tcon->tidStatus = CifsGood; | 3518 | tcon->tidStatus = CifsGood; |
3519 | tcon->need_reconnect = false; | ||
3521 | tcon->tid = smb_buffer_response->Tid; | 3520 | tcon->tid = smb_buffer_response->Tid; |
3522 | bcc_ptr = pByteArea(smb_buffer_response); | 3521 | bcc_ptr = pByteArea(smb_buffer_response); |
3523 | length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); | 3522 | length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); |
@@ -3746,6 +3745,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, | |||
3746 | cFYI(1, ("CIFS Session Established successfully")); | 3745 | cFYI(1, ("CIFS Session Established successfully")); |
3747 | spin_lock(&GlobalMid_Lock); | 3746 | spin_lock(&GlobalMid_Lock); |
3748 | pSesInfo->status = CifsGood; | 3747 | pSesInfo->status = CifsGood; |
3748 | pSesInfo->need_reconnect = false; | ||
3749 | spin_unlock(&GlobalMid_Lock); | 3749 | spin_unlock(&GlobalMid_Lock); |
3750 | } | 3750 | } |
3751 | 3751 | ||